Go modules: the dependency management system for the Go ecosystem.
What it optimizes for: automatic and stable version selection over time. This means that if your code builds now with a set of recorded dependencies, it will continue to build in the future no matter what your dependencies publish (or unpublish). To do this, it relies on the ecosystem following semantic versioning and on shared infrastructure such as a caching proxy and transparency log. Recommended reading: semantic import versioning minimal version selection reproducible, verifiable, verified builds
go mod init example.com/some/module
Modules are units of versioning, and in most cases you will want a single module at the root of your repository.
Module names are /
separated ascii, the first segment must contain a dot
(names without a dot are reserved for the stdlib with the exception of example
and test
).
If you host your code remotely, it should match the code host;
if it's only available locally, feel free to use one of the
reserved tlds
like some-name.local
.
Versions 2+ need a /vN
suffix.
go get some-package@version
or after writing import "..."
in youf code, go mod tidy
The first gives you more control, the second will also trim out unused dependencies.
@version
, one of:
vX.Y.Z
: a version number@abcdef
: a commit sha@master
: a branch name@latest
: one of the special version queries, latest means highest tagged versionnote: indirect references like @latest
and @branch
are cached by proxies. use GOPROXY=direct
to skip the proxy.
update all dependencies:
go get -u ./...
update all dependencies, but only to patch versions:
go get -u=patch ./...`
update only direct dependencies:
go get $(go list -f '{{if not (or .Main .Indirect)}}{{.Path}}{{end}}' -m ./...)
Things you actually use:
go list -deps -f '{{ if not .Standard }}{{ .Module }}{{ end }}' ./path/to/package | sort | uniq
# or
go version -m $executable
Why a module is somewhere in the dependency graph
go mod why some.module/dependency
# or
go mod graph
go mod vendor
and edit the files in vendor/
.
The next run of go mod vendor
will wipe away those changes.
If you need more control or expect to contribute a fix upstream,
clone the repo somewhere and use replace:
go mod edit -replace dependency.module/path=../path/to/cloned/repo
.
Is this a temporary or permanent fork?
Temporary: go mod edit -replace dependency.module/path=forked.module/path@version
Permanent: rename all instances of the import path in the forked module and treat it like any other dependency.
You want to write private code, also as other blog post.
If you use one of the more fully featured code hosting software that responds
with <meta go-import="...">
or you have a vanity domain setup:
go mod init gitlab.corp.example/path/to/repo
go mod init github.com/your/repo
go mod init vanity.example/repo
Otherwise (eg with plain git), use .git
as part of your module path
(also import paths):
go mod init git.host.example/your/repo.git
You'll also want to have git use ssh instead of https:
git config --global url."git@github.com:".insteadOf https://github.com/
git config --global url."you@git.host.example".insteadOf https://git.host.example
or if you prefer editing .gitconfig by hand:
[url "git@github.com:"]
insteadOf = https://github.com/
[url "you@git.host.example:"]
insteadOf = https://git.host.example/
Go will use a proxy by default. If you don't have a private one setup, exclude module path prefixes from lookup from proxies:
GOPRIVATE=github.com/you,git.host.example
# persist with
go env -w GOPRIVATE=github.com/you,git.host.example
You never want to share or host your code anywhere.
go mod init example.local/app1
if you still want to use multiple modules:
code
├── app1
│ ├───main.go
│ └── go.mod
│ # module example.local/app1
│ #
│ # go 1.16
│ #
│ # require example.local/some-lib v0.0.0
│ #
│ # replace example.local/some-lib => ../some-lib
│
└── some-lib
├───lib.go
└── go.mod
# module example.local/app1
#
# go 1.16
This is complicated How to optimize this depends on 2 things: are your worker nodes stateful (can they retain a cache/volume between builds) and what do you use to build containers (docker, docker buildx, kaniko, ...).
Your workers have persistent volumes you can use between builds.
This shares a module download cache and a build cache between all docker builds. Docker currently doesn't allow control over the cache location.
#syntax=docker/dockerfile:1.2
FROM golang:alpine AS build
WORKDIR /workspace
COPY . .
RUN --mount=type=cache,id=gomod,target=/go/pkg/mod \
--mount=type=cache,id=gobuild,target=/root/.cache/go-build \
go build -o app
FROM scratch
COPY --from=build /workspace/app /app
ENTRYPOINT ["/app"]
This takes advantage of kaniko skipping /var/run
,
so we can put our mutable cache there and share it between runs.
docker run --rm -it \
-v $(pwd):/workspace \
-v /path/to/mod/cache:/var/run/go-mod \
-v /path/to/build/cache:/var/run/go-build \
-w /workspace \
gcr.io/kaniko-project/executor:latest \
-c=. \
-f=Dockerfile \
-d=your.docker/registry/image
with dockerfile:
FROM golang:alpine AS build
ENV GOCACHE=/var/run/go-build \
GOMODCACHE=/var/run/go-mod
WORKDIR /workspace
COPY . .
RUN go build -o app
FROM scratch
COPY --from=build /workspace/app /app
ENTRYPOINT ["/app"]
This is complicated, you have to trade off between downloading and restoring a cache, and just doing the work.
if you just want the download step to be cacheable as a layer:
FROM golang:alpine AS build
WORKDIR /workspace
COPY go.mod go.sum .
RUN go mod download
COPY . .
RUN go build -o app
and run with the below if you're using multistage builds
docker buildx \
--cache-from type=registry,ref=your.registry/image \
--cache-to type=registry,ref=your.registry/image,mode=max \
If you also want to share the build cache as a layer, the best way might be to build a base image with your code once, and update the base image every time you update dependencies.
FROM golang:alpine AS base-image
WORKDIR /workspace
COPY . .
RUN go build ./... && \
rm -rf *
The better places to ask questions:
#modules
on gophers slack,
invite link
Mailing list: go-nuts
summary / comments
GOPATH/src/*/
interdependencies?go mod why
is anemic@ref
, losing clone into GOPATH/src