Pragmatism in the real world

Setting the version of a Go application when building

When I was building Rodeo, my command line Flickr uploader, one thing I wanted to do was set the version number that you see when you type rodeo -v to the correct version when I build the application for release.

The basic process I wanted was:

  1. Tag git repository with new version number and push
  2. Build rodeo with that version without editing any files
  3. Upload binary

I’ve emphasised the important part. I don’t want to change a number in a file when I do the build as then the files are different from the tag. I realise that I could change a number before I tag, but that’s easily forgotten.

The way to do this in Go is to use ldflags switch to the build command. This will insert information into the binary when the linker creates it.

Create a Version variable

For this to work, you need to set up variable for the linker to modify.

As I’m using the Cobra library for this app, I created a Version variable in commands/root.go:

...
var Version = "dev-build"

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
	Version: Version,
	Use:     "rodeo",
...

I set it a default of dev-build so that I don’t confuse a development build with a release one and as you can see, this is then used by the root Command object that I create, so Cobra knows the version and can display it in response to the -v parameter.

if we build the application and run it we see this:

Screenshot of terminal window showing building and running of rodeo verison 'dev-build'

Override Version when building

Next, we need to overide this variable when building. This is done using -ldflags like this:

go build -ldflags "-X github.com/akrabat/rodeo/commands.Version=1.0.0"

The contents of ldflags is passed to the go tool link command. There’s quite a few, but the interesting one in this case is -X:

-X importpath.name=value
	Set the value of the string variable in importpath named name to value.
	This is only effective if the variable is declared in the source code either uninitialized
	or initialized to a constant string expression. -X will not work if the initializer makes
	a function call or refers to other variables.

I’m using Go modules and each package name needs to be globally unique. One common convention, that’s also easy, is to use a reverse URL format, so for rodeo, my go.mod file has module github.com/akrabat/rodeo in it.

Hence, we can reference the Version variable in the commands directory using github.com/akrabat/rodeo/commands.Version and set it to whatever we need it to be:

Screenshot of terminal window showing building and running of rodeo verison '1.0.0'

(Not that rodeo has reached the giddy heights of 1.0 yet!)

Scripting it

This is obviously most useful when scripted. We can access using git describe --tags HEAD and then it’s simple to build up a script to build the binary with the correct version number:

version=`git describe --tags HEAD`
go build -ldflags "-X github.com/akrabat/rodeo/commands.Version=$version"

This way we can now build the binary for release with the correct version number within it.