In this fourth article of our SharePoint Continuous Integration series, we will look at how to implement assembly versioning as part of a Team Foundation Server (TFS) build. Versioning refers to changing the version number or name with each new release of code. Versioning is important because it enables you to easily see which version of a piece of code is installed in a particular environment. On the .NET platform, the version is typically represented as a four-part number, starting with 22.214.171.124 by default, that exists on each .NET assembly. Incrementing the version can be done manually, but where an automated build process exists (such as in a nightly build), it’s beneficial and a common practice to make versioning an automated step. Note that in TFS 2010, there is no simple configuration switch to enable versioning; some customization of the build process is required. This post provides a step-by-step guide for customizing the build process.
Although assembly versioning is a common practice in the .NET world it brings certain challenges in SharePoint, and this necessitates some changes to the approach outlined in many internet articles. The next section explores this, and the remainder of the article provides detailed steps and sample code to implement versioning for SharePoint projects in a TFS 2010 build definition. Note that the code provided here is a derivative of the excellent work done by Jim Lamb (TFS Program Manager). His post Create a Custom WF Activity to Sync Version and Build Numbers is a good place to check if you’d like more detail on the implementation.
Assembly Versions and SharePoint
Assembly versions are everywhere in SharePoint – web.config files, web part definitions, workflow definitions, event receiver bindings – the list goes on. In each case, the version number is absolutely required so that SharePoint can load the code to run at that time. If you update an assembly’s version, you have two choices to ensure your code can still be loaded:
- Find and update every reference to the previous assembly version.
- Use a binding redirect in the relevant .config file (e.g. web.config) to point to the new version of the assembly.
The first option is impractical, and although the second one is certainly possible – especially since SharePoint 2010 introduced support for adding binding redirect entries to config files during WSP deployment – it’s still not ideal. One reason for this is that binding redirects often aren't added to other config files where they might be required, a prime example being the config file for the SharePoint Timer Service, owstimer.exe.config.
Given these challenges, a better approach is to leave the main assembly version unchanged and instead increment the value of the “assembly file version” property. This is effectively a secondary version number which is not used by .NET in assembly binding. The KB article How to use Assembly Version and Assembly File Version has more details on differences between the two attributes. Using assembly file version means we get the benefits of easily identifying the version of an assembly (without having to take other steps, such as decompiling code), but without the hassle of having to update references to assembly names. Whilst the main assembly version will still show “126.96.36.199”, to see the version number of an assembly versioned in this way, simply right-click the file and view its properties. Under most circumstances, the incremented version number appears in the File version field. The exception to this case is when you wish to include a string in the assembly version – some teams like to include the build name, date or other string-based information as part of the version number. In this case, File version will be set to “0.0.0.0” and the Product version attribute will instead show the generated assembly number. This is the case in the image below where we use TFS 2010’s default build number convention of [build name]_[date in reverse format].[auto-incrementing revision number]:
To emphasize, the sample activity code linked to in this article takes the build number assigned by TFS during the build and applies it to the assembly version attribute(s). One benefit of this approach is that if the build is configured to apply a source control label to each release, this value will also become the assembly version. This method not only makes it easy to identify the version of an assembly in a particular environment, but also makes it possible for the developer to easily get the original files comprising the build. This can be accomplished by using the “get by label” command, rather than a "get latest" or "get changeset".
Implementing a custom workflow activity to increment the assembly file version
Implementing versioning in TFS Build typically revolves around adding a custom activity into the workflow. Notably, TFS 2010 Build runs as a .NET 4 workflow – meaning any custom activities must be written using .NET 4 code in an environment which has this version of the .NET Framework installed. In this case, the activity’s code performs the same steps as a human would to increment the assembly version, namely editing the AssemblyInfo.cs (AssemblyInfo.vb for VB.NET) file, and there are several articles published which detail this. The main difference with this guidance is that the implementation is flexible enough to suit SharePoint builds. The activity enables you to configure whether the main assembly version and/or the assembly file version is updated. (Updating the assembly file version is recommended for SharePoint builds.):
To implement versioning in your build workflow, you have two options:
- Write your own workflow activity that has the implementation, drop it into the workflow and then configure it (more complex).
- Drop a pre-built activity into your workflow and then configure it (simpler).
This article provides a pre-built activity. Before running through the detailed steps to use this activity in your build workflow, however, consider that what we broadly need to achieve is the following:
- Add the assembly to a nominated folder in TFS source control.
- Configure the TFS Build Controller to fetch custom assemblies from the same location.
- Edit the build process workflow to add the custom versioning activity.
- Add arguments to the workflow (optional).
Use the following process to add the activity to your workflow. Note that some steps (marked) require a development machine running .NET 4:
- Download the activity and accompanying source code from http://tfssimpleversioning.codeplex.com.
- In the zip file, find the compiled assembly named COB.CI.Workflow.dll.
- If you prefer a different name for the assembly, use the source code provided to rebuild the assembly.
- In the TFS source control tree, create a special folder for build activities/tools if one does not already exist. Add the assembly containing the workflow activity to this folder and then check-in:
- On the TFS build server, open the Team Foundation Server Administration Console and select the Build Configuration node. Find the Build Controller in the list of controllers and agents and then click the Properties link. In the dialog which appears, set the Version control path to custom assembliesproperty to the path where the custom assembly was previously added:
The next steps must be performed on a development machine that has Visual Studio 2010 and .NET 4 installed. This is because TFS 2010 workflows are .NET 4 workflows and the sample assembly is a .NET 4 assembly.
- Add the COB.CI.Workflow.dll to the .NET 4 GAC by using gacutil.exe. (Note: In .NET 4, assemblies cannot be dropped into the GAC via a Windows Explorer shell extension; instead, gacutil.exe must be used. To ensure that the .NET 4 version of the gacutil.exe tool is used, open a Visual Studio 2010 Command Prompt.)
- In Visual Studio 2010, open the workflow definition for your build. (If the workflow definition was already open, restart Visual Studio and then re-open the workflow definition for the new activities to appear in the Toolbox.) Open the Toolbox, right-click an empty area, and then select Choose Items…
- In the dialog that appears, click the Browse button located on the System.Activities Components tab, and then browse to the location of the COB.CI.Workflow.dll assembly on the file system.
- Ensure the ‘UpdateAssemblyVersionInFile" and "UpdateAssemblyVersionNumber" activities in the list and then click OK to add them to the Toolbox.
- In the workflow, navigate to the location where the assembly versioning step should happen. This is the "Run On Agent" > "Initialize Workspace" activity. (Important: The versioning step must be within the "Run On Agent" sequence so that the "SourcesDirectory" variable exists and is populated.) Drag the "UpdateAssemblyVersionNumber" activity onto this place in the workflow.
- Configure the activity using the Properties window. There are two options for doing this. One option is to configure the properties for updating the assembly version numbers directly on the activity (the defaults shown in the image below are recommended for SharePoint builds):
The other option is to add global arguments at the workflow level, to allow configuration of these options without opening and editing the workflow definition itself. The first step of this option is to add the arguments through the Arguments tab:
Once the arguments have been defined, edit the properties of the "UpdateAssemblyVersionNumber" activity to read from the values of these arguments:
The two parameters should now be exposed on the build definition, meaning no editing of the workflow XAML is needed to change the values:
- Save and check-in the workflow definition.
Testing the build
If everything is correctly configured, assemblies will now be automatically versioned by the build. The build report should indicate that the "Update Version Info in File" activity is called for every assembly being built:
It should now be easy to identify the version of an assembly in any given environment. Simply right-click the assembly and view its file properties, and the auto-generated version number will appear in the attributes (shown in earlier image). Integrating the versioning of assemblies as part of a build process can be a significant step forward for a development team seeking to improve code and release quality.