Go is an incredible language, and the go tools are one of its many strengths. But it isn't perfect. In particular, managing a project's dependencies can be difficult.
go get installs the latest commit from master, unless there is a
go1 branch (in which case it installs the latest commit from this branch).
Semver? Forget about it. The official golang way is to
always have a clean master branch that maintains ascendant compatibility. In
practice, this often works well, but issues do occasionally arise. It's also
problematic if you want reproducible builds on different computers.
There are a large number of third-party tools to help with package management, but be warned, most of them are still young, have bugs, are opinionated about the workflow you should use, lack features, etc. I've tried some of them, and in this post I wanted to talk about my journey, as I think it will help some developpers choose the right tool for them.
My first attempt was godep. On paper, it looks like the most advanced tool. Well, it doesn't have as many features as Ruby bundler by far, but the additional tools aren't better. I couldn't get it to run (see the issue on github). It's probably my fault though, so you shouldn't reject it too quickly.
Next, I tried going in the opposite direction: trying the simplest thing and
hoping it would work. The tool was
dondur, and all it does is create a
.dondur.lock file that consists of all the imported packages and their
current version control hashes. It ran, but I wasn't satisfied. It's too
basic: it doesn't skip the standard packages, and it doesn't include the
dependencies of the imported packages.
So, I looked to the next possibility: johnny-deps. But it didn't take me long to see a blocker: it only works when external packages are hosted on github, and I use a library, gofpdf, which is hosted on google code. Johnny-deps has a fork, gpm, but that has the same limitation.
So I moved onto the next tool: Go Manager, or
gom for short. And this time, success! Gom works with a
Gomfile that lists
the dependencies, and you can constrain the tag, branch or commit you want to
bundle for each package. Gom will install them in the
vendor directory, and
gom commands will use your vendor as your
GOPATH to use the bundled
If you already have a project with some dependencies, you can avoid typing
them out in the Gomfile by using
gom gen gomfile. It will parse your package
and its dependencies recursively to find all the external packaged required to
build your project. Then, you can add the commit/tag/branch constraints by
editing the Gomfile, and install them with
gom install. To
build/test/install/run your project, just replace
gom, for example
gom run main.go. You also have
gom exec for using the bundled packages in
As a bonus, I've got two tips for you. Firstly, the
Gomfile uses a syntax
inspired by Ruby, so you can get syntax highlighting in vim by adding this
line in your
au BufRead,BufNewFile Gomfile setlocal ft=ruby
Secondly, I'm also a zsh user. So I wrote a
completion script for gom.
You can install it in
$HOME/.zsh/Completion/ to get nice completion by
$ gom <TAB> build -- Build with vendor packages doc -- Run godoc for bundles exec -- Execute command with bundle environment gen -- Generate .travis.yml or Gomfile install -- Install bundled packages into vendor directory run -- Run go file with bundles test -- Run tests with bundles
A few last words: if you want to know more about this topic, you should read Go Package Management, a insightful cry for gophers to unify on a dependency management solution.