Tracking versions in Go
Some of my services were written in Go, and we wanted a way to track which version was used, so we can track and solve issues. Or even see when the application was deployed or from which source was built. The code is on git, so adding a git commit hash to the mix will make our problem easier. Luckily go provides us with a simple way of doing this.
Lets look at some of the stuff I keep track of so I can easily see which version I’m using. I usually call this file version.go and drop it in the root of the package.
package main
import ( "flag" "fmt" "os" "runtime")
var version = flag.Bool("version", false, "Print the version and exit")
var BuildVersion stringvar BuildHash stringvar BuildDate stringvar BuildClean stringvar Name = "application-name"
func init() { flag.Parse() if *version { fmt.Printf("Name: %s\n", Name); fmt.Printf("Version: %s\n", BuildVersion); fmt.Printf("Git Commit Hash: %s\n", BuildHash); fmt.Printf("Build Date: %s\n", BuildDate); fmt.Printf("Built from clean source tree: %s\n", BuildClean) fmt.Printf("OS: %s\n", runtime.GOOS) fmt.Printf("Architecture: %s\n", runtime.GOARCH) os.Exit(1) }}Lets also create a file called VERSION, and we use that file to keep track of what version is our application. My version file contains the following information
$ cat VERSION0.0.1To build the application that will show me the version details I would issue the following command, I usually write a Makefile that will generate the command bellow, with all the necessary information.
go build -ldflags "-X 'main.BuildVersion=$(cat VERSION)' -X 'main.BuildHash=$(git log --pretty=format:'%H' -n 1)' -X 'main.BuildDate=$(date -u)' -X 'main.BuildClean=no'" -o app .After building the application, we can run it and see the following details
$ ./app -versionName: application-nameVersion: 0.0.1Git Commit Hash: 5f80f2f91251474da7e92ccddbcabecad0fea35dBuild Date: Thu Oct 13 22:16:08 UTC 2016Built from clean source tree: noOS: linuxArchitecture: amd64Usually all our errors are logged to an elasticsearch cluster, and we can easily track and check if there are any errors and from which services the errors occurred.
And here is the Makefile that I use to automate this
.DEFAULT_GOAL := build
BIN_DIR=binBIN_FILE=application-nameVERSION_FILE=VERSIONGO_BIN=$(shell which go)
HAS_GO_BIN := $(shell command -v go 2> /dev/null)
GIT_STATUS=$(shell git status --porcelain)BUILD_VERSION:=$(shell git log --pretty=format:'%h' -n 1)BUILD_DATE:=$(shell date -u)
ifneq ($(wildcard $(VERSION_FILE)),) VERSION:=$(shell cat $(VERSION_FILE))else VERSION:=endif
ifeq ($(GIT_STATUS),) BUILD_CLEAN=yeselse BUILD_CLEAN=noendif
.PHONY: check-envcheck-env:ifndef GOPATH $(error GOPATH is undefined, you need to configure your system ...)endififndef GOROOT $(error GOROOT is undefined, you need to configure your system ...)endififndef HAS_GO_BIN $(error go command couldn't be found in PATH)endif
bin: mkdir ${BIN_DIR}
.PHONY: buildbuild: ${BIN_DIR} check-env ${GO_BIN} build -ldflags "-X 'main.BuildVersion=${VERSION}' -X 'main.BuildHash=${BUILD_VERSION}' -X 'main.BuildDate=${BUILD_DATE}' -X 'main.BuildClean=${BUILD_CLEAN}'" -o "${BIN_DIR}/${BIN_FILE}" .
.PHONY: cleanclean: rm -fv ${BIN_DIR}/${BIN_FILE}Let me know in the comments if you use some other ways of versioning your go application.