In ths post I will show how to build ClickOnce applications with TeamBuild for multiple environments in Team Foundation Server.
I have been trying to build ClickOnce applications in our TeamBuild for some time now and only after long ours of googling and trial and error was I successful. The most useful information I found on this blog:
Publish ClickOnce project with Team Build?
Made some modifications and refactoring and here are the code we currenlty use:
This is how you call the ClickOnce build from you TeamBuild project file (line breaks added for readability):
<Msbuild
Projects="$(SolutionRoot)ConfigFilesBuildClickOnce.targets"
Targets="BuildClickOnce"
Properties="SourceDir=$(SourceDir);PublishDir=$(PublishDir); ClickOnceAppName=$(ClickOnceAppName);ClickOnceExeFile=$(ClickOnceExeFile); ClickOnceProduct=$(ClickOnceProduct);Company=$(Company); ClickOnceDescription=$(ClickOnceDescription);ClickOnceUrl=$(ClickOnceUrl); VersionNumber=$(VersionNumber);SigningCert=$(SigningCert); SigningCertPassword=$(SigningCertPassword)"
/>
For every environment or configuration change you want you use this. Below you find the BuildClickOnce.targets file (line breaks added for readability):
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)MicrosoftVisualStudioTeamBuildMicrosoft.TeamFoundation.Build.targets" />
<Import Project="$(MSBuildExtensionsPath)MSBuildCommunityTasksMSBuild.Community.Tasks.Targets"/>
<Import Project="$(MSBuildExtensionsPath)MicrosoftSDCMicrosoft.Sdc.Common.tasks"/>
<Target Name="BuildClickOnce" DependsOnTargets="">
<PropertyGroup>
<ClickOnceApplicationUrl>$(ClickOnceUrl)$(ClickOnceAppName).application</ClickOnceApplicationUrl>
<AppPublishDir>$(PublishDir)</AppPublishDir>
<SdkPath>C:Program FilesMicrosoft SDKsWindowsv6.0A</SdkPath>
</PropertyGroup>
<BuildStep
TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
BuildUri="$(BuildUri)"
Message="Buildning $(ClickOnceAppName) ClickOnce version: $(VersionNumber)">
<Output TaskParameter="Id" PropertyName="StepId" />
</BuildStep>
<CallTarget Targets="UpdateWebPage" />
<!--
************************************************
Generate application manifest
************************************************
-->
<Exec
Command="mage.exe -New Application -TrustLevel FullTrust -ToFile "$(AppPublishDir)$(ClickOnceExeFile).manifest" -Name "$(ClickOnceAppName)" -Version "$(VersionNumber)" -FromDirectory "$(AppPublishDir)""
WorkingDirectory="$(SdkPath)Bin"/>
<!--
************************************************
Signing application manifest
************************************************
-->
<Exec Condition="'$(SigningCertPassword)'==''"
Command="mage.exe -Sign "$(AppPublishDir)$(ClickOnceExeFile).manifest" -CertFile "$(SigningCert)""
WorkingDirectory="$(SdkPath)Bin" />
<Exec Condition="'$(SigningCertPassword)'!=''"
Command="mage.exe -Sign "$(AppPublishDir)$(ClickOnceExeFile).manifest" -CertFile "$(SigningCert)" -Password "$(SigningCertPassword)""
WorkingDirectory="$(SdkPath)Bin"/>
<!--
************************************************
Renaming source files to .deploy
************************************************
-->
<ItemGroup>
<SourceFilesToRename Include="$(AppPublishDir)***.*" Exclude="$(AppPublishDir)*.manifest;$(AppPublishDir)*.htm"/>
<SourceFilesToDelete Include="$(AppPublishDir)***.*" Exclude="$(AppPublishDir)*.application;$(AppPublishDir)*.manifest;$(AppPublishDir)*.htm"/>
</ItemGroup>
<Copy
SourceFiles="@(SourceFilesToRename)"
DestinationFiles="@(SourceFilesToRename->'$(AppPublishDir)%(RecursiveDir)%(Filename)%(Extension).deploy')"
/>
<Delete Files="@(SourceFilesToDelete)"/>
<!--
************************************************
Generating deployment manifest
************************************************
-->
<GenerateDeploymentManifest
MapFileExtensions="true"
AssemblyName="$(ClickOnceAppName).application"
AssemblyVersion="$(VersionNumber)"
MinimumRequiredVersion="$(VersionNumber)"
DeploymentUrl="$(ClickOnceApplicationUrl)"
Description="$(ClickOnceDescription)"
Product="$(ClickOnceProduct)"
Publisher="$(Company)"
SupportUrl="$(SupportUrl)"
EntryPoint="$(AppPublishDir)$(ClickOnceExeFile).manifest"
Install="true"
UpdateEnabled="true"
UpdateMode="Foreground"
OutputManifest="$(PublishDir)$(ClickOnceAppName).application"/>
<!--
************************************************
Signing application manifest
************************************************
-->
<Exec Condition="'$(SigningCertPassword)'==''"
Command="mage.exe -Sign "$(PublishDir)$(ClickOnceAppName).application" -CertFile "$(SigningCert)""
WorkingDirectory="$(SdkPath)bin"/>
<Exec Condition="'$(SigningCertPassword)'!=''"
Command="mage.exe -Sign "$(PublishDir)$(ClickOnceAppName).application" -CertFile "$(SigningCert)" -Password "$(SigningCertPassword)""
WorkingDirectory="$(SdkPath)bin"/>
<!--
************************************************
Generating Bootstrapper
************************************************
-->
<ItemGroup>
<BootstrapperFile Include="Microsoft.Net.Framework.2.0">
<ProductName>Microsoft .NET Framework 2.0</ProductName>
</BootstrapperFile>
</ItemGroup>
<GenerateBootstrapper
ApplicationFile="$(ClickOnceAppName).application"
ApplicationName="$(ClickOnceAppName)"
ApplicationUrl="$(ClickOnceUrl)"
BootstrapperItems="@(BootstrapperFile)"
Culture="en"
FallbackCulture="en-US"
CopyComponents="true"
Validate="false"
Path="$(SdkPath)Bootstrapper"
OutputPath="$(PublishDir)"/>
<BuildStep
TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
BuildUri="$(BuildUri)"
Id="$(StepId)"
Status="Succeeded"/>
<OnError ExecuteTargets="MarkBuildStepAsFailed" />
</Target>
<!--
************************************************
Marks a buildstep as failed
************************************************
-->
<Target Name="MarkBuildStepAsFailed">
<BuildStep
TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
BuildUri="$(BuildUri)"
Id="$(StepId)"
Status="Failed"/>
</Target>
<!--
************************************************
Updating web page
************************************************
-->
<Target Name="UpdateWebPage">
<ItemGroup>
<WebPage Include="$(PublishDir)publish.htm" />
</ItemGroup>
<RegEx
Condition="Exists(@(WebPage))"
Path="@(WebPage)"
RegularExpression="#VERSION#"
NewValue="$(VersionNumber)"
Force="true"/>
</Target>
</Project>