GitVersion and TeamCity – Configuring GitVersion For GitHubFlow

In the previous post I outlined how to get GitVersion up and running in TeamCity, with a basic version number that looks like “0.1.0+27”, where the final digit is an incrementing counter showing code changes (commits to master). It’s something but it’s not great. It technically works with a “GitHubFlow” workflow, but doesn’t give me full control over versioning.

Back to my wishlist of requirements – I want version info that:

  1. Allows for a semantic version, with “major.minor.patch” components
  2. Lets me control when the major/minor build numbers change
  3. Has a “patch” component that is incremented as the code changes
  4. Is “baked in” to the release code so I can display it on the app (UI footer, “info” API method, etc)
  5. Is displayed on the build server dashboard, so I can track a release through the build pipeline from CI to Test to Live
  6. As a bonus, will point to the specific code commit that represents the actual live code

I’m using TeamCity as my build server, but the majority of this post relates to configuring GitVersion to control version number format.

Default GitVersioning

The default behaviour of Git Version is to create a version number like “0.1.0+27”, where 27 is the number of commits. If all you did was to add a GitVersion build step, that’s what you get. Try it. Set up a basic build and observe the generated version number. If you look at the AssemblyInfo.cs files, you’ll see the assembly and file versions are “0.1.0.0”, and the informational version is a big long string of “0.1.0+27.Branch.master.Sha.485…”. Not great, but at least you have some version and source control reference info you could use. Dig into the build logs for your GitVersion step and you’ll see that Git Version generated a bunch of properties. We want to try use these to get better quality version numbers.

A Better Version Number Format

OK, so “0.1.0+1234” isn’t a great version number format. You probably want something nicer, maybe some semantic versioning. Thankfully, there’s a simple trick you can do – adding a GitVersion.yml config to your code.

I’m going to use an approach where GitVersion and configuration within the code iteself controls the versioning – no need for the build server (eg. TeamCity parameters) or branching/tagging to be used to control the major/minor versions.

GitVersion Configuration Experiments

You can run GitVersion locally – just get the GitVersion.exe installed and added to your path. Because I have a test TeamCity instance on my development PC, I have a gitversion.exe in my “chocolatey” directory and on my path as a result of the TeamCity meta-runner setup.

Open a command prompt in your local working directory copy of the repository (the one with the “.git” file).

Start by running a simple “GitVersion” to see all the values that you can get.

We want to create a GitVersion.yml configuration file for our project that specifies how GitVersion should work. As a starting point, Gitversion has a nice little wizard that will create one for us.

Do a “gitversion init”, and up pops the wizard. So many choices! Let’s try “2) Run get started” and see what happens…

This is where I had to go with some trial-and-error. I’m using a simple “GitHubFlow” approach where releases are regularly cut from master, but all work is done on short-lived feature branches.

I also tried experimenting with “GitFlow” only to be presented with a message about the “develop” branch. You can use CTRL-C and start again, or the “Unsure tell me more” option. Note that their definitions of continuous delivery (tagged branches) versus continuous deployment might look a bit odd, and seem to be built for teams using git branching to control releases flowing through the CD pipeline.

Anyway, I went for “GitHubFlow”, “increment every commit”, saved my changes (option “0”) and ended up with a simple GitVersion.yml file:

mode: ContinuousDeployment
branches: {}
ignore:
  sha: []

When I just run gitversion again, or commit my changes and re-run the build, I get a version number that looks like “0.1.0-ci.12” (it was “0.1.0+11”).

What I really want is something more like this:

assembly-versioning-scheme: MajorMinor
mode: Mainline
branches: {}
ignore:
  sha: []

This give me a “0.1.9” version number. Instant observation – the generated “commit count” number has actually decreased (but we did select “Mainline” mode, so we’re probably only counting commits to master, not other branching/merging activities). A quick check of the generated “informational” version has the full “0.1.9+Branch.master.Sha.722…” info.

Experimenting with build specifics by re-running builds on the build server is inefficient, so it’s back to the command prompt (in your local working directory).

Type “gitversion” and you get a list of all the various values GitVersion calculates for your code.

The “AssemblySemVer” of “0.1.0.0” might be an issue. Changing the assembly-versioning-scheme to “MajorMinorPatch” will update “AssemblySemVer” to “0.1.9.0”.

So the last thing we need to do is control the major/minor version. For that, I can use the “next-version” property, and add the following entry:

next-version: 1.2

You can edit your GitVersion.yml to customise the format used in AssemblyInfo values by setting “assembly-versioning-format”, “assembly-file-versioning-format” and “assembly-informational-format”.

For example, I set a more concise informational format using

assembly-informational-format: '{Major}.{Minor}.{Patch}.{Sha}'

Now I have an extended version number with a reference to the code, so if I need to retrieve the exact code that’s running in production, I can.

So I added a GitVersion.yml file that looks like:

assembly-versioning-scheme: MajorMinorPatch
mode: Mainline
next-version: 1.2
assembly-informational-format: '{Major}.{Minor}.{Patch}.{Sha}'
branches: {}
ignore:
  sha: []

I have a simple “major.minor.patch” version format, where I can control the major/minor versions, and have the patch counter auto-incremented on every commit or merge to master. The version format I’m using works for my build pipeline. But with a little experimentation with the GitVersion.yml, you can tweak the version number generation to work for a different workflow.

Summary

So to get all this to work with a standard TeamCity build, I needed to do the following:

  1. Configure GitVersion in TeamCity (Meta Runner for the .exe and a couple of project parameters)
  2. Configure GitVersion for my project by adding a GitVersion.yml file into the code
  3. Add GitVersion build step to generate a version and bake it into AssemblyInfo.cs files

My requirements were to setup GitVersion for a “GitHubFlow” workflow. With some reference documentation and experimentation, you can use GitVersion for your chosen workflow.

Branching Gotchas

So far we’ve only covered building on the master branch. If you have builds on other branches (I like to set up CI on all the PR branches so I can have code checked before it gets merged into master) then you may run into the following:

  • You might need to add an additional environment variable (parameter in your TeamCity project or build configuration) and set “env.IGNORE_NORMALISATION_GIT_HEAD_MOVE” to “1”
  • The version patch count on the “other” branch might appear a little out (counting commits on master is the important task)

GitVersion 3 vs GitVersion 4

While I was experimenting with better GitVersion settings, GitVersion upgraded to version 4. I noticed a few differences:

  • Mainline mode seems to be new in version 4
  • I had a bunch of builds suddenly fail with “GitTools.Core has a bug, your HEAD has moved after repo normalisation” error, fixed by adding an environment variable parameter to the build config with name “IGNORE_NORMALISATION_GIT_HEAD_MOVE”, value “1”

GitVersion and TeamCity

One advantage of old “centralised” version control systems over Git is that commits have useful sequential numbers. Assigning version numbers automatically as the code changes (as opposed to just numbering builds) becomes tricky with Git.

My aim is to have a build process generate meaningful version number/info that:

  1. Allows for a semantic version, with “major.minor.patch” components
  2. Lets me control when the major/minor build numbers change
  3. Has a “patch” component that is incremented as the code changes
  4. Is “baked in” to the release code so I can display it on the app (UI footer, “info” API method, etc)
  5. Is displayed on the build server dashboard, so I can track a release through the build pipeline from CI to Test to Live
  6. As a bonus, will point to the specific code commit that represents the actual live code

Thankfully, there’s a tool – Git Version – that will generate more meaningful version numbers, including a sequential count of code commits.

This post covers TeamCity building .Net, but you should be able to get Git Version to work with other build tools and spit out version numbers you can use. For .Net, we need to apply the generated version number to the AssemblyInfo.cs files before compiling, but this “generate version and write to file” approach can be adapted for other languages.

Initially, I was using TeamCity’s built-in “Assembly Info Patcher” build feature, and just feeding it values of “%Major.version%.%Minor.version%.%build.counter%.0” and “%Major.version%.%Minor.version%.%build.counter%.%build.vcs.number%” for the version/file and informational numbers (I used parameters for the major and minor versions to control the semantic versioning). By default, TeamCity’s “build.vcs.number” property only gives you the commit SHA.

For “something better”, I need to configure TeamCity and GitVersion to give me “something meaningful”, then I need to tweak the versioning to match my workflow.

Setting up TeamCity and GitVersion

First download – for TeamCity use https://github.com/JetBrains/meta-runner-power-pack/tree/master/gitversion. You can just download the repository as a zip, and then unzip the archive somewhere.

GitVersion runner needs to be added to a project as a Meta Runner – you can add this to a single project, but it’s best to add to the root project.

So go to the “<Root project>”->Meta Runners, and upload

Filename: “MR_GitVersion3.xml”
File: browse to “MR_GitVersion3.xml”

(Look in the meta-runner-power-pack-master/gitversion folder).

Check by editing the meta-runner – you will see the following:

ID: MR_GitVersion3
Name: GitVersion3
Description: Execute GitVersion 3

And the source will be the content of the XML file you uploaded. You can now delete the local copy of the meta runner you downloaded. You can also set GitVersion to check for updates.

If you want to use a different build server, start with the GitVersion project code at https://github.com/GitTools/GitVersion. As you’ll see later, all you need is a GitVersion executable.

Versioning a Build

I’m assuming you have a basic build configuration setup that grabs code from Git (VCS root settings) and compiles the code (build step), and that your build doesn’t yet apply a version number. Hopefully your build will end up doing other things (eg. running unit tests, creating a release package).

Run the build, observe the output (see build logs for agent and working directory location) and you’ll see that all the newly-created DLLs have whatever default version number you put in the AssemblyInfo.cs files (check DLL properties->details).

Now we have an “unversioned” build, let’s try stamping a version number. You’ll need to add a build step to your build configuration – this should be the first step, before you do anything that requires a version number (compilation, packaging, etc). Important note – don’t use a TeamCity AssemblyInfoPatcher build feature if you’re using GitVersion.

Edit your build step, and set runner type “GitVersion3”. A remote git password is added when you first add the build step (checking the config in “ProgramData\JetBrains\TeamCity\config\projects\ProjectName\buildTypes\BuildConfig.xml” shows that this is a generated password).

For .Net, you’re going to want to check “Update AssemblyInfo files”.

Run this and TeamCity will complain about a “parameter called ‘env.Git_Branch’ with value %teamcity.build.vcs.branch.<vcsid>%”.

GitVersion needs some parameters set up for your build, so go to the “Parameters” tab and set up two parameters:

Environment variable: “env.Git_Branch”, value “%teamcity.build.vcs.branch.<vcsid>%”

Configuration parameter: “teamcity.build.vcs.branch.<vcsid>”, value “master”

So if you run this, then you’ll see that both the AssemblyInfo.cs and the DLL now have the actual generated version numbers. Unfortunately, they’re probably not the version numbers you’re looking for…

In a follow-up post I’ll cover how to configure GitVersion to spit out the version info and format you actually want.

Summary – so far we have:

  1. Set up CI build to grab code from Git
  2. Added build step to compile code
  3. Added GitVersion build step to generate a version and bake it into AssemblyInfo.cs files

Showing the GitVersion Build Number on the TeamCity Build Dashboard

You probably want to have TeamCity use this generated version number as the displayed build number, rather than an arbitrary build counter. This is especially useful if you go for a “build artifact once, deploy repeatedly” pipeline approach, rather than a branch-based / re-build on deployment approach. Go into the build configuration general settings and check the build number format. I’ve actually found that both “%build.counter%” and “%build.vcs.number%” seem to work with GitVersion.

Implementing Feature Toggles in a .Net MVC Application

This is part 2 of a set of posts on feature toggles (part 1 is here). These are some example some code snippets (C#) based on techniques I have used to implement feature toggle systems in .Net applications. This example assumes that Unity is used for dependency injection. A real-world implementation would likely include a database for feature storage (with admin screens) and caching of feature checks for performance.

All Known Features

First, you need a master list of known features:


public enum FeaturesEnum
{
    CustomiseUserWorkspace,
    CancelSubscription,
    NewAwesomeThing
    //etc
}

Feature-Checking Code

Now you’re going to want a central authority for the current feature toggle state – a FeaturesService, and some kind of FeatureStore (not shown, add your own implementation). The FeatureStore implementation could be a config file or a database (hence the “ToggleFeature” method).


    public interface IFeaturesService
    {
        bool IsEnabled(FeaturesEnum feature);

        void ToggleFeature(FeaturesEnum feature, bool isEnabled);
    }

    public class FeaturesService : IFeaturesService
    {
        private readonly IFeatureStore _featureStore;

        public FeaturesService(IFeatureStore featureStore)
        {
            _featureStore = featureStore;
        }

        public bool IsEnabled(FeaturesEnum feature)
        {
            var featureRecord = _featureStore.GetFeature(feature);
            return (featureRecord == null) ? false : featureRecord.IsEnabled;
        }

        public void ToggleFeature(FeaturesEnum feature, bool isEnabled)
        {
            _featureStore.SetFeature(feature, isEnabled);
        }
    }

    public class FeatureRecord
    {
        public FeaturesEnum FeatureType { get; set; }
        public bool IsEnabled { get; set; }
    }

 

That’s all you need for basic feature toggle infrastructure. Now you need to add code to check those feature toggles.

Checking Feature State

First, to check for a feature and branch the code (eg. in a controller):


    public class HomeController : Controller
    {
        private readonly IFeaturesService _featuresService;

        public HomeController(IFeaturesService featuresService)
        {
            _featuresService = featuresService;
        }

        public ActionResult Index()
        {
            if (_featuresService.IsEnabled(FeaturesEnum.NewAwesomeThing))
            {
                //Do the shiny new thing
            }
            else
            {
                //Do the boring old thing
            }

            return View();
        }
    }

If you want to toggle UI elements (or hide the button that launches your new feature), add some HTML helper code:


    public static class FeaturesHelper
    {
        private static IFeaturesService _featuresService = null;
        private static IFeaturesService FeaturesService
        {
            get
            {
                if (_featuresService == null)
                {
                    _featuresService = UnityConfig.Container.Resolve();
                }
                return _featuresService;
            }
        }

        public static bool IsFeatureEnabled(this HtmlHelper helper, FeaturesEnum feature)
        {
            return FeaturesService.IsEnabled(feature);
        }
    }

And make a single-line check:


@if (Html.IsFeatureEnabled(FeaturesEnum.NewAwesomeThing))
{
<p>New awesome markup</p>
}

And finally, putting security checks on the UI is never enough, so you might want some access control attributes:


    public class FeatureEnabledAttribute : AuthorizeAttribute
    {
        private readonly FeaturesEnum _feature;

        public FeatureEnabledAttribute(FeaturesEnum feature)
        {
            _feature = feature;
        }

        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            var featuresService = UnityConfig.Container.Resolve();
            return featuresService.IsEnabled(_feature);
        }
    }

Now you have another one-line check you can use to block access to controllers or methods:


    [FeatureEnabled(FeaturesEnum.NewAwesomeThing)]
    public class AwesomeController : Controller

Notes

I favour using enums over strings for my known features, because then I don’t have to worry about run-time typos suddenly checking for a brand new unknown feature. It also makes finding and removing features easier. And depending on language used, it might even be easy to document the features using decoration attributes (eg. adding longer descriptions for an admin system).

Any “master record” of which features exist should live with the code. You’re going to need to provide this to any admin system you build (via reflection, API call, etc).

When it comes to unit testing, it’s probably worth your while to actually implement a “FakeFeatureService” that’s just an in-memory dictionary and use the standard ToggleFeature() methods, rather than going with a mocking framework. Also, if you go with enums for your list of known features, the list is going to change frequently. To prevent brittle tests, don’t use a specific feature when testing your feature toggle framework code, just write a little builder/helper to give you a random valid feature enum from the list of current features.

Notes on Implementing Feature Toggles

If you want to release software frequently, you need to be able to cut releases at almost any time, and your code always needs to be in a releasable state (so no “code freeze” while you test and assemble the quarterly release). This makes dealing with in-progress feature development an issue, assuming you can’t completely build and test a new feature in a matter of hours.

One solution is to rely heavily on code branches for development and release, “cherry picking” the feature-complete-and-tested branches. This can introduce a load of test headaches though, as you try and coordinate which of the many possible “release candidates” you have are viable, and prevents using a simple no-or-minimal-branching “trunk-based” or “github-flow” workflow.

Another solution for regularly releasing constantly-changing software is to use a system of “feature toggles” (AKA “feature flags”). The basic idea is that the new code (which you may or may not be comfortable releasing) is already part of the release, but might not actually be available to use. Basically, you leave both the old (if you’re reworking something) and the new code in the release until you’re happy that the new code is ready to go into production, at which point the new code is “baked in”. With some simple “is feature X switched on” logic, and an admin screen or config file, you can control whether or not the new feature is “on”. And as a bonus, you can easily test old versus new behaviour, or even hit the emergency kill-switch and revert to the old code after you’ve deployed to production.

You could just have a load of bespoke configuration settings and checks, but the aim is to decouple and leverage as much common code as possible, so that you can just make a simple standard check for “is this new feature enabled?” and not have to worry about the underlying feature-toggling implementation.

What follows is some notes and observations based on the experience of implementing a feature toggle system (for a large e-commerce monolith .Net MVC web app).

Where and When to Check Feature Toggles

If you were building an API, you could just create a new version of an API method and deprecate the old one. Feature toggles are a great solution for UIs and other customer-facing apps where you want to hide and control the “old versus new code” decision. You can use feature toggles to control flow in microservices or “back-end” code, but you probably don’t need to.

To implement a feature toggle, you’re going to have to add code that you need to remove later, and to keep things simple you want to make as few checks as possible for each feature. Generally, you want to check a feature and branch to use the new path at the earliest possible opportunity, and you want that check to be a one-liner.

Typical examples of checks you would make:

  1. UI controls – the “should I show this button?” check for whether to show new/old or new/no UI component
  2. Authorisation/security – the “is the user allowed to visit this page?” check to block access to unfinished code
  3. Code branching for new/old behaviour – branch at the earliest opportunity, typically one of the first checks a controller action method would make

A suggestion: work on the basis that features default to “off”. Seriously, the additional code and deployment configuration complexity needed for “default to on” is really not worth it. If you have to ship the code with the feature “on”, then re-think your design.

How to Configure / Control Feature Toggles

To get started, you could have a simple “features” configuration file that you ship with your application. This does make it difficult to turn features on or off, other than when you deploy. A database (with an optional admin screen) gives you more control. If you have a microservices / service-based architecture, you might want a separate “features” microservice, although managing a small set of features for each individual UI app is probably going to work better (the feature toggle is generally used to control the start of the user journey). If you’re building your own admin system, a single shared admin UI calling APIs on your platform of client apps and services probably makes sense.

The Life of a Feature Toggle

Over its short life, the process should be:

  1. Create feature toggle, develop the new code, test locally in “on” and “off” states
  2. Allow the new code to ship to Test and Production in incomplete state, leave the feature “off”
  3. When development is complete, ship code to Test and test the “on” and “off” states
  4. Ship to Production, turn on the toggle
  5. After a few days/weeks without problems, remove the toggle and the “old code path” from the code – the change will work its way through to Production

Creating and Removing Features

Features should be easy to create and remove. You should only need to “create” a feature in one place to use it (eg. a “master feature list” enum). If you go for a database approach, don’t require migrations (or worse, a manual developer process) to add database records – just have the database record automatically created when you first toggle a feature on, and assume that “no matching record” means the feature is “off” (if you’re using config files, assume that “no setting” means feature defaults to “off”).

If you store features in a database, but your code holds the master record of the features that exist, you’re going to end up with a load of orphaned records for the features you removed. If you really care, you can clean these up manually in the database, or just have your application check (eg. on startup) the master list of features against the DB records and purge any old features.

The simplest approach is actually to do nothing to create features and just use a convention of referring to features by name, but if you can reference a “master list” of known features, you guard against any nasty issues with typos later on, and it’s easier to find feature references when you come to remove them later.

Advanced Feature Management

For bonus points, if you have a lot of feature toggles to manage, you might want to build the following into your admin system and testing process:

  1. The ability to export/import a set of toggles, so you can replicate the live setup on development or test environments
  2. The ability to script setting toggle states for doing an automated test run
  3. The ability to compare the toggles on two different environments (eg. live vs test)
  4. An indication of when a feature was toggled on (so you know it’s safe to remove a toggle after a couple of weeks)
  5. An audit trail of who turned the feature on, and when

Performance

If your feature toggles are stored in a database (or features microservice), there’s a cost to each feature check, and every user action could check the same toggle multiple times on the same screen (UI, security, code-branching). Invest in caching, even if it means that some users might have to wait an hour for the feature to become available.

Kill Your Feature Toggles!

It’s cool to be able revert new features to their old state, but every feature toggle in your codebase has a maintenance cost. And if a feature is hard to remove, that might indicate a more fundamental problem (is it really a long-term configuration setting?). When you’re happy with a feature being “on”, then bake it in to the code.

My ultimate aim would be to add an “auto-lock” mechanism to any feature toggle system, automatically locking a feature “on” after it had been enabled in production for a set length of time.

Not Everything is a Feature Toggle – Real World Lessons

I helped build a bespoke feature toggling system into a system that was deployed in two countries (UK and US deployments), each of which had its own test and live environments (in addition to 20 local-developer environments). We often had 30-40 toggles in operation at once. The ability to import/export toggles was very useful, as was the ability to compare UK/US or test/live.

We did end up with a lot of features that were hard to kill, either because they were buried deep in the code (always check features as early in the user request/journey as possible), inter-dependent so you had to remove them in a set order, or because they had been built “for the US only” and were actually configuration settings. To help deal with the long list of features, I ended up adding description attributes to each feature in the “master list” enum with a standard “Jira card number and long description” comment, and using this to provide column-sorting options on the admin screen.

I used some of the same tricks when refactoring the feature toggle system in another application. Putting the “master list” of features in the code helped simplify an app that was initially built to use the database as the source of all feature toggle knowledge (migration-heavy feature toggle creation). The admin screen didn’t get any more complex when it switched to reading known features from an enum instead of a database, and the setup process replaced “create an enum and a migration” with “add an enum”.

Remember, every feature toggle you add should be removed and baked into the code a couple of weeks after it gets turned on in production. If it relates to a “for region X only” feature, it should still be safe to turn it on in all regions.

Feature Toggles and A/B Testing

A simple implementation of feature toggles is to only have “on” and “off” states, but you could if you wanted extend feature toggles to include A/B testing or “canary release” scenarios. Assuming you have a production environment with “A” and “B” deployment slots (one handles the lucky “canary” users), you could instead have three feature states – “off”, “on in B only”, “on for all”. Your feature toggle system becomes a little more complicated, but you don’t need to change a single line of code when checking features. Your code can still ask the question “is feature X enabled?”, and the underlying logic will determine whether you’re running on the A or B environment and whether the feature is “on” for you. You could even expand this approach to any number of “slots”, depending on complexity – a shared development environment might need such an approach to allow individual developers to independently toggle features.