Go Modules

Go’s package management (Go Modules, since Go 1.11, formerly called vgo) is quite different take coming from another programming language background. It is designed in a way it assume you’ll publish your package someday (but you don’t have to). Let’s take a look!

Background

Beofre Go Modules, everything was put under GOPATH (Workspace):

bin/
pkg/
src/

Since v1.5, vendor folder got introduced, Go will pick packages from vendor if available.

Go 1.11 introduced Modules support (2020.09.10 latest version is 1.15), it is an alternative to GOPATH.

Go’s program/repository has exactly 1 module. A module has many related packages versioned together. A package is a directory contain many Go source files (*.go).

Go module file

https://github.com/golang/go/wiki/Modules#gomod

// go.mod

module github.com/juanitofatas/lib-go/v2

go 1.15.2

require (
        github.com/stretchr/testify v1.6.1
)

Go rewrites your go.mod and go.sum files. // indirect appended when specified a custom dependency or dependency is not using Go Modules.

go.sum is not a lockfile. go.sum is a record to make sure modules not being modified by checking its checksum.

Version Conventions

Let’s see examples used in go.mod:

  • go.mod: module github.com/juanitofatas/mygo/v2
  • require: require github.com/juanitofatas/mygo/v2 v2.0.0
  • replace: replace github.com/original/proj github.com/juanitofatas/forked-proj v0.1.2.20200909123456-0f7232a2bf7e

Use in Go source file:

  • import: import "github.com/juanitofatas/mygo/v2/mypkg"

When your Go repository is still v0 or v1, the version suffix can be neglected for practical use (v0 does not need to care version compatibility, v1 is so common so omit).

version pattern vMAJOR.MINOR.PATCH.yyyymmddhhmmss-abcdefabcdef

Version follows SemVer and starts with v, v2.2.3.

Commands

go build, go install, go test will fetch dependencies automatically and cache dependencies if you run same command again!

  • go list -m -versions github.com/buildkite/agent — Show all versions of github.com/buildkite/agent
github.com/buildkite/agent v2.0.1+incompatible v2.0.3+incompatible
v2.0.4+incompatible v2.1.1+incompatible v2.1.2+incompatible
v2.1.3+incompatible v2.1.4+incompatible v2.1.5+incompatible
v2.1.6+incompatible v2.1.7+incompatible v2.1.8+incompatible
v2.1.9+incompatible v2.1.10+incompatible v2.1.11+incompatible
v2.1.12+incompatible v2.1.13+incompatible v2.1.14+incompatible
v2.1.15+incompatible v2.1.16+incompatible v2.1.17+incompatible
v2.3.1+incompatible v2.3.2+incompatible v2.4.1+incompatible
v2.5.1+incompatible v2.6.0+incompatible v2.6.1+incompatible
v2.6.2+incompatible v2.6.3+incompatible v2.6.4+incompatible
v2.6.5+incompatible v2.6.6+incompatible v2.6.7+incompatible
v2.6.8+incompatible v2.6.9+incompatible v2.6.10+incompatible
v3.1.2+incompatible v3.2.0+incompatible v3.2.1+incompatible
v3.3.0+incompatible v3.4.0+incompatible v3.5.0+incompatible
v3.5.1+incompatible v3.5.2+incompatible v3.5.3+incompatible
v3.5.4+incompatible

What does +incompatible mean?

  • go list -m -versions github.com/buildkite/agent/v3 — Show all v3 versions of github.com/buildkite/agent
github.com/buildkite/agent/v3 v3.0.0 v3.0.1 v3.1.1 v3.1.2 v3.2.0
v3.2.1 v3.3.0 v3.4.0 v3.5.0 v3.5.1 v3.5.2 v3.5.3 v3.5.4 v3.6.0
v3.6.1 v3.7.0 v3.8.0 v3.8.1 v3.8.2 v3.8.3 v3.8.4 v3.9.0 v3.9.1
v3.10.0 v3.10.1 v3.10.2 v3.10.3 v3.10.4 v3.11.0 v3.11.1 v3.11.2
v3.11.3 v3.11.4 v3.11.5 v3.12.0 v3.13.0 v3.13.1 v3.13.2 v3.14.0
v3.15.0 v3.15.1 v3.15.2 v3.16.0 v3.17.0 v3.18.0 v3.19.0 v3.20.0
v3.21.0 v3.21.1 v3.22.0 v3.22.1 v3.23.0 v3.23.1
  • go list -m all — list all dependencies
  • go list -u -m all — list all upgradable dependencies
  • go get -u — upgrade all dependencies to latest
  • go get -u=patch — upgrade all dependencies to latest patch
  • go mod tidy — remove unused dependency
  • go clean -modcache — to remove all modules

I am a user

You should not need to set GOROOT, GOPATH, and GO111MODULE.

To use a library say, Go Stripe v72:

import(
        "github.com/buildkite/stripe-go/v72"
)

I am a library maintainer

require / replace dependency in go.mod file.

module github.com/cli/cli

go 1.13

require (
        github.com/AlecAivazis/survey/v2 v2.1.1
        github.com/MakeNowJust/heredoc v1.0.0
        github.com/briandowns/spinner v1.11.1
        github.com/charmbracelet/glamour v0.2.1-0.20200724174618-1246d13c1684
        github.com/google/go-cmp v0.5.2
        github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
        github.com/hashicorp/go-version v1.2.1
        github.com/henvic/httpretty v0.0.6
        github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
        github.com/mattn/go-colorable v0.1.7
        github.com/mattn/go-isatty v0.0.12
        github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d
        github.com/mitchellh/go-homedir v1.1.0
        github.com/shurcooL/githubv4 v0.0.0-20200802174311-f27d2ca7f6d5
        github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f
        github.com/spf13/cobra v1.0.0
        github.com/spf13/pflag v1.0.5
        github.com/stretchr/testify v1.6.1
        golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
        golang.org/x/text v0.3.3
        gopkg.in/yaml.v2 v2.2.8 // indirect
        gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
)

replace github.com/shurcooL/graphql => github.com/cli/shurcooL-graphql v0.0.0-20200707151639-0f7232a2bf7e

From GitHub CLI.

Let’s say your library is currently at v2.

  • Main branch1 has v2 import paths
  • Release v3? Bump main branch to v4. Release v3 to a tag.

I want to deliver Go binaries easily

Use GoReleaser (Example).

Or write custom release script for each target platform: Example.

I am a v0 CLI maintainer

https://github.com/cli/cli

I am a v3 CLI maintainer

https://github.com/buildkite/agent

Upgrade to Go Modules Example: Go Stripe

Go Stripe upgraded to Go Modules in this PR.