I’d like to replace retool with go modules tools.go “tools as dependencies”. However I’m struggling to understand how this works when my developers and CI env all use different operating systems.
I want to make sure each environment uses the exact same version of tools.
For a concrete example, my app requires the protoc compiler to generate go code via github.com/golang/protobuf/protoc-gen-go. I have 3 OS, all needing to execute protoc with the protoc-gen-go plugin/generator:
- Ryan: uses MacOS
- Joe: Linux (Ubuntu)
- CI: Linux (CentOS)
I currently use retool to make sure ALL environments are locked in on the same version of tools (protoc-gen-go in this ex):
retool do build/bin/protoc -Ibuild/protoc/include -I. rpc/platform/platform.proto --go_out=.
My new go modules / “tools as dependencies” setup
tools.go:
// +build tools
package tools
import (
_ "github.com/golang/protobuf/protoc-gen-go"
)
Set the path that go install
will use:
export GOBIN=$PWD/bin
Install:
go install github.com/golang/protobuf/protoc-gen-go
If Ryan runs the go install ..
a bin/protoc-gen-go
MacOS executable is created.
Questions:
- At this point, why is
protoc-gen-go
tool version (or git hash) NOT listed in go.mod? - When Joe clones the app repo, how does he get and compile the same version of protoc-gen-go that Ryan used?
- How does
protoc
know to use theprotoc-gen-go
executable generator in my./bin
dir?
2
Answers
Go modules work with the imports of your .go files. If they find an import, they will automatically download the latest version that satisfies your requirements. You have to read https://github.com/golang/go/wiki/Modules and understand how modules work since Go 1.11 and later.
This is because protoc-gen-go is just an external tool as far Go modules are concerned. You don’t import
golang/protobuf/tree/master/protoc-gen-go
but the code it generates.Use:
to install a specific version on each machine of the users. Probably write a build script that automates the process.
From the github docs:
The compiler plugin, protoc-gen-go, will be installed in $GOPATH/bin unless $GOBIN is set. It must be in your $PATH for the protocol compiler, protoc, to find it.
I was able to accomplish vendored tools build for protoc (and plugins like Twirp) following the Go Modules tools guidelines, plus a little Makefile-Fu for the protoc binary.
A complete working example can be found in the Aspiration Labs pyggpot repo. Following are the essential details. Worth noting: getting the import path right for some of the tools was very fiddly, but ultimately, successful.
For
protoc
itself, I vendor the binary release in the Makefile and set it up into atools/bin
dir:The
PROTOC_PLATFORM
string can be automated with something like OS detecting makefile. The version of that we use is at https://github.com/aspiration-labs/pyggpot/blob/master/build/makefiles/osvars.mk.On to building the go tools. Create a
tools.go
something likeNote: the
// +build tools
tag will keepgo build
from over-building tools imports in your final build.Finally, some make code to build your go tools:
And finally, a
make setup
target to rungo mod vendor
and process the targets above.