Use go mod to manage modules dependency and version

Module

Go has included support for versioned modules as proposed here - https://golang.org/design/24301-versioned-go since 1.11. The initial prototype vgo was announced - https://research.swtch.com/vgo in February 2018. In July 2018, versioned modules landed in the main Go repository.

Since Go 1.14 - https://golang.org/doc/go1.14, module support is considered ready for production use, and all users are encouraged to migrate to modules from other dependency management systems. If you are unable to migrate due to a problem in the Go toolchain, please ensure that the problem has an open issue - https://github.com/golang/go/wiki/Modules#github-issues filed. (If the issue is not on the Go1.16 milestone, please comment on why it prevents you from migrating so it can be prioritized appropriately). You can also provide an experience report - https://github.com/golang/go/wiki/ExperienceReports for more detailed feedback.

A module is a collection of related Go packages that are versioned together as a single unit.

Modules record precise dependency requirements and create reproducible builds.

Most often, a version control repository contains exactly one module defined in the repository root. (Multiple modules - https://github.com/golang/go/wiki/Modules#faqs–multi-module-repositories are supported in a single repository, but typically that would result in more work on an on-going basis than a single module per repository).

Summarizing the relationship between repositories, modules, and packages:

  • A repository contains one or more Go modules.

  • Each module contains one or more Go packages.

  • Each package consists of one or more Go source files in a single directory.

Modules must be semantically versioned according to semver - https://semver.org/, usually in the form v(major).(minor).(patch), such as v0.1.0, v1.2.3, or v1.5.0-rc.1. The leading v is required. If using Git, tag released commits with their versions. Public and private module repositories and proxies are becoming available (see FAQ - https://github.com/golang/go/wiki/Modules#are-there-always-on-module-repositories-and-enterprise-proxies below).

go.mod

A module is defined by a tree of Go source files with a go.mod file in the tree’s root directory. Module source code may be located outside of GOPATH. There are four directives: module, require, replace, exclude.

Here is an example go.mod file defining the module github.com/my/thing:

1
2
3
4
5
6
module github.com/my/thing

require (
github.com/some/dependency v1.2.3
github.com/another/dependency/v4 v4.0.0
)

A module declares its identity in its go.mod via the module directive, which provides the module path. The import paths for all packages in a module share the module path as a common prefix. The module path and the relative path from the go.mod to a package’s directory together determine a package’s import path.

For example, if you are creating a module for a repository github.com/user/mymod that will contain two packages with import paths github.com/user/mymod/foo and github.com/user/mymod/bar, then the first line in your go.mod file typically would declare your module path as module github.com/user/mymod, and the corresponding on-disk structure could be:

1
2
3
4
5
6
mymod
|-- bar
| `-- bar.go
|-- foo
| `-- foo.go
`-- go.mod

In Go source code, packages are imported using the full path including the module path. For example, if in our example above, we declared the module identity in go.mod as module github.com/user/mymod, a consumer could do:

1
import "github.com/user/mymod/bar"

This imports package bar from the module github.com/user/mymod.

exclude and replace directives only operate on the current (“main”) module. exclude and replace directives in modules other than the main module are ignored when building the main module. The replace and exclude statements, therefore, allow the main module complete control over its own build, without also being subject to complete control by dependencies. (See FAQ - https://github.com/golang/go/wiki/Modules#when-should-i-use-the-replace-directive below for a discussion of when to use a replace directive).

Usage

1
2
3
$ mkdir -p go-mod-example

$ cd go-mod-example

go mod init

The go mod init command initializes and writes a new go.mod file in the current directory, in effect creating a new module rooted at the current directory. The go.mod file must not already exist.

init accepts one optional argument, the module path for the new module. See Module paths for instructions on choosing a module path. If the module path argument is omitted, init will attempt to infer the module path using import comments in .go files, vendoring tool configuration files, and the current directory (if in GOPATH).

Usage:

1
go mod init [module-path]

Example:

Initialize a new module:

You can replace cloudolife.com/examples/go-mod-example with your preferred content.

1
2
3
$ go mod init cloudolife.com/examples/go-mod-example

go: creating new go.mod: cloudolife.com/examples/go-mod-example

Write your code:

1
2
3
4
5
6
7
8
9
10
11
12
$ cat <<EOF > hello.go
package main

import (
"fmt"
"rsc.io/quote"
)

func main() {
fmt.Println(quote.Hello())
}
EOF

go mod tidy

Usage:

1
go mod tidy [-e] [-v]

go mod tidy ensures that the go.mod file matches the source code in the module. It adds any missing module requirements necessary to build the current module’s packages and dependencies, and it removes requirements on modules that don’t provide any relevant packages. It also adds any missing entries to go.sum and removes unnecessary entries.

Build and run:

1
2
3
4
5
$ go build -o hello

$ ./hello

Hello, world.

The go.mod file was updated to include explicit versions for your dependencies, where v1.5.2 here is a semver tag:

1
2
3
4
5
$ cat go.mod

module cloudolife.com/examples/go-mod-example

require rsc.io/quote v1.5.2

go mod vendor

Usage:

1
go mod vendor [-e] [-v]

The go mod vendor command constructs a directory named vendor in the main module’s root directory that contains copies of all packages needed to support builds and tests of packages in the main module. Packages that are only imported by tests of packages outside the main module are not included. As with go mod tidy and other module commands, build constraints except for ignore are not considered when constructing the vendor directory.

When vendoring is enabled, the go command will load packages from the vendor directory instead of downloading modules from their sources into the module cache and using packages those downloaded copies. See Vendoring for more information.

Daily Workflow

Note there was no go get required in the example above.

Your typical day-to-day workflow can be:

  • Add import statements to your .go code as needed.

  • Standard commands like go build or go test will automatically add new dependencies as needed to satisfy imports (updating go.mod and downloading the new dependencies).

  • When needed, more specific versions of dependencies can be chosen with commands such as go get [email protected], go get foo@master (foo@default with mercurial), go get foo@e3702bed2, or by editing go.mod directly.

A brief tour of other common functionality you might use:

  • go list -m all — View final versions that will be used in a build for all direct and indirect dependencies (details)

  • go list -u -m all — View available minor and patch upgrades for all direct and indirect dependencies (details)

  • go get -u ./... or go get -u=patch ./... (from module root directory) — Update all direct and indirect dependencies to latest minor or patch upgrades (pre-releases are ignored) (details)

  • go build ./... or go test ./... (from module root directory) — Build or test all packages in the module (details)

  • go mod tidy — Prune any no-longer-needed dependencies from go.mod and add any dependencies needed for other combinations of OS, architecture, and build tags (details)

  • replace directive or gohack — Use a fork, local copy or exact version of a dependency (details)

  • go mod vendor — Optional step to create a vendor directory (details)

Referecens

[1] Modules · golang/go Wiki · GitHub - https://github.com/golang/go/wiki/Modules

[2] Go Modules Reference - The Go Programming Language - https://golang.org/ref/mod

[3] Using Go Modules - The Go Blog - https://blog.golang.org/using-go-modules

Semantic Versioning 2.0.0 | Semantic Versioning - https://semver.org/