From eed92f9df5eed311eb96d9cb3754dfb187bd25d7 Mon Sep 17 00:00:00 2001 From: Ariejan de Vroom Date: Mon, 12 Oct 2015 13:00:24 +0200 Subject: [PATCH] Add: Building golang cli tools update --- ...-10-12-building-golang-cli-tools-update.md | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 content/posts/2015-10-12-building-golang-cli-tools-update.md diff --git a/content/posts/2015-10-12-building-golang-cli-tools-update.md b/content/posts/2015-10-12-building-golang-cli-tools-update.md new file mode 100644 index 0000000..f504075 --- /dev/null +++ b/content/posts/2015-10-12-building-golang-cli-tools-update.md @@ -0,0 +1,112 @@ ++++ +date = "2015-10-12" +title = "Building Golang CLI Tools Update" +tags = ["golang"] +description = "After my previous post on using a `Makefile` to set version and build info I got some valuable feedback from other Gophers. Here's an update." +slug = "building-golang-cli-tools-update" ++++ + +In my [previous post][previous] I discussed how to use a `Makefile` to set version and +build information at compile time. Although this approach may work fine for you, it has +three drawbacks I want to discuss. + +### 1. Simplicity + +Andrew responded on the [golang-nuts mailing list][golang-nuts] with the following comment: + +> To me it seems like you took something simple and cross platform "go generate" + "go install/build" and turned it into something more complicated and less portable. + +Although I'm not sure `go generate` is relevant in this case, I agree that on some level +a `Makefile` is complicating things unnecessarily. Let's remove it! + +### 2. Non-reproducable builds + +Guilio responded with: + +> I only have an issue with buildTime: it makes the build not reproducible. + +This is a valid point. The idea that if you compile a given version of your application, +the resulting binary's hash (be it MD5 or whatever) should be equal to that of any other +binary build from that specific version. + +By using `BuildTime` the binary is never the same. + +Build time is also irrelevant. It does not matter _when_ a binary was compiled, but it +_does matter_ which precise version was build. + +Let's replace `BuildTime` with the current git commit hash instead. + +### 3. Why is there a `VERSION` file? + +If you're going to store version information under version control, you might as well +put it right in the code, where it belongs. + +Let's remove `VERSION` and instead create a nice `version.go` to handle everything. + +## Let's refactor + +Okay, first things to go are `Makefile` and `VERSION`. + +Next, I created a `core/version.go` which contains the necessary version information. +I've also taken the liberty of creating a proper struct for the version information, +including major, minor and patch numbers, as well as a label and release name. + + package core + + import "fmt" + + type version struct { + Major, Minor, Patch int + Label string + Name string + } + + var Version = version{1, 2, 3, "dev", "Chuck Norris"} + + var Build string + + func (v version) String() string { + if v.Label != "" { + return fmt.Sprintf("Roll version %d.%d.%d-%s \"%s\"\nGit commit hash: %s", v.Major, v.Minor, v.Patch, v.Label, v.Name, Build) + } else { + return fmt.Sprintf("Roll version %d.%d.%d \"%s\"\nGit commit hash: %s", v.Major, v.Minor, v.Patch, v.Name, Build) + } + } + +As you can see, it's quite easy to set and update the version numbers, label and release name. + +`Build` is still set at compile time and contains the current git commit hash. + +Because the `go build` command is quite long, I've put it in a nice `build.sh` file that +makes building easier. + + go build -ldflags "-X github.com/ariejan/roll/core.Build=`git rev-parse HEAD`" -o roll main.go + +This will result in a build that reports version information like this: + + $ ./roll version + Roll version 1.2.3-dev "Chuck Norris" + Git commit hash: b72b076af8b18ef4f6b10296f12840f23258acec + +### Check that SHA + +If you want, you can [grab the code][code] and run `./build.sh` yourself. The resulting binary + has a SHA-1 of `3ad7509279690d99e4144332dc200ede732663fd`. Yay for reproducable builds! + +### Naming variables in ldlags + +A short note on Peter Kleiweg's comment. He pointed out that I could use + + "-X main.Build=`git rev-parse HEAD`" + +This would be true if the `Build` variable is in the `main` package. But because it's +not (it's in `core`) I have to specify the full package name. + +## Thank you! + +Thanks to all the awesome gophers responding to my previous post! It's great to get +feedback and get to learn more about Golang. Keep the comments coming, please! + +[previous]: https://ariejan.net/2015/10/03/a-makefile-for-golang-cli-tools/ +[golang-nuts]: https://groups.google.com/forum/#!forum/golang-nuts +[code]: https://github.com/ariejan/roll