I’ve been using AppVeyor to build my open-source applications for a little while now but they’ve always generally been class libraries or web applications. Recently I’ve been working a little bit more with WPF, and what is the easiest way to distribute Windows desktop applications? ClickOnce. As the name implies, the user downloads one file, clicks it and the application installs and updates itself for rest of time with no interruption.
Getting this to work with AppVeyor took me a little time to get right but it is incredibly simple in the end. First off, this setup may not work for everyone as I had a specific set of goals and a few things I was able to set aside:
- Continuously integrate a solution.
- Create*.nupkgs for Chocolatey packages.
- Create a ClickOnce deployment package upon a successful build.
- Publish the ClickOnce package to an FTP location.
- Signing the application is irrelevant at this stage.
- Application files are versioned with the build number.
- Setup executable and manifests are manually published initially.
So the context is set, what did I have to do. Well first off I setup a deployment location on my FTP server and then published the application to the FTP server using the Visual Studio wizard that setup the manifests. I then committed the changes to the project file so that the publish information is available when it is built on AppVeyor.
Then I setup the AppVeyor project to build everything as normal but also enabled packaging of nuspec files so that my Chocolatey package was created for my application. At a later date I will get it to deploy to Chocolatey as well.
When I was happy that the project was building correctly I could then look at getting the ClickOnce package to be created. I set it up using the After Build Powsershell script that then calls msbuild for a second time. This might sound like a bad idea but as the assemblies are already created it won’t attempt to build it again. The logger confirms this stating that the assemblies are all up to date. I made sure to include the /logger argument so that I could get the awesome real time logging during this step. The /t:Publish argument makes the msbuild process to use the properties saved in the csproj file. The property argument makes sure the application files are version in accordance with the 6th aim.
This will then put in the output directory an app.publish directory that can zipped up into an artefact. We pick up a specific directory; app.publish/Application Files so that we only create the artefact containing the versioned deploy files.
So I then had a deployable, versioned, ClickOnce application. Deployable but not deployed. AppVeyor makes this really simple. As we have a build artefact, it can be expanded directly onto a environment. The important part is where we deploy the atefact too, the Application Files directory that replicates where we extracted them from the output directory and we don’t place the manifest and setup files onto the environment.
After that, it was as simple as linking the environment to the build project. I’ve now got to look into correctly building the Chocolatey package and pushing that and signing the application files.
After publishing this post, I’ve made a few tweaks to the way I’m building the application after I found a few bugs.
Firstly, I found that the method I was using, caused the application to not automatically update. This was down the fact the updated manifest was not being pushed, and even if it was, MSBuild was not updating the version number for me. Two very easy fixes. To make sure the manifest and was pushed as well, I pulled the path to the artefact up a directory so the entire app.publish directory was zipped up, and then amended the remote folder on the publish environment to it’s root so mirror the artefacts’ structure. To manually update the manifests version number I created a Before Build PowerShell script: