How Glide Solves Go Vendoring
Glide is a practical tool for managing Go workspaces and dependencies in a sophisticated way, but without any sophisticated configuration.
Inspired by gpm and gvp, Glide manages both your GOPATH and your dependencies to eliminate the need for vendoring, path munging, and version control subtrees.
This post shows by example how to use Glide.
Update: Added an example of private repositories.
Getting Started
Let's say I have a new Go project called foo
. Its full name will be
github.com/technosophos/foo
. In my local development environment, I have a
directory called foo
which contains main.go
.
To start up my Glide project, I do this:
$ cd foo
$ glide create # Create a new glide.yaml file
$ vi glide.yaml # edit my new YAML file
$ glide in # start using Glide.
Above, we create a new glide.yaml
file, edit it, and then use glide in
to
set up all the important environment variables.
The glide in
command will configure the environment variables like GOPATH
and PATH
so that your usual Go tools will all work in a predictable way.
(Strictly speaking, you don't
need to use glide in
to use Glide, but I find it helpful. You may
prefer using glide gopath
to emit the GOPATH location.)
Now let's take a look at the YAML file.
A Basic glide.yaml
Glide uses a simple YAML configuration file to declare information about a given project. A bare-bones example looks something like this:
package: github.com/technosophos/foo
import:
- package: github.com/Masterminds/cookoo
- package: github.com/aokoli/goutils
The above declares that our new project is called github.com/technosophos/foo
,
and then declares that the project has two dependencies.
The first time you create a glide.yaml
file, you should run glide install
:
$ glide install
The glide install
command will install the dependencies and configure the
workspace for working on this new foo
project. By default, it will create a
new _vendor/
directory and use that to store local dependencies. But
installing requires the glide.yaml
file, so make sure you create that
first using glide create
.
From then on, you can keep your packages up to date with another command:
$ glide update
glide update
will update packages. In the case above, it will update the local
packages to the latest revision. In a moment we'll see how to alter that
behavior. Both install and update will also intelligently build the libraries
into Go archive files (foo.a
), just like go get
and go install
do.
That's really all you need to get going with Glide. But here are some examples that show more of the features of Glide.
More Examples
Above and beyond merely supporting basic dependency downloading and installing, glide also supports more sophisticated management.
Example 1: Use only a specific version of a package
Say we want to only use a specific version of a package. Let's say, for example,
that we want to use version 1.1.0
of Cookoo. We can easily do this by setting
an explicit reference:
package: github.com/technosophos/foo
import:
- package: github.com/Masterminds/cookoo
ref: 1.1.0
vcs: git
This tells Glide to checkout the 1.1.0
tag from the git repository.
Glide can take commit numbers, tags, and branches in the ref
argument.
Example 2: Alias a repository
One thing that Go developers sometimes struggle with is the fact that
package names are coded into the import
statement. But when working
on, say, testing a git clone of a repository, you have to rewrite all of
your package's import statements.
Glide provides a simple way of working around this. With Glide, you can alias a VCS repo to another name:
- package: github.com/technosophos/foo
repo: git@bitbucket.org:technosophos/foo
vcs: git
In this case, Glide will check out a bitbucket.org/technosophos/foo
copy of
the repo, but configure it to act like github.com/technosophos/foo
. So
Go code that does import "github.com/technosophos/foo"
will actually
be using the bitbucket clone. Glide manages all of this for you...
without changing a single line of your Go code.
Example 3: Get a full repo, but only build part
Sometimes you need to go get
an entire repository, but you only plan
on using one or two pacakges. You can declare this intent like so:
- package: git.apache.org/thrift.git
vcs: git
ref: master
repo: http://git.apache.org/thrift.git
subpackages:
- lib/go/thrift
The below checks out the entire Thrift source code, but only builds Go
archives for the subpackage lib/go/thrift
. Practically speaking, this
particular example mirrors the behavior of go get
. But because
subpackages
can take multiple arguments you can optimize the behavior:
- package: github.com/crowdmob/goamz
vcs: git
ref: master
subpackages:
- aws
- s3
This builds the aws
and s3
subpackages.
Example 4: Use a Private Repository
In addition to handling public repositories, Glide can handle private repositories, too.
Assuming that you can do a git clone
from your commandline, you can also
use Glide to handle a private repository:
- package: bitbucket.org/technosophos/foo
repo: git@bitbucket.org:technosophos/foo
vcs: git
ref: feature/bar
The above will pull package bitbucket.org/technosophos/foo
from the
repository path specified in repo: git@bitbucket.org:technosophos/foo
.
Also in the example above, note that we're pulling from a different branch --
feature/bar
instead of master
. This is certainly not required, but it is
allowed.
Conclusion
Glide has two goals:
- Manage project-specific GOPATHs with dependencies stored in a local
_vendor/
directory. - Manage package dependencies, including with versioning.
When these are done right, builds are reproducible, dependencies are stablized, and projects are isolated. This is better not only for ongoing development, but for the entire development-to-production lifecycle.
I've swapped Glide into just about every Go project I'm now working on, from small command-line clients to large servers with dozens and dozens of external package dependencies. It works.