Disclaimer: This post includes Amazon affiliate links. If you click on one of them and you make a purchase I’ll earn a commission. Please notice your final price is not affected at all by using those links.
Versioning dependencies is key because it allows us to know exactly what the final artifact will look like, it let’s us create deterministic builds that work the same as long as the dependencies don’t change. Regarding tool dependencies, specifically those tools written in Go, the versioning process is similar to what we typically do when using Go Modules.
The code used for this post is available on Github.
How to version tools in Go?
Although not necessary I recommend you to install
direnv, this tool allows you to sandbox environment variables by directory, and because it allows us to do that we can define our own
GOBIN for a concrete directory, what this means in practice is that by doing so we can we install binaries relative to the project we are currently working on without making them globally available in our system.
The Tools as Dependencies Paradigm is what we need, basically define a file that blank imports all the tools we need and make it only buildable for a concrete tag.
That paradigm works for sure but I like to take this a bit further and instead of making the
tools.go file part of your principal Go module I prefer defining another separated Go module inside an
internal package, for example
internal/tools; this is with the idea of keeping those dependencies clearly isolated from the principal Go module.
For example, assuming we depend on
sqlc, we will create a new module in
internal/tools, and then we will create a file called:
internal/tools/tools.go with the following content:
package tools import ( _ "github.com/kyleconroy/sqlc/cmd/sqlc" )
Next, we will
go get it:
go get github.com/kyleconroy/sqlc/cmd/sqlc@latest
This is so the
go.sum is populated correctly, by doing all of this if users of our Go module decide to import the principal package they will not require the packages needed for the tools we use but rather only those required by the principal package; and because we have our
tools package in
internal there’s no way external users are able to import that package either.
Please refer to the source code for more details about the final
Versioning is fundamental because we know for sure the exact dependencies we need for building our program, the same applies for external programs we need for our program to build, example of those are linters or code generators; having everything tagged as part of our repository allows building artifacts the same way no matter what because we know for sure the versions we depend on.
If you’re looking to sink your teeth into more Go-related topics I recommend the following:
- Get Programming with Go
- Go in Practice
- Go in Action
- Go Web Programming
- Go Programming Blueprints - Second Edition
- The Go Programming Language