One of the stated goals of Go was to provide a language which could be built without Makefile
s or other kinds of external configuration. This was realised with the release of Go 1, and the go tool which incorporated an earlier tool, goinstall,
as the go get
subcommand.
go get
uses a cute convention of embedding the remote location of the package’s source in the import path of the package itself. In effect, the import path of the package tells go get
where to find the package, allowing the dependency tree a package requires to be discovered and fetched automatically.
Compared to Go’s contemporaries, this solution has been phenomenally successful. Authors of Go code are incentivised to make there code go get
able, in much the same way peer pressure drives them to go fmt
their code. The installation instructions for most of the Go code you find on godoc and go-search has become, in essence, go get $REPO.
So, what is the problem?
Despite the significant improvement over some other language’s built in dependency management solutions, many Go developers are disappointed when the discover the elegance of go get
is a double edged sword.
The Go import declaration references an abstract path. If this abstract path represents to a remote DVCS repository, the declaration alone does not contain sufficient information to identify which particular revision should be fetched. In almost all cases go get
defaults to head/tip/trunk/master
, although there is a provision for fetching from a tag called go1
.
The Go Authors recognize that this is an issue, and suggest that if you need this level of control over your dependencies you should consider using alternative tools, suggesting goven as a possible solution.
Over the past two years no fewer than 19 other tools have been announced, so there is clearly a need and a desire to solve the problem. That said, none of them have achieved significant mind share, let alone challenged go get
for the title of default.
Making it personal
Here are three small examples from my work life where go get
isn’t sufficient for our requirements as we deliver our rewrite of Juju in Go.
Keeping the team on the same page
Juju isn’t just one project, but more than a dozen projects that we have written or rely on. Often landing a fix or feature in Juju means touching one of these upstream libraries and then making a corresponding change in Juju.
Sometimes these changes make it easy for other developers on the team to detect that they don’t have the correct version of a dependency as Juju stops compiling. Other times the effects can be more subtle, a bug fix to a library can cause no observed breakage so there is no signal to others on the team that they are compiling against the wrong version of the package.
To date, we’ve resorted to an email semaphore whenever someone fixes a bug a package, imploring everyone else to run go get -u
. You can probably imagine how successful this is, and how much time is being spent chasing bugs that were already fixed.
Reproducible builds
As a maintenance programmer on Juju, I regularly switch between stable, trunk and feature branches. Each of those expects to be compiled against exactly the right version of its dependencies, but go get
doesn’t provide any way of capturing this so I may reliably reproduce a build of Juju from the past.
Being a good Debian citizen
As our product eventually ends up inside Debian based distributions we have to deliver a release artifact which relies only on other Debian packages in the archive. We currently do this with a blob of shell scripts and tags on repos that we control to produce a tarball that contains the complete $GOPATH
of all the source for Juju and its dependencies for exactly this release.
While it gets the job done, our pragmatism is not winning us any favors with our Debian packaging overlords as our approach makes their security tin foil hats itch. We’ve been lucky so far, but will probably at some point have a security issue in a package that Juju depends on, and because that package has been copied into every release artifact we’ve delivered fixing it will be a very involved process.
What to do?
Recently William Kennedy, Nathan Youngman and I started a Google Group in the hope of harnessing these disparate efforts of the many people who have thought, argued, hit their head against, and worked on this problem.
If you care about this issue, please consider joining the [go-pm] mailing list and contributing to the Goals document. I am particularly interested in capturing the requirements of various consumers of Go packages via user stories.