During my presentation at DevTeach in Vancouver, I showed a technique for setting up versioning of your applications using nAnt and CruiseControl.NET. Since then I’ve had a couple of people ask me if I could post something on this as they missed getting notes on it when they were in Vancouver. Here we go.
Versioning of .NET applications is done through the values that are in the AssemblyInfo.cs file. When you create a new project in Visual Studio that file is created for you. In it there is a bunch of assembly metadata information. AssemblyVersion, AssemblyCompany, AssemblyTitle and a number of others are in there. By default the AssemblyVersion value is set to 18.104.22.168. Regardless of the number of times that you compile the application, the version will stay the same since this value isn’t automatically incremented.
If we’re working in a Continuous Integration environment we have the capability to incrementally version the assemblies created during each integration phase. There are two components to accomplishing this. The first piece of the puzzle is getting CruiseControl.Net to provide an automatically incrementing value. The second piece is using nAnt to create an customized version of the AssemblyInfo.cs file that can then be compiled into the assembly.
CruiseControl.Net takes care of automatically incrementing a value for you. By default CCNet is using the Default Labeller which starts at 0 and increments by one for each successful integration. This could be used to version your application, but in the end your application may have a build label of 843, which doesn’t really conform to the industry standard for version format (22.214.171.124). The Default Labeller has a prefix node that will allow you to prefix the automatically incrementing build number. If you set the prefix node to 2.0.0. and the last successful build was #569 then the labeller will create a label value 126.96.36.1999.
One of the other labeller blocks that I regularly use is the Iteration Labeller. The Iteration Labeller works much like the Default Labeller. The Iteration Labeller is most useful if you are working on an iterative release schedule with a fixed and consistent duration for each iteration. Configuring the Iteration Labeller requires you to set the duration (say 14 if you are releasing every 2 weeks) and the release start date (what was the first day of the first iteration). Like the Default Labeller, you also provide a prefix, but only for the first two values in the four part build label (2.0. – always remember the last “.” for it to work correctly). CruiseControl.Net will take care of automatically incrementing the last two values for you. As the iteration duration passes, the third value in the build label will increment by one (from 2.0.0.x to 2.0.1.x) and the final value in the build label will reset to zero (from 188.8.131.52 to 184.108.40.206). The great thing about this labeller is that you will be able to look at the version number of an assembly and immediately associate it with an iteration release, and probably the feature set that should have been included in it or the defects that should have been fixed in it.
You don’t have to use one of just these two labellers. There are a number of others that offer different value and may be better suited to your project.
Once you have CCNet configured to create a build label that you’re happy with using as a version number, you can move off to nAnt and put that value to use. When you use the nAnt Task within CCNet, some CCNet values are automatically pushed through to the nAnt script being called. One of those values is CCNetLabel. You can use this in nAnt just like any other nAnt property.
One of the nice features in nAnt is the asminfo Task. With it you can create a customized replacement for the default AssemblyInfo.cs file created by Visual Studio. This may look something like this in your nAnt build script.
<asminfo output="CommonAssemblyInfo.cs" language="CSharp">
So we’ve created the custom AssemblyInfo file that we wanted (in this case called CommonAssemblyInfo.cs). Now you need to get this included into your project. In the csc Task in the compilation portion of your nAnt script you will need to make sure to include the generated custom AssemblyInfo file and also exclude the AssemblyInfo files generated automatically by Visual Studio. If you don’t exclude the auto generated AssemblyInfo file you will get a error at this compilation point in the nAnt build script.
<csc target="library" output="MyApplication.dll" debug="true">
That’s all great. You now have your Continuous Integration automatically versioning your application. There’s more that you should probably do though.
I’m a huge proponent of having the developers running the exact same nAnt build script locally on their machines. If the developers are doing this I know that the application is repeatedly being executed, as if it were going to production, before it’s even getting to the integration server. Developers will be executing the build script through nAnt, not CruiseControl.NET. This will cause problems with the versioning information I showed above. Without CCNet kicking off the build process, none of the values that it pushes into nAnt will be available. For the purpose of this post, that means that we will not have the CCNetLabel when we run the build script from nAnt by itself. This will actually cause an error when we try to run the build script. To avoid that happening we need to make use of the property::exists nAnt Function and a temporary property.
<property name="version" value="0.0.0.0"/>
What that block of xml prior to the asminfo task is doing is to ensure that there is a version number for the application even if the CCNetLabel is not available (the developer is running the build script on their local machine). The first step is to setup the temporary property ‘version’ and setting it to a default value. The next thing we setup is the ‘if’ block that checks to see if the CCNetLabel property is available. When it is (if the build script is running because CCNet kicked it off), the ‘version’ property is reassigned the value that was passed in using the CCNetLabel property. The final step is to use the ‘version’ property when we create the custom CommonAssemblyInfo.cs file.
One of the things that I like about using the 0.0.0.0 default value is that you will always know that you have a rouge compile and deploy if you find a file with that version in your production or test environment. Like using the Iteration Labeller, using a default value provides you with a quick point of reference based on the metadata associated with your application.