My current role has me involved in several projects utilising the Microsoft Dyanamics AX framework. As part of my work I wanted to use Team Foundation Server to automate a build procedure that wasn’t specifically .NET and would require a custom build template to pull it off. I originally wrote our build templates for other projects using Team Foundation Server 2005. We had never bothered to upgrade them to the Windows Workflow style build templates but had rather utilised upgrade templates to get them to work. I had done a small amount of work with the new style of build templates but I needed to know more so I started doing some serious research on what had changed.
One of the major components I wanted to leverage from our legacy process was the updating of version numbers by the build. Our old implementation used AssemblyInfoTask which is part of the MSBuild Extension Pack and it served us well for many years. As part of the build process all of the AssemblyInfo.cs files would be checked out and the version numbers updated before the actual build occurred. If the build succeeded the changes to the AssemblyInfo.cs files would be checked in to persist the version number increase. If the build failed then the version number updates would be discarded. To make our version numbers a little more readable, we chose the <major>.<minor>.<date>.<revision> format and this version number was updated to both the AssemblyVersion and the AssemblyFileVersion in every AssemblyInfo.cs in the solution.
Since we were still using Team Foundation Server 2005 build templates I began by searching for a more current method to perform the same function. I hadn’t been searching long when I discovered the Community TFS Build Extensions project on CodePlex. The extension library contains a TFSVersion activity which performs all of the functions of the old AssemblyInfoTask module plus much more. The other difference is that the TFSVersion activity does not need to check the files out to update the version. Instead it parses the current build number in Team Foundation Server to get the information it needs to construct a unique build number. This can be more effectively traced back to the label that is automatically associated with the source when when a build occurs.
Finally, on to the “doing it wrong” part. While I was performing this research I stumbled upon some important information that I didn’t previously realise. In the legacy build process we always update both the AssemblyVersion and the AssemblyFileVersion with every successful build. While this is nice and tidy in practice, it is technically a build faux par. For the official word on the different version attributes, MSDN states:
- AssemblyVersionSpecifies the version of the assembly being attributed.
- AssemblyFileVersionInstructs a compiler to use a specific version number for the Win32 file version resource. The Win32 file version is not required to be the same as the assembly’s version number.
- AssemblyInformationalVersionDefines additional version information for an assembly manifest.
We don’t use the last attribute however the other two effect us in the following manner:
- AssemblyVersionThis is the version that the CLR cares about. For strongly named assemblies, changing any part of this number will require all referencers to also recompile. As most of our current codebase is not strongly typed this is currently not too much of a problem. It would present us with some challenges if we wanted to strongly type in the future.
- AssemblyFileVersionThis is the actual current version of the assembly and this should be updated from every build. This is ultimately what the installer uses to discover operations it has to perform during deployment.
The recommendation for dealing with these two version numbers is to define the AssemblyVersion as <major>.<minor>.0.0 and the AssemblyFileVersion as <major>.<minor>.<date>.<revision>. The AssemblyFileVersion should be updated with every build and the AssemblyVersion only updated for breaking changes.
In conclusion, the major issues that the new strategy attempts to address:
- The original solution had to perform source control operations to check in and check out AssemblyInfo.cs files. This creates undesirable noise as the version history will contain a record of these operations which only gets magnified with an increased number of builds.
- As the version number is based on the version in the AssemblyInfo.cs file it will not always be aligned with the build number after at least one failed build. When the build fails, the AssemblyInfo.cs change will be rolled back but the next build revision number will still be incremented. This makes identifying the build and their associated version more challenging.
- AssemblyVersion and AssemblyFileVersion shouldn’t be maintained as a single version. They represent different versions and could potentially cause problems if they are incremented together on every build.
These points are re-iterated in Versioning Code in TFS – Revised along with several solutions including the two detailed above. Moving to TFSVersion to update the version numbers should address the three highlighted issues as well as giving future maintainers of the build access to a whole host of activities contained in the extension libraries.