Building a ClickOnce Manifest File with Team Foundation Services Team Build

Issue:

When building a ClickOnce application using Team Foundation Build Services, the goal is to modify the details contained within the .application deployment manifest file to conform to desired settings segregated by build type.  This would allow Build Masters and Release Managers to build the application using different build types within Team Foundation Server and have the drops for those builds take on different deployment profiles.  For example, the deployment URL for a specific debug drop would be different than its production companion.  In order to change this, the information within the project can be altered so that the build process honors the settings, but this approach is not maintainable (i.e., every time the project is build, a developer has to manually edit the .csproj or .vbproj’s ClickOnce deployment settings. 

 

Proposed Solution:

The goal is to have these settings arranged in a build type organization so a Release Manager or Build Master can simply run a build type which knows its own deployment settings or modify a set of tags to change deployment settings for a specific build type.  This provides a more maintainable approach.  Alternatives to accomplishing this are as follows:

Use Mage.exe – Mage.exe can be scripted or tailed to the end of the batch script that builds TFS on a nightly basis.  This approach eventually resolves to MSBuild.  Using MSBuild is preferred because it is a streamlined and maintainable process that does not require the involvement of Mage due to the built in capability of the GenerateApplication/DeploymentManifest task in MSBuild

Put the deployment information into the .csproj/.vbproj – This solution would require manual editing of the project before each build and does not follow best practices for release and software configuration management

Override and modify the Team Build MSBuild targets – This solution is not recommended because during various service packs the VSTS TFS team can change any of the configuration elements within the .targets associated with Team Build and it makes the implementation more esoteric and hard to maintain

Use an existing TeamBuild MSBuild plug-in target (i.e., AfterCompile, BeforeDropBuild) to specify the GenerateDeploymentManifest task – This plug-in interface to the TeamBuild target is likely to remain as it is a designed exposed interface and it is more supportable and keeps the build script simple and easy to maintain

Based on this evaluation, I chose the fourth choice.

 

Implementation Details:

 Create a TfsBuild.proj file by creating a new build type (normal process for creating a build type in Team Client).  Repeat this for each profile of build that will be required (i.e., Development, QA, Production, etc.)

Modify the build (getting latest on the using Team Foundation Source Control and checking the .proj file) to include 2 new sections following the last <ItemGroup/> node

  <ItemGroup>

    <EntryPoint Include ="C:\Builds\MyProject\MyProject - Debug\Sources\MyProject\MyProject.Client\obj\Debug\MyProject.Client.exe.manifest"/>

  </ItemGroup>

   <Target Name ="BeforeDropBuild">

    <GenerateDeploymentManifest

            EntryPoint="@(EntryPoint)"

            DeploymentUrl="https://TFSBuildServer/MyProject/Development/MyProject.Client.exe"

            Install="true"

            TrustUrlParameters = "True"

            MapFileExtensions="true"

            UpdateEnabled="true"

            UpdateInterval="3"

            UpdateMode="Background"

            UpdateUnit="Weeks"

            OutputManifest="C:\Builds\MyProject\MyProject - Debug\Binaries\Debug\MyProject.Client.application">

      <Output

          ItemName="DeployManifest"

          TaskParameter="OutputManifest"/>

    </GenerateDeploymentManifest>

  </Target>

 

These sections include the implementation of a target which implements the MSBuild GenerateDeploymentManifest task to create a .applicaiton manifest file based on the Entry Point (.exe) assembly being built.  Specify any details that should be within the deployment manifest here.

Specify the entry point as the location of the assembly that will need the deployment manifest generated.  This example assumes the path to the build is using a directory called c:\builds.  If no directory is specified, the build process will error out.  It assumes a local path and needs to be told in specific terms where the assembly file is located and where the deployment file should go.  Specify this by including a valid path in the OutputManifest argument.

Crucial:  The outputs from the build process are copied to the Binaries\[BuildType] folder, so that is the deployment file that should be modified, otherwise, the drop process will copy the original .applicaiton deployment manifest and none of the specified changes in the .proj build type will appear in the drop location.

Note that this target occurs before the Team Build BeforeDropBuild target so that we can be sure that all compilation and Team Build CoreCompile targets have been successfully implemented.

Note that we do not regenerate the assembly’s application manifest file as we would manually have to maintain application and file dependencies which are best stored within the .csproj or .vbproj itself.