Making your application's version more descriptive with Stamp.Fody and TeamCity

Problem

Sometimes you want to display an application's version visible somewhere, so it is easy to determine, which version has been recently deployed. In .NET application the easiest way is to get a version number from the one of the following attributes:

[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

The problem is, that in the world of automatic builds and deployments, changing those values is cumbersome and error-prone. Of course you can shape your delivery process in a way, that will allow manual version changes - but we should avoid such actions if possible, shouldn't we?

Solution

Assuming that you use some kind of a build server, getting all necessary data(in my particular case - build number and SHA of a commit) can be pretty easy or very difficult. To be honest, it would be possible to create a whole functionality, I'm talking about, relying solely on TeamCity. The thing is, that the more your build server knows, the harder it gets to version it.

Because of all above concerns I decided to change the concept a little. The best idea I came up with was to get all necessary info directly from the .git folder of my solution after the build. Because I'd already had working psake build script, the easiest thing to do seemed to use some Powershell magic and fetch all what I needed using it. However, why should I extend my script when there is already a better solution?

Stamp.Fody

Yes, there is a weaver, which allows you to automate work needed to embed some Git info in application's version info. The best thing about it is, that it is completely transparent - all it needs is installed Fody in your solution.

Stamp's concept is pretty simple - it checks whether there is an AssemblyInformationalVersionAttribute attribute added to your assembly and if it is - it replaces predefined tokens used as a version's info using data found in your solution's .git folder:

[assembly: AssemblyInformationalVersion("%version%-%shorthash%-%branch%")]

In the above example it is possible to get following result - 1.0.0.0-0772a13a-develop. More tokens are listed on the weaver's GitHub page.

We have some Git info in application's version - what about getting a build number and combining all those values?

TeamCity & psake

As I mentioned before, I use a psake script to handle all build-related tasks. This lets me version all steps required during building an application. Because Stamp takes care of getting the Git info, the only thing left is to combine it somehow with a build number provided by TeamCity.

The very first thing needed here is to pass a build number parameter to a psake script. To do that, you have to go to the build steps of your project and find the one, which executes psake. There, in the script source area, you probably have something similar to:

Import-Module .\.tools\psake.4.4.2\tools\psake.psm1
Invoke-Psake .\build.ps1

All you need is to add a parameter using -parameters switch:

Import-Module .\.tools\psake.4.4.2\tools\psake.psm1
Invoke-Psake .\build.ps1 -parameters @{tcBuildNumer=%build.number%}

Now each build will pass its number to our script. Next todo is to create a function in Powershell, which will obtain generated application's version from an assembly:

function Assembly-GetVersion($file) {    
    $version = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($file).ProductVersion;
    return $version;
}

The last thing is to use above function and generate a new application's version number, which will be much more informative:

Task ExtractVersion {
    $assemblyVersion = Assembly-GetVersion "path_to_your_assembly"
    $majorVersion, $minorVersion, $buildNumber, $revision = $assemblyVersion.split('.', 4)
    $version = "$majorVersion.$minorVersion.$tcBuildNumber.$revision"
    
    New-Item "path_to_the_base_directory\version.json" -type file -value "{`"Version`": `"$version`"}"
}

Note that above syntax is correct using Powershell 4.0 - if you have a different version, some changes would be required. What is more - I am generating a version.json file, which can be requested by your front-end and displayed(e.g. using Angular):

var version = $http.get('version.json');
version.success(function (res) {
     $scope.appVersion = res.Version;
});

Summary

By using mentioned tools you can easily generate an application's version, which will easily tell you and your client what version has been deployed. It can be especially helpful when dealing with multiple environments + automatic deployments(e.g. using Octopus Deploy) during development phase - it is easy to determine whether correct version of an application is being tested and used.