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.