Introduction
With TFS2010, Microsoft has created a very flexible build system, but the problem is that there is no out-of-the-box support for ClickOnce application. In this article, I intend to show how you can use the Team Build in TFS2010 to build and release a ClickOnce application.
If you want to release the application, you will also require TFS Deployer (Get it here).
Prepare ClickOnce Application
Before we can build a ClickOnce application with Team Build, we need to prepare the application and create all the ClickOnce manifests. If the application has been deployed with ClickOnce before, you can skip this stage. To do this, open the application and go to the project properties:
Go to the Publish tab and enter the location of where you want to deploy the application (I have used \\Dave\ClickOnce). When you have configured this, click the “Publish Now” button to set up all the manifests and deploy the application.
The application should now have a signing key and all the manifest files will be present. Add this solution to your TFS 2010 source control and we are ready to go.
Setting up Team Build
Now we need to set up Team Build to ask MSBuild to create the ClickOnce files when it runs a build. The simplest solution is to pass /t:publish
in the MSBuild arguments field of the Team Build setup.
The problem with this solution is using the “DefaultTemplate.xaml” template adding this parameter will ask MSBuild to create the ClickOnce manifest files alongside the existing files, which will appear in the binaries folder. The proper ClickOnce file structure will appear under the project debug folder (e.g. ClickOnceApplication\bin\Debug\app.publish folder). This folder is not copied to the Binaries folder during the build.
The solution to this is to modify the DefaultTemplate.xaml; in order to do this, we need to know the project name of the deployable ClickOnce application. We therefore add a parameter to the build script so that we can easily configure this for multiple builds.
Create Variable
Open the DefaultTemplate.xaml in VS2010. You will see a link at the bottom of the window to the Arguments screen.
Open the Arguments screen and add a new Argument called “PublishProject
”, the direction is “In
” and the type is “String
”.
To make the request for the variable more user friendly, select the MetaData
argument and click on the more button (“…”)
You will open the MetaData
window (shown below), in here add a new item with the details shown:
Alter the Workflow
As we want this script to be used by ClickOnce and normal applications, all changes we make should be OK to run a normal application through. We use the parameter that we created to check if this is a normal or ClickOnce application. The first change we make is to automatically add the /t:publish
parameter to the MSBuild call when the variable is populated. There are multiple paths through the workflow so the MSBuild appears in 2 places, make sure the change is done in both places.
You need to look for the MSBuild activity called “Run MSBuild for Project”, the first one is under:
“Process > Sequence > Run On Agent > Try Compile, Test, and Associate Changesets and Work Items > Sequence > Compile, Test, and Associate Changesets and Work Items > Try Compile and Test > Compile and Test > For Each Configuration in BuildSettings.PlatformConfigurations
> Compile and Test for Configuration > If BuildSettings.HasProjectsToBuild
> For Each Project in BuildSettings.ProjectsToBuild
> Try to Compile the Project > Compile the Project” (that is one long path to follow).
One of the properties of the Build Task is “CommandLineArguments
” which is populated with:
"String.Format("/p:SkipInvalidConfigurations=true {0}", If(MSBuildArguments, ""))"
We need to add the extra parameter but only if a Publish Project is passed in. Alter the parameter to contain:
"String.Format("/p:SkipInvalidConfigurations=true {0} {1}", _
If((PublishProject IsNot Nothing AndAlso PublishProject <> ""), _
"/t:publish", ""), If(MSBuildArguments, ""))"
The next change is in the copy of the output files to the drop location. Under normal application builds, we want to copy all files from the Binaries folder. For ClickOnce applications, we need to copy the output which was put in “application\bin\debug\app.publish” folder. As the application folder is named as the same name as the application, this is where the variable comes into play. Let's find the location of where the copy takes place, it is at:
“Process > Sequence > Run On Agent > Try Compile, Test, and Associate Changesets and Work Items (in the finally block) > Revert Workspace and Copy Files to Drop Location > If DropBuild And DropLocation is Set (in the Then
section).
At the moment the Then
block will only contain the CopyDirectory
task, to save this for later, copy it to the Else
section. Insert another If
statement in the Then
section with the criteria being:
"PublishProject IsNot Nothing AndAlso PublishProject <> """
This just checks for a valid PublishProject
. In the else
section of the new if
statement, copy the CopyDirectory
back from the other Else
section we moved it to earlier. In the Then
section, take a clone of the CopyDirectory
task in the Else
section.
Now you will need to check the file into TFS. I have included the edited file in the solution, for those who got lost doing the above.
Building the Solution
Once the file has been updated, you can create a build definition using the new DefaultTemplate.xaml file. On the process tab, you should now see an extra section called “ClickOnce” which has a new parameter called “Project To Publish”, you need to put the name of the project that you want to publish via ClickOnce.
Once you have saved the build definition, you can queue a new build. Once complete, it should look like this (I have added a message in to show the file copy):
Deploying the Solution
TFS Deployer can be downloaded from http://tfsdeployer.codeplex.com/. I will not cover how to set up TFS Deployer as it is covered in detail elsewhere. In the attached file are PowerShell scripts used to release the project from the Drop folder.
History
- 28th October, 2010: Initial post