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