How Glide Solves Go Vendoring

Sep 22 2014

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:

  1. Manage project-specific GOPATHs with dependencies stored in a local _vendor/ directory.
  2. 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.