Welcome, Go modules - part2


Go modules: part 1, part 2, part 3

Go module

A go module is a collection of related Go packages. A single unit that defines the minimum requirements that must be satisfied by their dependencies.

  • A go.mod file is placed in the root of the project. This location is known as the module root and contains all Go packages and subdirectories.

    • note: subtrees, i.e., submodules, can have their own go.mod files and will excluded when performing operations affecting all packages within a module, e.g., go test ./...

  • We used to have GOPATH, but instead we now have the concept of a main module

    • go commands will try to determine if the current working directory is the module root, i.e., location of the go.mod file. If not, it’ll keep looking up one directory until either a go.mod file is found or none.
    • being inside a module root or any of its subdirectories is referred to as being in module-aware mode.


GOPATH mode vs module-aware mode

In 1.11, module support is not enabled by default and is controlled by the env variable GO111MODULE.

GO111MODULE=auto (or unset)
default: go modules only supported outside the GOPATH

GO111MODULE=off
GOPATH mode: disable go module support, as if you’re on 1.10

GO111MODULE=on
module-aware mode: go commands will use go modules and never consult GOPATH to resolve imports


go.mod file

A go.mod file defines the precise set of packages available for use by go commands.

If the project is using a third-party dependency manager, e.g., dep, godep, glide, etc., then go mod init will add requirements based on the existing configuration, e.g., Gopkg.lock.

To start a new module go to the root of your project (must not already contain a go.mod) and run:

go mod init 

# The module name is guessed from import comments or vcs config
# To override, add the module path like so
go mod init github.com/mfridman/srfax

this will create a go.mod file in the current directory. Now run:

go get -d
# The -d flag instructs go to download packages, but not install them.

cat go.mod 
module github.com/mfridman/srfax

require (
    github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699
    github.com/pkg/errors v0.8.0
)

A few things to note:

  • In module-aware mode downloaded dependencies are still downloaded to GOPATH/pkg/mod and installed commands to GOPATH/bin, unless otherwise set.

  • If a dependency is tagged v2.0.0 or higher and contains no go.mod file it is suffixed with +incompatible (likewise for pseudo-versions, e.g., v2.0.1-0.yyyymmddhhmmss-abcdefabcdef+incompatible)

  • Dependencies of imported packages are marked with // indirect


Day-to-day commands

Out of the box your favorite go commands such as build, install, run, test, etc. will just work.

As you run them, the go.mod file will automatically be updated and new dependencies will be downloaded to satisfy imports.

From here on we’re assuming module-aware mode, which you can achieve in one of two ways:

  1. copy/move/start a project outside your GOPATH, yes you heard right! Anywhere.
  2. set env variable to GO111MODULE=on and continue working in your GOPATH. (option 1 is preferred)

upgrading

go get -d github.com/pkg/errors@v0.7.1
go get -d github.com/pkg/errors@17b591d # records v.0.7.1
go get -d github.com/pkg/errors@master  # records v0.8.1-0.20180311214515-816c9085562c
go get -d github.com/pkg/errors@latest  # v0.8.0

downgrading

go get -d github.com/pkg/errors@v0.6.0


replacements and exclusions only apply when building module directly; they are ignored when the module is incorporated into a larger build.

excluding version

go mod edit -exclude github.com/pkg/errors@v0.7.0

# entry added to go.mod
# exclude github.com/pkg/errors v0.7.0

remove exclusion

go mod edit -dropexclude github.com/pkg/errors@v0.7.0

replace

# -replace=old[@v]=new[@v] 
go mod edit -replace github.com/pkg/errors@v0.6.0=github.com/pkg/errors@v0.7.0

# entry added to go.mod
# replace github.com/pkg/errors v0.6.0 => github.com/pkg/errors v0.7.0

dropping replacement

# -dropreplace=old[@v] 
go mod edit -dropreplace github.com/pkg/errors@v0.6.0


updating and patching

with no package arguments, ‘go get’ applies to the main module

minor or patch:

# update all dependencies to newer minor or patch releases
go get -u

# update single dependency to newest minor or patch release
go get -u github.com/pkg/errors # v0.6.0 -> v0.8.0

patch only:

# update all dependencies to newer patch
go get -d -u=patch

# update single dependency to newest patch
go get -d -u=patch github.com/pkg/errors # records v0.7.0 -> v0.7.1

Advanced commands

There are a few of low-level commands available, but we’ll explore those later on:

go mod graph
# prints module requirements

go mod tidy
# keeps go.mod in sync with the module; adds modules required to build current module's packages and deps, removes unused modules. Also keeps go.sum updated

go mod verify
# checks deps of current module, which are stored in local downloaded cache, have not been modified since last download $GOPATH/pkg/mod/cache/download

go mod vendor 
# builds a vendor dir, includes all packages required to build and test all the current module's packages

go mod fix
# unlikely you need this because many go commands run this command under the hood

To use the vendor dir:

This operation is will recreate the vendor dir. If you’re using another package manager that already has vendor, everything will be wiped and rebuilt by this command.

go build -mod=vendor

# note the -mod flag also applies to go clean, get, install, list, run, and test

Lastly, I have some ideas about using the following command, which provides information about modules and their dependencies. It can also return JSON with the -json flag which is useful for downstream tools.

go list all
# in module-mode = all packages in the main module and their dependencies. all
# in GOPATH-mode = ALL packages on local system <- CAREFUL, this may take a long time