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”

Leave a comment