Go Vendoring On The Cheap

Go is great, but I just had to do something about its borderline insane dependency management.

Being able to do import github.com/foo/bar is cute, but there’s very little guarantee that dependencies won’t change overnight, and no practical way of pinning stuff to a specific version/revision. Having a strong desire to avoid a repeat of my early experiences with NodeJS, where stuff like express broke all of my code a few weeks down the road, I started looking for solutions early on.

But all the approaches I came across required third-party tools that are entirely too much hassle to set up and use reproducibly, so after a little experimentation, I converged on a rather lazy approach that mimics my usual Python pattern:

I set GOPATH to a vendor directory inside each project repository, go dep into that and clean out the .git/.hg folders in order to commit all the dependencies together with the project.

It’s a little wasteful in terms of storage, but perfectly doable for my ongoing projects and ensures that I’ll be able to rebuild things six months down the line. Plus it’s trivial to automate with a Makefile like so (which also makes it easy for me to do cross-compiles):

export GOPATH:=$(shell pwd)/vendor
export PATH:=$(PATH):$(GOPATH)/bin

$(BINARY): *.go
    go build -o $(BINARY)

    mkdir -p vendor
    go get github.com/rakyll/globalconf
    go get github.com/efarrer/iothrottler
    find vendor -name .git | xargs rm -rf
    find vendor -name .hg | xargs rm -rf
    git add vendor/src

    rm -f $(BINARY)
    go fmt *.go

    GOARCH=arm GOARM=6 go build -o $(BINARY)-rpi

    GOARCH=arm GOARM=5 go build -o $(BINARY)-syno

So to add a dependency I just add another go get line, do a make deps and all the required code is added to the project. To update a dependency, I just remove its files and do it again. A trifle unsubtle, for sure, but it works, and has the additional side benefit of my being able to organize my projects anywhere on the filesystem without having to submit to Go’s “there can be only one way to organize your code” silliness.

Since make is available pretty much everywhere and not something that is likely to go out of fashion (outside the NodeJS world, that is, where they keep reinventing it), I think this solves my problems for the foreseeable future – or until Go standardizes on something at least as simple to use and maintain.