In this article, we are going to create a build process, where the resulting assemblies contains the build number in the assembly version. Having this process in place helps to identify from where a problematic assembly came from.
In .NET, the assembly versions are composed from four numbers, separated by dots. If you want to follow semantic versioning, which really helps to define which APIs are compatible, it will take up the first three number places. This leaves us with the opportunity to use the last number to identify the build number. Let’s see what do we need to solve the problem at hand:
-
All projects from our solution should have the same assembly version.
-
The last number from the version should be replaced by the build number.
Example solution for this article can be found at https://github.com/atzimler/CommonVersioning. The starting state of the code can be found in the before directory. The result is in the after directory.
Achieving common assembly version through the whole solution
To have the same version number across all assemblies, we should update the AssemblyVersion attribute of each assembly to the same information. One solution that easily comes to mind for this is to manually update all the AssemblyInfo.cs files. However, this is also error-prone. The right solution is to have only one file defining the AssemblyVersion and including that into each assembly. This solution can also be used for having the same AssemblyCompany attribute as well, which is associated with the owner of the product.
First, let’s remove from all AssemblyInfo.cs files the AssemblyVersion and AssemblyFileVersion attributes.
The next step is to create a file describing this the AssemblyVersion attribute in the top-level directory of the solution. This will the place where Jenkins will look for the file to customize it to include the build number as well. The content of this file is simple:
using System.Reflection; [assembly: AssemblyVersion(“1.0.0.*”)] |
After creating the file, include it into the projects as a link. To do this, in Visual Studio, you select add existing item, select the file and then use the dropdown button on the Add button of the window, to select the Add as Link functionality. Be sure to add the item as a link, not just add as an existing item, because in the latter case it is going to be copied into the project folder and thus not containing any updates made to the original file.
If you now build your solution, all your assemblies will have the same version number, but the * in the version number will be replaced with a random number. This number is not so random, but seems to be. The algorithm is based on build time, but only using one placeholder with * will cause early morning builds to be considered earlier versions than late afternoon ones, even if the former is built on a later date. This would not be a problem if you would only build one version of a software to be released. Pay attention to this fact, however, if you test your assemblies or NuGet packages locally. If you run your development cycle across days, your randomly generated number will be reset at midnight. This in turn can cause scenarios, where the NuGet package manager classify your earlier build as the most recent one, even it is not.
Configuring Jenkins to replace the asterisk (*) with the build number
To solve the problem caused by the last part seemingly randomly assigned, we will replace the asterisk during builds on the build server with the actual build number of the project. This number in turn is strictly increasing, so a later build will always have a higher number. Until you decide the alter the build project information, of course.
To achieve this, we will call a simple shell script that will get the build number from the environment variables of the running shell, and insert it into the file. This environment variable is set by Jenkins before calling any task. This script is in the scripts directory of the example repository. It first looks for the AssemblyVersion.cs file in the working directory and if does not find it, exits. If it does find the file, then it locates the line containing the text AssemblyVersion, and replaces the first asterisk character with the content of the BUILD_NUMBER environment variable. To be able to execute this script you will need a Cygwin setup documented in the article Building a Build Server – Building your first C# Project with Jenkins.
Additional configuration
The bash script will be executed from the Jenkins task Execute shell. To have this task work, you need to configure where the bash executable is. To do so, from the main Jenkins page go to Manage Jenkins / Configure System, then in the Shell section set Shell executable to C:\cygwin64\bin\bash.exe. Also, the shell script uses the ed command-line application, which needs to be added as Cygwin package. See my article titled Building a Build Server – Installing NAnt, first section on how to add packages to your Cygwin installation.
Putting it together
To try the solution out, use the following Jenkins project configuration for building the source code.
Source Code Management
Use Git source code management with
Repositories |
|
Repository URL |
|
Branches to build |
|
Branch specifier (blank for ‘any’) |
*/master |
Repository browser: |
(Auto) |
Build
Execute shell |
|
Command |
cd after sh ../scripts/assemblyversioning.sh |
Build a Visual Studio project or solution using MSBuild |
|
MSBuild Version |
MSBuild 14.0 |
MSBuild Build File |
after/CommonVersioning.sln |
Verifying the build output
To check the result, download any of the assemblies from the bin/Debug directories in the after directory, right click on it, select Properties and switch to the Details tab. You should see your version number with the asterisk being replaced with the build number you are checking.
You can connect with me on LinkedIn or Twitter. Share if you found this article useful.
No comments yet.