In my last post I talked about how we aim to have a release build in Visual Studio produce production ready binaries. Obviously for something to be production ready it must have a version in order for users know what they're actually using. Just to clarify, when I talk about versioning in this post I refer to the technical version, most likely following SemVer, that you give the artifacts you release. I'm not taking about the marketing version that you might brand your things with.
The strategies for technical versioning I've seen can be split into 2 camps:
- Have the build server determine the version
- Commit version information into the repository it self
Since we want the version to be correct even when you go back in time and build older branches and tags #1 is ruled out. Other drawbacks with this strategy is that you need to have the build server up and running to produce a release meaningful version. We definitely want a local build to generate a good version so that puts us firmly in camp #2.
At this time 2 different forces where in play, firstly we didn't like all those "Bumping version to X" commits cluttering our repos. And a process like that cause a lot of emails from users saying "the latest release have the wrong version, did you forget to change it?" appear in your inbox. Secondly since we're moving to a multi repository model we needed to ensure that we use a consistent branching model across our repositories. Why is that important? Trust me spending the first few brain cycles on figuring out where to start coding every time you switch repo is definitely a drag on productivity.
For us the branching model we've decided to go with is GitFlow and as we started to discuss our versioning scheme we realized that if we follow the GitFlow model and also adhere to SemVer we can deduct the version based on the branch names and merge commits in Git. If we could pull that off we could kill 2 birds with one stone, version information is now embedded in the repo without dreaded "bumping to X" commits and we could also enforce our branching model by failing the build if we detected illegal commits and branch names. As an example a commit straight to "master" is not valid using GitFlow since the head will always point to either a merge commit for a hotfix or release branch.
GitFlowVersion was born
I'm not going into full detail on the algorithm as its already documented here but in short the develop branch is always one minor version above the master. Master is always the SemVer version of the latest merge commit (eg: hotfix-4.1.1 or release-4.3.0) and so on.
We use GFV across all our repos and so far it seems to hold up. The major hurdles so far has been all the quirks in NuGet that we've had to work around since the version generated by GitFlowVersion also is used to version our NuGet packages. One of the main snags was that NuGet has no concept of builds and since we use NuGet-packages to manage dependencies across repositories we need to make sure that our develop branch always outputs packages that sorts highest at all time. This forced us to give our develop builds the "Unstable" pr-erelease suffix. This makes sure that they stay on top since Unstable > Beta, RC, etc. I'll go into more details on how we do our cross repo dependencies in an upcoming post so stay tuned!
Here is the NServiceBus repository being versioned by GFV: (the number you see is the PR number that's assigned by GitHub)
Use GitFlowVersion to automatically version your code without mucking around with either the buildserver or VERSION.txt files...