Publishing ClickOnce application with NANT script - visual-studio-2010

My WPF application is deployed with ClickOnce.
In Visual Studio I open "Project properties / Publish".
There I have:
Publish location
Publish URL
Version
Signature
The problem is, that I have to publish every version for test and production.
The difference between them are properties publish location and publish URL. Currently I have to execute the process twice, while changing the values before publishing for production.
So the result of pressing publish is a folder containing folder "ApplicationFiles", the application manifest file and a setup.exe.
Then i decided to automate this process using NANT.
I build/publish the application first for testing (here i set the .csproj file location, publish folder and application varsion)
<target name="BuildTestApplication" depends="Clean" description="Build">
<echo message="Building..." />
<exec program="${msbuildExe}" workingdir="." verbose="true">
<arg value="${projectFile}" />
<arg value="/target:Clean;Publish" />
<arg value="/p:PublishDir=${testPublishFolder}" />
<arg value="/p:ApplicationVersion=${version}" />
<arg value="/p:Publisher="${publisherName}"" />
</exec>
<echo message="Built" />
</target>
With this I found out that build does not set the publisher. Plus I need to change the provider URL, since the application is also installed via internet (different URLs for test and production). So i did:
<target name="UpdateTestApplication" depends="BuildTestApplication" description="Update">
<echo message="Updating..." />
<exec program="${mageExe}" workingdir="." verbose="true">
<arg value="-Update" />
<arg value="${testPublishFolder}/EdpClient.application" />
<arg value="-ProviderUrl" />
<arg value=""${testPublishUrl}"" />
<arg value="-Publisher" />
<arg value=""${publisherName}"" />
</exec>
<echo message="Updated" />
</target>
With this I have updated the application manifest file with correct values (Publisher and ProviderUrl)...
I do the same for production build, meaning i build the application to another folder and update it with different ProviderUrl and add Publisher, since it has to be included in every mage update...
Now the problem is with setup.exe file.
Setup.exe is generated at build and it takes the values from the .csproj file.
Considering all of the above I have three issues:
1.
Is there a way of building the application with the correct parameters, so the setup.exe would contain the correct values?
2.
Also how would I update Assembly information (parameter version) before build? When publishing from VS i need to update it on "Probject properties / Application / Assembly Information"
3.
I noticed that when Publishing from VS the application manifest file is also generated in the "Application Files" folder, while publishing with MSBUILD it is not. Why is that?
Thank you in advance and best regards, no9
EDIT:
I fixed the problem #2 like so:
<!--UPDATE ASSEMBLY INFORMATION BEFORE BUILD-->
<target name="UpdateAssemblyInfo">
<asminfo output="${assemblyInfoFile}" language="CSharp">
<imports>
<import namespace="System" />
<import namespace="System.Reflection" />
<import namespace="System.Resources" />
<import namespace="System.Runtime.CompilerServices" />
<import namespace="System.Runtime.InteropServices" />
<import namespace="System.Windows" />
</imports>
<attributes>
<attribute type="AssemblyTitleAttribute" value="some value" />
<attribute type="AssemblyDescriptionAttribute" value="some value" />
<attribute type="AssemblyConfigurationAttribute" value="some value" />
<attribute type="AssemblyCompanyAttribute" value="some value" />
<attribute type="AssemblyProductAttribute" value="some value" />
<attribute type="AssemblyVersionAttribute" value="some value" />
<attribute type="AssemblyFileVersionAttribute" value="some value" />
<attribute type="AssemblyCopyrightAttribute" value="some value" />
<attribute type="AssemblyTrademarkAttribute" value="some value" />
<attribute type="AssemblyCultureAttribute" value="some value" />
<attribute type="CLSCompliantAttribute" value="boolean value" />
<attribute type="ComVisibleAttribute" value="boolean value" />
</attributes>
</asminfo>
<echo file="${assemblyInfoFile}" append="true">
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
</echo>
<echo message="Updated" />
</target>
Meaning I override Assembly.info file before build and add relevant values.
And the problem #3 like so:
<!--COPY APPLICATION MANIFEST TO APPLICATIONFILES FOLDER-->
<target name="CopyTestApplicationManifestToApplicationFilesFolder" depends="Dependency target name" description="Update">
<echo message="Copying..." />
<copy
file="source file"
tofile="target file" />
<echo message="Copied" />
</target>

I was able to create tasks that properly generated the assets you mentioned in your question (setup.exe,application folder, etc.) without having to explicitly sign manifest.
the "clean_and_publish_application" task does the following
Clean the project
Rebuild the project
Publish the project **Here i provide several parameters specifying my publish url, BootstrapperSDKPath,Application Version, and Application Revision
<target name="clean_and_publish_application" description="Build the application.">
<echo message="Clean the build directory"/>
<msbuild project ="${src.dir}\${target.assembly.name}.csproj">
<arg value="/property:Configuration=Debug;outdir=bin\Debug" />
<arg value="/t:clean" />
</msbuild>
<echo message="Rebuild the application"/>
<msbuild project ="${src.dir}\${target.assembly.name}.csproj">
<arg value="/property:Configuration=Debug;outdir=bin\Debug" />
<arg value="/t:rebuild" />
</msbuild>
<echo message="Publish the application"/>
<msbuild project ="${src.dir}\${target.assembly.name}.csproj">
<arg value="/p:publishurl=${publish.url};
GenerateBootstrapperSdkPath=
C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\Bootstrapper\;
ApplicationVersion=${app.version};
ApplicationRevision=${app.revision}" />
<arg value="/t:publish" />
</msbuild>
</target>
The BootstrapperSdkPath property is required for the creation of the setup.exe file. I hard coded the location because it doesn't change. MSBuild is looking for a setup.bin file within that directory.
The ApplicationVersion property is formatted as for example 2.0.0.%2a
The ApplicationRevision property is formatted as a number for example 24 (these values are the Publish Version we see in visual studio. I never actually update the AssemplyInfo.cs file at all.)
Any property you see listed in the .csproj file can be passed as an argument for msbuild. I found this documentation very helpful MSBuild Command-Line Reference
The above task does everything you need EXCEPT actually copy the files to your publish url (Still looking for the answer for this). So I just manually copy all the files from the app.config directory the publish creates
<target name="copy_src">
<echo message="Copying app.config folder"/>
<copy todir="${publish.url}" overwrite="true" failonerror="true">
<fileset basedir="${src.dir}\${app.config.location}">
<include name="**" />
</fileset>
</copy>
</target>
I'm running these scripts in Team City. Because I used the publish target, I didn't have to worry about signing any manifests. Also, I use the msbuild task from the NAnt.Contrib.Tasks.dll which you can download here NAntContrib

Related

How-To Set and Get Build Version in TFS SQL Project

I'm using TFS in Visual Studio 2013 & SSDT to create various SQL Database scripts. i.e. I'm doing all my SQL DB development via VS not SSMS.
Want I'm now trying to achieve is to generate/retreive a version number from an external text file when the project is built/published, based on the functionality posted here:
http://www.codeproject.com/Articles/468855/Working-with-MSBuild-Part-2
So I've added the following to the MyProject.sqlproj xml file:
<PropertyGroup>
<WorkingFolder>C:\Source Control\MISTP\Main\DB\SSMS\MyProject</WorkingFolder>
</PropertyGroup>
<Target Name="GetVersion">
<Message Text="GetVersion: Reading version number from VersionInfo.txt" />
<Attrib Files="$(WorkingFolder)\VersionInfo.txt" Normal="true" />
<Version VersionFile="$(WorkingFolder)\Build\VersionInfo.txt">
<Output TaskParameter="Major" PropertyName="Major" />
<Output TaskParameter="Minor" PropertyName="Minor" />
<Output TaskParameter="Build" PropertyName="Build" />
<Output TaskParameter="Revision" PropertyName="Revision" />
</Version>
<Message Text="GetVersion: $(Major).$(Minor).$(Build).$(Revision)" />
</Target>
<Target Name="SetVersion" DependsOnTargets="GetVersion">
<Message Text="SetVersionInfo: Updating Versions in all files" />
<CreateItem Include="$(WorkingFolder)\**\*.*">
<Output TaskParameter="Include" ItemName="Files"/>
</CreateItem>
<Attrib Files="#(Files)" Normal="true" />
<FileUpdate Files="#(Files)" Regex="FileVersionAttribute\("(\d+)\.(\d+)\.(\d+)\.(\d+)"\)" ReplacementText="FileVersionAttribute("$(Major).$(Minor).$(Build).$(Revision)")" />
<FileUpdate Files="#(Files)" Regex="FileVersion\("(\d+)\.(\d+)\.(\d+)\.(\d+)"\)" ReplacementText=" FileVersion ("$(Major).$(Minor).$(Build).$(Revision)")" />
<FileUpdate Files="#(Files)" Regex="FileVersion\("(\d+)\.(\d+)\.(\d+)\.(\d+)"\)" ReplacementText="FileVersion("$(Major).$(Minor).$(Build).$(Revision)")" />
</Target>
I have a VersionInfo.txt file located in:
C:\Source Control\MISTP\Main\DB\SSMS\MyProject\Build
Which simply contains the string: 1.2.3.4
However, this doesn't seem to actually do anything when I Build and/or Publish the project within VS. What am I missing?!
I'm new to MSBuild, but the syntax appears correct - and is largely lifted from the codeproject article - and the path to the file are ok.
It feels like the xml is not being executed, but I'm assuming that it's very presence in the .sqlproj file will result in it being executed.
Thanks
The target isn't triggered during the build. Update "SetVersion" target as following:
<Target Name="SetVersion" DependsOnTargets="GetVersion" AfterTargets="PostBuildEvent">

Visual Studio Post build command line Deployment

In Visual Studio am creating a post-build event for Deploying using
md "$(SolutionDir)Deploy\bin"
which created the bin folder inside Deploy folder, inside my Solution.
How do I point this to the folder in some remote machine (where I have the web server)?
$(SolutionDir) to some other folder on a remote machine?
It may look simple to you. :) This is the first time am trying this stuff.
Thanks
The easiest way is to replace $(SolutionDir) with \\server\share
Just as an alternative, I like to keep my .sln and .csproj files "clean".
Then use a second (mini) .msbuild ( which is just a .xml file) to build the .sln, and then do these copy type events as a second action.
Here is a basic example:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="AllTargetsWrapper">
<PropertyGroup>
<WorkingCheckout>.</WorkingCheckout>
<WorkingOutputs>m:\working\outputs</WorkingOutputs>
</PropertyGroup>
<Target Name="AllTargetsWrapper">
<CallTarget Targets="Clean" />
<CallTarget Targets="Build" />
<CallTarget Targets="CopyItUp" />
</Target>
<Target Name="Clean">
<RemoveDir Directories="$(WorkingOutputs)" />
<MakeDir Directories="$(WorkingOutputs)" />
<Message Text="Cleaning done" />
</Target>
<Target Name="Build">
<MSBuild Projects="$(WorkingCheckout)\MySolution.sln" Targets="Build" Properties="Configuration=$(Configuration)">
<Output TaskParameter="TargetOutputs" ItemName="TargetOutputsItemName"/>
</MSBuild>
<Message Text="Build completed" />
</Target>
<!-- -->
<Target Name="CopyItUp" >
<ItemGroup>
<MyExcludeFiles Include="$(WorkingCheckout)\**\SuperSecretStuff.txt" />
<MyExcludeFiles Include="$(WorkingCheckout)\**\SuperSecretStuff.doc" />
</ItemGroup>
<ItemGroup>
<MyIncludeFiles Include="$(WorkingCheckout)\MyCsProject\bin\$(Configuration)\**\*.*" Exclude="#(MyExcludeFiles)"/>
</ItemGroup>
<Copy
SourceFiles="#(MyIncludeFiles)"
DestinationFiles="#(MyIncludeFiles->'$(WorkingOutputs)\%(RecursiveDir)%(Filename)%(Extension)')"
/>
</Target>
</Project>

Exclude file types in teamcity artifacts

I'm just about to setup teamcity for the first time on my own. Very nice and simple in most ways I have to say. However, I have one issue that I haven't manage to solve and find any information about.
When I wanna publish my artifacts I want to exclude some file types.
example:
%system.agent.work.dir%\trunk\Source\Projects\Webproject.Web/Controllers => Webproject.Web/Controllers
However, I don't want to copy all the .cs files in the folder. I just need the folder.
Is it possible to copy just the folder and not the content, and then copy what ever content I need? Or can I exclude files if I copy a directory?
You can add a MSBUILD target which prepares the "deployment" package for you. I have the following (may need some changes for your project):
<Target Name="Publish" DependsOnTargets="Build" Condition="'$(WebProjectOutputDir)'!=''">
<RemoveDir Directories="$(WebProjectOutputDir)" ContinueOnError="true" />
<!-- Log tasks -->
<Message Text="Publishing web application for $(MSBuildProjectName)" />
<Message Text="WebProjectOutputDir: $(WebProjectOutputDir)" />
<!-- Create the _PublishedWebsites\app\bin folder -->
<MakeDir Directories="$(WebProjectOutputDir)\bin" />
<!-- Copy build outputs to _PublishedWebsites\app\bin folder -->
<Copy SourceFiles="#(IntermediateAssembly)" DestinationFolder="$(WebProjectOutputDir)\bin" SkipUnchangedFiles="true" />
<Copy SourceFiles="#(AddModules)" DestinationFolder="$(WebProjectOutputDir)\bin" SkipUnchangedFiles="true" />
<Copy SourceFiles="$(IntermediateOutputPath)$(_SGenDllName)" DestinationFolder="$(WebProjectOutputDir)\%(Content.SubFolder)%(Content.RecursiveDir)" SkipUnchangedFiles="true" Condition="'$(_SGenDllCreated)'=='true'" />
<Copy SourceFiles="$(IntermediateOutputPath)$(TargetName).pdb" DestinationFolder="$(WebProjectOutputDir)\bin" SkipUnchangedFiles="true" Condition="'$(_DebugSymbolsProduced)'=='true'" />
<Copy SourceFiles="#(DocFileItem)" DestinationFolder="$(WebProjectOutputDir)\bin" SkipUnchangedFiles="true" Condition="'$(_DocumentationFileProduced)'=='true'" />
<Copy SourceFiles="#(IntermediateSatelliteAssembliesWithTargetPath)" DestinationFiles="#(IntermediateSatelliteAssembliesWithTargetPath->'$(WebProjectOutputDir)\bin\%(Culture)\$(TargetName).resources.dll')" SkipUnchangedFiles="true" />
<Copy SourceFiles="#(ReferenceComWrappersToCopyLocal); #(ResolvedIsolatedComModules); #(_DeploymentLooseManifestFile); #(NativeReferenceFile)" DestinationFolder="$(WebProjectOutputDir)\bin" SkipUnchangedFiles="true" />
<!-- copy any referenced assemblies to _PublishedWebsites\app\bin folder -->
<Copy SourceFiles="#(ReferenceCopyLocalPaths)" DestinationFiles="#(ReferenceCopyLocalPaths->'$(WebProjectOutputDir)\bin\%(DestinationSubDirectory)%(Filename)%(Extension)')" SkipUnchangedFiles="true" />
<!-- Copy content files recursively to _PublishedWebsites\app\ folder -->
<Copy SourceFiles="#(Content)" DestinationFolder="$(WebProjectOutputDir)\%(Content.RelativeDir)" />
<!-- Copy items that have been marked to be copied to the bin folder -->
<Copy SourceFiles="#(_SourceItemsToCopyToOutputDirectory)" DestinationFolder="$(WebProjectOutputDir)\bin" SkipUnchangedFiles="true" />
<Copy SourceFiles="#(_SourceItemsToCopyToOutputDirectoryAlways)" DestinationFolder="$(WebProjectOutputDir)\bin" SkipUnchangedFiles="false" />
</Target>
So, in the TC build, I use the MSBUILD builder like this:
Targets: Rebuild;Publish
Command line parameters: /p:WebProjectOutputDir="%system.teamcity.build.workingDir%\Website"
You can then use the Website directory as your artifact.

TeamCity MSBuild 4.0 Help

I need some help with my MSBuild file i created a while ago.
All i want to do is build the solution, publish a project inside the solution and than copy the files to a directory
At the moment when i set Teamcity to .net 4 msbuild, msbuild 4.0 tools and for 86 i get an error stating
error MSB4067: The element <ItemDefinitionGroup> beneath element <Project> is unrecognized.
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Run">
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets"/>
<PropertyGroup>
<OutputFolder>$(OutputDir)</OutputFolder>
<DeploymentFolder>$(DeploymentDir)</DeploymentFolder>
<CompilationDebug />
<CustomErrorsMode />
<ContentEditorsEmail />
<AdministratorsEmail />
</PropertyGroup>
<Target Name="Run">
<CallTarget Targets="Compile" />
<CallTarget Targets="Publish" />
<CallTarget Targets="Deploy" />
</Target>
<Target Name="Clean">
<ItemGroup>
<BinFiles Include="bin\*.*" />
</ItemGroup>
<Delete Files="#(BinFiles)" />
</Target>
<Target Name="Compile" DependsOnTargets="Clean">
<MSBuild Projects="WebCanvas.ZakisCatering.Website.sln"
Properties="Configuration=Release"/>
</Target>
<Target Name="Publish">
<RemoveDir Directories="$(OutputFolder)" ContinueOnError="true" />
<MSBuild Projects="WebCanvas.ZakisCatering.Website\WebCanvas.ZakisCatering.Website.csproj"
Targets="ResolveReferences;_CopyWebApplication"
Properties="Configuration=Release;WebProjectOutputDir=$(OutputFolder);OutDir=$(WebProjectOutputDir)\" />
</Target>
<Target Name="Deploy">
<RemoveDir Directories="$(DeploymentFolder)"
ContinueOnError="true" />
<ItemGroup>
<DeploymentFiles Include="$(OutputFolder)\**\*.*" />
</ItemGroup>
<Copy SourceFiles="#(DeploymentFiles)"
DestinationFolder="$(DeploymentFolder)\%(RecursiveDir)" />
</Target>
</Project>
I'm getting that error code too, although complaining about a different element:
error MSB4067: The element <ArtifactAssemblies> beneath element <ItemGroup> is unrecognized.
I did notice that Teamcity is invoking the 2.0 version of MSBuild, which could explain why msbuild is struggling with the xml.
'C:\Windows\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe'
'#"D:\BuildAgent\work\2f016459feee51ce\Build\BuildSolution.msbuild.teamcity.msbuild.tcargs"
"D:\BuildAgent\work\2f016459feee51ce\Build\BuildSolution.msbuild.teamcity.patch.tcprojx"'
working dir =
'D:\BuildAgent\work\2f016459feee51ce'
Microsoft (R) Build Engine Version
2.0.50727.4016 [Microsoft .NET Framework, Version 2.0.50727.4200]
I fixed the 2.0 msbuild problem by adding to the .\conf\buildagent.properties file on the team city build agent machine, the following:
env.MSBuild=%system.DotNetFramework4.0_x86_Path%
Restart the service after that and problem solved.
Unfortunately, they don't package the WebApplications targets.
I can't find an SDK that has these targets packaged without having VS installed...no way.
I don't understand why MS makes CI so difficult.

is it possible to make nant run a publish on web application project

is it possible to make nant run a publish on mvc project or a good old web application project
and after the publish make nant FTP the files to the web server
UPDATE: found the solution to the ftp problem
Nant ftp task thanks Paco
what i mean by publich
is there a command line application or nant task that can public like visual studio publish...
The visual studio publish command rebuilds your solution and then copies the files in the solution directory to a new directory. I use the following target to do almost the same:
<target name="copyToPublish">
<delete dir="${dir.publish}" />
<mkdir dir="${dir.publish}" />
<mkdir dir="${dir.publish}\wwwroot"/>
<copy todir="${dir.publish}\wwwroot" includeemptydirs="false">
<fileset basedir="${website.dir}">
<exclude name="**/*.cs"/>
<exclude name="**/*.pdb"/>
<exclude name="**/*.csproj*"/>
<exclude name="**/obj/**"/>
<include name="**/*.*"/>
</fileset>
</copy>
<mkdir dir="${dir.publish}\database"/>
<copy todir="${dir.publish}\database" includeemptydirs="false">
<fileset basedir="${dir.databasescripts}">
<include name="**/*.sql" />
</fileset>
</copy>
<xmlpoke
file="${dir.publish}\wwwroot\Web.config"
xpath="/configuration/system.web/compilation/#debug"
value="false" />
<xmlpoke
file="${dir.publish}\wwwroot\Web.config"
xpath="/configuration/system.web/trace/#enabled"
value="false" />
<move file="${dir.publish}\wwwroot\Web.config" tofile="${dir.publish}\wwwroot\Release.config" overwrite="true" />
<delete file="${dir.publish}\wwwroot\Web.config" />
</target>
Before this target you have to run the normal build procedure of course.
There is a Ftp Task for nant.
Beside that, you have to create a script that copies the files and directories you need and the config files. I don't do it automatically, because I want to have control over database update scripts and changes in web.config.

Resources