Whenever you are doing continuous build and/or deployment you always want to keep the amount of installation and configuration you have to do on each build agent to an absolute minimum. There are a number of reasons for this: to be able to scale out by easily adding new agents without having to worry about pre-requisites, to minimize the need for documentation around special setups and risk hard person dependencies, to cut the time for setting up new build environments .
Ideally everything you need to build the project should be fetched from the source repo or some other storage. Unfortunately this is really hard to accomplish and you often end up going the “dirty route” of installing different SDK: s or even Visual Studio on the build machines.
The above is very true when it comes to automating ClickOnce deployments.
It is actually really fascinating how a technology especially designed to help with deployment scenarios is so darn hard to automate in a sane way.
At Active Solution we use TeamCity for the majority of our builds and automated deployments. TC is a really nice solution – a breeze to install and setup, has a friendly intuitive user interface that makes it easy to create new build definitions and the build server is rock solid with very few issues.
In one project I’ve been involved in recently we wanted to automate the deployment of an old WinForms ClickOnce application – deployed to the users through a web site. This posed a couple of challenges:
- The ClickOnce deployment mechanism is dependent on a binary being installed as part of the Visual Studio installation.
- You need a way to increment the version number for the application, and to set that version number in the ClickOnce manifest.
- Preferably you also want to reflect this version number in the HTML page that you install the application from.
- The ClickOnce manifest has to be signed with a certificate.
To accomplish this we created an MSBuild script that essentially:
- Copies the binaries needed by MSBuild to do a ClickOnce deploy from source control to the root folder of the ClickOnce project.
- Reads the version number from a shared Assembly info file.
- Builds the solution and does a ClickOnce publish to disk locally on the build agent.
- Copies the published folder + HTML install page to a target directory
- Updates the HTML install page with the correct version number.
After that, the catalogue is ready to be XCopy deployed to the server; this is done as a separate build step.
Below is the script. The comments in the script are pretty self-explanatory – but maybe a couple of things need an explanation:
Removing the need for installing VS/.NET SDK
“Copy the required ClickOnce build engine files” – where on earth does these come from??? Turns out ClickOnce is hardwired to search for build engine files that come with the VS or SDK installation. These files can reside under a couple of different locations described here: http://blogs.msdn.com/b/emmamou/archive/2009/04/08/team-build-for-clickonce-application-with-bootstrapper.aspx
This is where they reside on my dev box:
However – if it doesn’t find the installation path in the registry it tries to find the files in a catalogue called ‘Engine’ in the project root. So we just checked these files to source control and then copy them from there in the build process.
What is SharedAssemblyInfo?
We read the version number from a file called SharedAssemblyInfo.cs – but what is that?
SharedAssemblyInfo is a way to share the version number (and other information about assemblies) across several projects by linking to a shared assembly info file : http://theburningmonk.com/2010/03/net-tips-use-a-shared-assemblyinfo-cs-for-your-solution/
We could have used TeamCity’s incremented version number, but in this case we wanted to set the version number across all related assemblies by hand – but we only need to change it in one place.
We then use this technique to parse the assembly version from the file: http://stackoverflow.com/questions/2042350/how-to-read-the-assemblyversion-from-assemblyinfo-cs
The Build script also make use of functions found in the MSBuild Community Tasks – we have checked the MSBuild.Community.Tasks.dll and targets into to source control as well.
Here is the GitHub gist with the script: These are the settings for the TeamCity build step:
The only really interesting setting here is the OutputPath which tells ClickOnce where to output the publish – in our case we set the name of the catalogue to the current build configuration (Staging).
There is still one thing that we have to install by hand on the build agents: the signing certificate.
An improvement would be to build the manifest with the help of Mage: http://msdn.microsoft.com/en-us/library/acz3y3te.aspx – with Mage we could use the –CertFile filePath parameter and have the certificate checked into source control as well. Pease let me know if there is another/better way of doing this.
But still: we can now build and deploy our ClickOnce application without getting too dirty and installing VS/.NET SDK:s on the server: Robert – ClickOnce 1 – 0! (or maybe that should be Robert – ClickOnce 1 – 11