2 minute read

This is what I wanted my automated build to do:

  1. Get the latest version from CodePlex
  2. Update the version number in AssemblyInfo.cs
  3. Build the project
  4. Check-in the updated AssemblyInfo.cs
  5. Label the project with the version number
  6. Publish the ClickOnce package to my webserver

In order to achieve this I used the CodePlex Source Control Client (cpc.exe) to perform the get latest and check-ins. I was not able to complete #5 as the cpc client does not provide labelling. Maybe once the SvnBridge supports it I can upgrade this guide to use a SubVersion client.

I also wrote a command line utility SetVersion.exe utility that updates the version number on an AsssemblyInfo.cs or .vb file. The source for this is published as SetVersion on the MSDN Code Gallery.

So without further ado this is the MSBuild project file that performs the tasks.

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
  <PropertyGroup>
    <CodePlexProjectName>mycodeplexprojectname</CodePlexProjectName>
    <Version>1.$(CCNetNumericLabel).0.0</Version>
    <BuildFolder>C:\MyBuilds\$(Version)\</BuildFolder>
    <IISPublishFolder>C:\MyInstallLocation\</IISPublishFolder>
    <ProjectFolder>MySolution\Client\</ProjectFolder>
    <BuildPublishFolder>$(ProjectFolder)bin\Release\app.publish</BuildPublishFolder>
    <BuildProject>$(ProjectFolder)Client.vbproj</BuildProject>
    <ToolsFolder>C:\Projects\SolutionFolder\Build\</ToolsFolder>
    <CodePlexClient>$(ToolsFolder)cpc.exe</CodePlexClient>
    <SetVersion>$(ToolsFolder)SetVersion.exe</SetVersion>
    <CodePlexUser>codeplexusername</CodePlexUser>
    <CodePlexPassword>codeplexpassword</CodePlexPassword>
  </PropertyGroup>
 
  <Target Name="Build">
    <Message Text="##Building version: $(Version)" Importance="high"/>
    <Message Text="##Cleaning folder..." Importance="high"/>
    <Exec Command="md $(BuildFolder)"/>
 
    <Message Text="##Getting latest version" Importance="high"/>
    <Exec Command="$(CodePlexClient) checkout $(CodePlexProjectName)" WorkingDirectory="$(BuildFolder)"/>
 
    <Message Text="##Updating build number $(SetVersion) $(BuildFolder)$(ProjectFolder)AssemblyVersionInfo.vb $(Version)" Importance="high"/>
    <Exec Command="$(SetVersion) $(BuildFolder)$(ProjectFolder)AssemblyVersionInfo.vb $(Version)"/>
 
    <Message Text="##Building" Importance="high"/>
    <MSBuild Projects="$(BuildFolder)$(BuildProject)" Targets="Publish" Properties="Configuration=Release;ApplicationVersion=$(Version)" />
 
    <Message Text="##Commiting version change" Importance="high"/>
    <Exec Command="$(CodePlexClient) commit /username $(CodePlexUser) /password $(CodePlexPassword) /message ---Automated-Build---$(Version)" WorkingDirectory="$(BuildFolder)"/>
 
    <Message Text="##Publishing" Importance="high"/>
    <Exec Command="rd $(IISPublishFolder) /s /q"/>
    <Exec Command="xcopy $(BuildFolder)$(BuildPublishFolder)\*.* $(IISPublishFolder) /e /i /y"/>
    <Exec Command="xcopy $(IISPublishFolder)..\default.htm $(IISPublishFolder)"/>
  </Target>
</Project>

Although I’ve worked with MSBuild files in the past this was the very first time I wrote one so a few things to remember. Items defined in the <PropertGroup> node are like declaring variables. You can then use them anywhere in your script in this format $(VariableName).

What's even better is that you can override the default values you provide in the build file with values from the command line like. For example to force a particular version number I can peform the build like this:

MSBuild ReleaseBuild.proj /p:Version=1.6.0.0

When getting the latest version I opted to create a new folder for each build and pull the files into that folder. This way I didn’t have to worry about cleaning the bin folders. Every new build would start from an empty folder.

In my case rather than building the solution file I opted to build just the Client project since that would compile all the dependant projects. The key part that helped me create the necessary files for the ClickOnce publishing was the Targets=”Publish” parameter, that along with the ability to set the ClickOnce version using the properties provided an elegant solution for the tricky problem of keeping the AssemblyVersion and the ClickOnce application version in sync.

<MSBuild Projects="$(BuildFolder)$(BuildProject)" Targets="Publish" Properties="Configuration=Release;ApplicationVersion=$(Version)" />

I use CruiseControl to kick off the build process as well as drive the version numbering. CruiseControl is not absolutely necessary though, as the build file can be run from the command line, as long as a version number is specified.

I’ve packaged the build file along with the SetVersion.exe, cpc.exe (codeplex client) and the ccnet.config for download here.

Categories:

Updated: