Even though one of the famous Go proverbs says a little of copying is better than a little of dependency, sooner or later you will find yourself importing one into your project. You just cannot solve all the problems your application may be facing, and often it's better to defer to ready, battle-tested solutions. But how should you manage your dependencies? Since we've recently (finally!) saw the premiere of an official tool, dep, let's take a look on that.
One of the things that many Go programmers like about the language is that it uses tabs instead of spaces at all times. Many people mention it very quickly when talking about the bool features of the language. Why? It's not because tabs are better (let's not start a war now), but because the choice has already been made and everyone has to follow - there is no place for discussions as there is just one, the right way.
Unfortunately, the language creators dropped the ball a bit when it comes to solving the problem of vendoring the dependencies. There was never a single, recommended tool and it allowed the community to create multiple solutions that tackle this issue. Don't get me wrong, those projects that are available are truly awesome, but they will always raise some arguments about which one is the best. This is why I'm excited to see one tool to rule them all in
Setting it up
In order to start using
dep we need to install it first:
$ go get -u github.com/golang/dep/cmd/dep
Then, to use it with your project you first need to initialize it:
$ dep init
This creates two files:
Gopkg.toml which is often referred to as manifest and
Gopkg.lock. There is an important thing that we need to remember from now on: the manifest is something that we own (and edit), and the lock is handled by
dep. So in case of my
diwia project, I have three dependencies defined:
// Gopkg.toml ... [[constraint]] name = "github.com/boltdb/bolt" version = "1.3.1" [[constraint]] name = "github.com/pkg/errors" version = "0.8.0" [[constraint]] name = "github.com/spf13/cobra" version = "0.0.1"
While all the dependencies, including the transitive ones (the dependencies of my dependencies), with exact revision hashes, are defined in the other file:
// Gopkg.lock # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. [[projects]] name = "github.com/boltdb/bolt" packages = ["."] revision = "2f1ce7a837dcb8da3ec595b1dac9d0632f0f99e8" version = "v1.3.1" [[projects]] name = "github.com/inconshreveable/mousetrap" packages = ["."] revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" version = "v1.0" ... [solve-meta] analyzer-name = "dep" analyzer-version = 1 inputs-digest = "e265cd42611c0178be36724ffe88805554c2e3e83849e2f0419878e006807106" solver-name = "gps-cdcl" solver-version = 1
The second subcommand that we need to get familiar with is
ensure which checks what dependencies are being used (imported) in your code and ensures that they are listed in the manifest. Also, it checks the manifest for any unused dependencies and removes them from that file.
One important thing that you might have already noticed is the versions next to the dependencies. This comes from git tags assigned in the repositories, which allows you to track them much better. What is also important is that
dep will not take the latest version of the repository from
master branch if it noticed that there is some tag defined. It recognizes the tagged version as the last stable and selects that one as your dependency.
This is basically all you need to know about
dep to get started and have all your dependencies stored in
vendor/ directory. But there's more!
A step forward
dep is not yet a very mature project, there are still some things that it might be doing better. One of the issues people have with it, that even if you import
github.com/somebody/bad-repo/awesome-subpackage, it will fetch the whole
somebody/bad-repo from GitHub. This bugs me as well because the
vendor/ directory grows a lot (and so does your repository) for no reason. Fortunately, you can run
prune to get rid of all unused packages. In case of one of the projects I'm working with, we're talking about trimming 96MB to just 13!
Last but not least, there is a command called
status which allows you to check if the dependencies you are using in the project updated their versions:
$ dep status PROJECT CONSTRAINT VERSION REVISION LATEST PKGS USED github.com/boltdb/bolt ^1.3.1 v1.3.1 2f1ce7a 2f1ce7a 1 github.com/inconshreveable/mousetrap * v1.0 76626ae 76626ae 1 github.com/pkg/errors ^0.8.0 v0.8.0 645ef00 645ef00 1 github.com/spf13/cobra ^0.0.1 v0.0.1 7b2c5ac 7b2c5ac 1 github.com/spf13/pflag * v1.0.0 e57e3ee e57e3ee 1 golang.org/x/sys * branch master 665f652 665f652 1
Awesome! I definitely need to play around with
dep more, but I'm sure it will grow into one of the must-have elements of the Go toolchain.