When you’re shipping software, you need to know what features, known bugs and fixed bugs have been shipped to each environment (test, uat, live).
This is essential, because when someone reports an issue, you need to know whether it’s an issue that’s been fixed in a different version to the one they’re using, or whether it’s a new issue.
In git, versions are usually handled by “tagging” a commit, which labels a specific commit as being a particular version.
This is so standard that github.com will pick tags up automatically as a release, but how can we get our Go program to know that the commit has a version assigned to it?
I had a quick look and found a few approaches:
- Godeps doesn’t use semantic versioning and checks in a monotonically increasing number instead: https://github.com/tools/godep/blob/master/version.go
- Terraform uses semantic versioning, but from what I can see, still bumps the version string in a file: https://github.com/hashicorp/terraform/commit/5861802d08f5ed3a1559ef474f2ae34008700367
Regardless, some sort of version string needs to be present in the software source code, and the convention seems to be that it’s found in
I read the State of Go Packaging document (https://docs.google.com/document/d/1M4Ldgtxr9vC8wpyTldQe5oUyGN9eb1CeUcJT3N5XaPk/edit#heading=h.6lfzusuj3jzn) and Dave Cheney’s proposal (https://github.com/golang/go/issues/12302) – both suggest tagging your repo with a version (prefixed with
v1.0.0), but that doesn’t get the version number baked into your software in the way that Terraform are doing it.
My team wanted a way to be able to handle versioning by pushing a tag, and letting the build server handle baking the version number into the software, so I figured the best way to do this would be to use the
go generate tool to interact with
git and pull the tag for the commit into the build that way.
This turned out to be easy enough, the steps are:
- Tag your git repository with a version number (ideally based on the semantic versioning specification e.g. v0.0.0).
git tag -a v0.0.0 -m "First tag."
- Push the version number to the remote repository.
git push --tags
- Generate an up-to-date version_PLATFORM.go file (normally done by your CI server)
- The version_PLATFORM.go file gets automatically updated with data from the tag.
- Build as normal
I put together a working example of this at https://github.com/a-h/version – there’s a script for each platform I work on (Windows, OSX and Linux) which allows the build system to take account of differences in shell environments.
I think it’s important to have both a version HTTP endpoint for web applications and a version flag for command line tools, so I’ve also added examples of those too.
What do you think?