Monday, 28 September 2015

VSO Build vNext – versioning assemblies with MSBuild

In this blog post I will show you how to version assemblies during the Visual Studio Online vNext build.

Why to version assemblies?

Versioning assemblies will help you to detect, which library version has been deployed to your environment. You can obviously do it manually for each of your projects, but it can be a mundane and error prone task. Instead, I’ll show you how to easily automate the process.

In result, each project library will be automatically versioned. The version number format will follow the recommended standard:

[Major version[.Minor version[.Build Number[.Revision]]]]

How to version assemblies?

By default the assembly version is defined in the AssemblyInfo.cs class, which can be found in the Properties folder.
[assembly: AssemblyVersion("1.0.0.0")]
If you have many projects it’s much easier to have a single file with all common assembly information shared by all projects. This way you will only need to update the property once and it will be picked up by all projects.

To do that create a separate file called CommonAssemblyInfo.cs and place it the solution’s root folder. Move the AssemblyVersion definition to that file. Then, link the file from all projects:

  1. Right click project
  2. Add Existing…
  3. Select the created CommonAssemblyInfo.cs
  4. Click small arrow next to the Add button
  5. Select Add as Link
  6. Drag the linked file to the Properties folder.
The result should look like this:
Obviously you can move more common properties to the CommonAssemblyInfo file e.g. AssemblyCompany, AssemblyCopyright etc.

Automate versioning

Now that we store the assembly version in a single file we can easily automate the versioning process. The idea is that before executing the actual build we will run another MSBuild script that updates the version in the Common file. The script will be using the AssemblyInfo target task from the MSBuild Community tasks.

The script takes 2 parameteres: BuildId & Revision:

<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <!-- path to MSBuild community tasks --> 
    <MSBuildCommunityTasksPath>.</MSBuildCommunityTasksPath> 
  </PropertyGroup>
  <Import Project=".\tasks\MSBuild.Community.Tasks.Targets"/>
  <Target Name="UpdateAssemblyInfo">
    <Message Text="Updating Assembly versions with Build Id $(BuildId) and Revision $(Revision)"></Message>
    <!-- update assembly and file versions in C-Sharp CommonAssemblyInfo  --> 
    <AssemblyInfo OutputFile="..\CommonAssemblyInfo.cs"
                     CodeLanguage="CS"
                     AssemblyVersion="1.0.$(BuildId).$(Revision.Replace('C',''))"
                     AssemblyFileVersion="1.0.$(BuildId).$(Revision.Replace('C',''))" >                  
    </AssemblyInfo>
  </Target>
</Project>
The script must be added to your source together with community tasks files, so you can reference it in your VSO build definition.

VSO Build definition

Now that we have all components it’s time to define the VSO build. The first step would be execution of our custom build task:
As you can see we use 2 environment variables here:
  • $(Build.BuildId) – id of the build
  • $(Build.SourceVersion) - The latest version control change set that is included in this build .If you are using TFS it will have the format “C1234”, so you need to remove the “C” prefix (see our build script above)
Then, we can use the regular MSBuild step to build the solution. All assemblies that have the CommonassemblyInfo.cs file linked should have the correct version number set. Now you can add more steps to the build definition: running unit tests, publishing artifacts, etc.

Alternative approach

You can also achieve the same functionality using PowerShell instead of MSBuild. There is a good example here. Which one you choose is up to your personal preference – I prefer my solution, as it requires less code.