Injecting Build Time Variables

September 30, 2019
golang | build | git

A common use-case for build-time variables is to inject the Git version information in the binary. There are several approaches on how to do this, but this one is the one I found the most useful one.

Let’s start with creating a sample project using Go modules:

$ mkdir example-go-build-time-variables
$ cd example-go-build-time-variables
$ go mod init github.com/pieterclaerhout/example-go-build-time-variables
go: creating new go.mod: module github.com/pieterclaerhout/example-go-build-time-variables

As we want to play with the Git revision and branch info, let’s also initialize it as a Git repository:

$ echo "# example-go-build-time-variables" >> README.md
$ git init
Initialized empty Git repository in ~/example-go-build-time-variables/.git/
$ git add .
$ git commit -m "first commit"
[master (root-commit) 47c55c2] first commit
 2 files changed, 4 insertions(+)
 create mode 100644 README.md
 create mode 100644 go.mod

Let’s start with creating the most simple app we can think of by adding a main.go file with the following contents:

main.go

package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello stranger!")
}

Let’s also create a package called version in which the version information is going to be stored:

version/version.go

package version

// GitRevision will be injected with the current git commit hash
var GitRevision string

// GitBranch will be injected with the current git branch name
var GitBranch string

I prefer to create a non-related package just containing this information. This makes it easy to access the version information from anywhere.

We can then update the main.go file to use this information:

main.go

package main

import (
	"fmt"

	"github.com/pieterclaerhout/example-go-build-time-variables/version"
)

func main() {
	fmt.Println("Git Revision:", version.GitRevision)
	fmt.Println("Git Branch:", version.GitBranch)
}

If we build and run it, you’ll get the following output:

$ go build -o example-go-build-time-variables .
$ ./example-go-build-time-variables
Git Revision: 
Git Branch: 

That’s the expected behaviour as we didn’t inject the version information yet.

To inject the information, you need to specify the -ldflags option during the go build process. You can test it manually by changing the build command to:

$ go build -o example-go-build-time-variables -ldflags "-X github.com/pieterclaerhout/example-go-build-time-variables/version.GitRevision=revision -X github.com/pieterclaerhout/example-go-build-time-variables/version.GitBranch=branch" .
$ ./example-go-build-time-variables
Git Revision: revision
Git Branch: branch 

Now, the only thing we need to do is to get the proper Git revision and branch name. The revision can be found with the following command:

$ git rev-parse --short HEAD
47c55c2

The branch name is obtained by the following command:

$ git rev-parse --abbrev-ref HEAD | tr -d '\040\011\012\015\n'
master

To make it easier, I usually combine everything in a simple Makefile which looks as follows:

APPNAME := example-go-build-time-variables
PACKAGE := github.com/pieterclaerhout/example-go-build-time-variables/version

REVISION := $(shell git rev-parse --short HEAD)
BRANCH := $(shell git rev-parse --abbrev-ref HEAD | tr -d '\040\011\012\015\n')

build:
	go build -ldflags "-X $(PACKAGE).GitRevision=$(REVISION) -X $(PACKAGE).GitBranch=$(BRANCH)" -o $(APPNAME)
	
run: build
	./$(APPNAME)

You can then simply run either make build to create the build or make run to build and run the application:

make run
go build -ldflags "-X github.com/pieterclaerhout/example-go-build-time-variables/version.GitRevision=47c55c2 -X github.com/pieterclaerhout/example-go-build-time-variables/version.GitBranch=master" -o example-go-build-time-variables
./example-go-build-time-variables server
Git Revision: 47c55c2
Git Branch: master

The complete source code for this example can be found here.