Ilija Matoski

    ← Articles

    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 string
    var BuildHash string
    var BuildDate string
    var BuildClean string
    var 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 VERSION
    0.0.1

    To 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 -version
    Name: application-name
    Version: 0.0.1
    Git Commit Hash: 5f80f2f91251474da7e92ccddbcabecad0fea35d
    Build Date: Thu Oct 13 22:16:08 UTC 2016
    Built from clean source tree: no
    OS: linux
    Architecture: amd64

    Usually 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=bin
    BIN_FILE=application-name
    VERSION_FILE=VERSION
    GO_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=yes
    else
    BUILD_CLEAN=no
    endif
    .PHONY: check-env
    check-env:
    ifndef GOPATH
    $(error GOPATH is undefined, you need to configure your system ...)
    endif
    ifndef GOROOT
    $(error GOROOT is undefined, you need to configure your system ...)
    endif
    ifndef HAS_GO_BIN
    $(error go command couldn't be found in PATH)
    endif
    bin:
    mkdir ${BIN_DIR}
    .PHONY: build
    build: ${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: clean
    clean:
    rm -fv ${BIN_DIR}/${BIN_FILE}

    Let me know in the comments if you use some other ways of versioning your go application.