/ #Go #Git 

Dynamic Versioning your Go Application

Learn how to use Git tags to dinamically version your Go application.

I’ve been working on a small application written in Go and I wanted to display the version as part of the help text or when the application was called using the --version flag.

My first approach was traditional. Create a variable named version and print its value when the application is invoked.

The code looks like this:

package main

import "fmt"

var version = "v0.1"

func main() {
    fmt.Printf("Version: %s\n", version)
}

This approach was good enough, but it quickly became a point of failure as I could forget to update the variable when releasing a new version. Besides, the build process was done using a Makefile so I wondered if there was a way to update the version whenever the application was built.

It turns out Go has a very elegant solution to this problem.

When running or building an application, Go allows parameters to be passed to the linker by using the argument -ldflags.

The linker provides the argument -X, that sets the value of a string variable and has the form:

-X importpath.name=value

Putting it all together, I changed my build script to the build the application like this:

go build -ldflags "-X main.version=NEW_DYNAMICALLY_GENERATED_VERSION"

This can also be used in conjunction with go run. For example:

go run -ldflags "-X main.version=v6.90" app.go

Using Git tags

I use Git for version control and I use tags to control the versions released. The development process is very simple and once I have the changes I want in place, I’ll create a tag with the version being released.

What if I could assign the tag name to my version variable every time I build the application?

Easy. Let’s start by listing the tags in the repository:

git tag

The output when I run this command is:

$ git tag
v1.49
v1.50
v1.51

These are all the versions I released. But I need only the latest. You can sort the output of your tag command using the --sort flag and providing the sort key, like this:

git tag --sort=-version:refname

Where the key version:refname (or v:refname) make the tag names be treated as versions. The dash (-) in front of version make the sort descending.

Running this command considering the previous example would output:

$ git tag --sort=-version:refname
v1.51
v1.50
v1.49

We have the list of versions sorted with the latest on top; now we only need to extract that single version. If you’re on macOS, Linux, or Unix, you can make use of head. Head is a utility that display the first lines of a file.

Putting it all together:

git tag --sort=-version:refname | head -n 1

The -n 1 argument tells head to return only the first line.

Runnin these commands together give me:

$ git tag --sort=-version:refname | head -n 1
v1.51

A simple way of putting it all together would be (if you running these commands on Bash):

go build -ldflags "-X main.version=`git tag --sort=-version:refname | head -n 1`

The application at the beginning of this post, after running the command above, would output:

Version: v1.51

Now be creative and adapt it to your workflow.


Cover picture by Mika Baumeister via Unsplash