Automatically updating assembly version from git

Originally when I started developing Rotorz Tile System I would update the version number of the assembly manually with each update. Despite the simplicity of this approach there were a couple of occasions where I would forget to bump to version in the “AssemblyInfo.cs” files or in the readme file. I improved this workflow by automating the updating of these version numbers from the most recent semver encoded git tag along with the hash of the most recent commit.

Due to a limitation with Unity I was originally forced to input the version number into a ProductInfo class because on some platforms compiled games were completely crashing when trying to access the assembly version with the traditional .NET / Mono approach.

I decided to automate this using node.js using the nunjucks template engine to render the “AssemblyInfo.cs” and “ProductInfo.cs” source files. I added a “PreBuildProject” project to the “Rotorz Tile System” solution which is built before any of the other projects allowing it to output .cs source files:

Screenshot of PreBuildProject with pre-build events

The latest version tag is accessed using the following command:

git describe --match v[0-9]* --abbrev=0

Since the version tags follow the semver guidelines it is easy to extract the various parts of the version number so that they can be written out to the “AssemblyInfo.cs” and “ProductInfo.cs” source files using nunjucks templates like the following:

// Template for AssemblyInfo.cs
using System.Reflection;

#if DEBUG
[assembly: AssemblyConfiguration("Debug")]
#else
[assembly: AssemblyConfiguration("Release")]
#endif

[assembly: AssemblyCompany("{{ product.company.name }}")]
[assembly: AssemblyProduct("{{ product.name }}")]
[assembly: AssemblyCopyright("{{ product.copyright }}")]
[assembly: AssemblyTrademark("{{ product.company.trademark }}")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyVersion("0.0.0.0")]
[assembly: AssemblyFileVersion("{{ version.major }}.{{ version.minor }}.{{ version.patch }}")]
[assembly: AssemblyInformationalVersion("{{ version.informational }}")]

In the above you may be wondering why AssemblyVersion is hard-coded to be “0.0.0.0”. This is actually a dirty hack to workaround complete breakage in other peoples assets each time a new update is released for “Rotorz Tile System”. Unity actually uses the exact same trick with its “UnityEngine.dll” and “UnityEditor.dll” assemblies to reduce friction between updates.

// Main part of the template for ProductInfo.cs
public static class ProductInfo
{
    /// <summary>
    /// Gets the name of this product.
    /// </summary>
    public const string Name = "{{ product.name }}";
    /// <summary>
    /// Gets the version of this build.
    /// </summary>
    public const string Version = "{{ version.major }}.{{ version.minor }}.{{ version.patch }}";
    /// <summary>
    /// Gets the commit hash of this build.
    /// </summary>
    public const string CommitHash = "{{ commit.longHash }}";
    /// <summary>
    /// Gets the release type of this build.
    /// </summary>
    internal const string Release = "{{ version.label }}";

    /// <summary>
    /// Gets the target version of Unity for this build.
    /// </summary>
#if UNITY_5
    internal const string Target = "Unity 5";
#else
    internal const string Target = "Unity 4";
#endif
}

Since the process is being automated I wanted to also include the hash of the current commit in the about window. The node script reads this using the following command:

git rev-parse HEAD

So each time the solution is built; everything gets updated automatically.