Go Module Proxy
Internals and Pitfalls
Andreas Linz—klingt.net
2019-11-14T19+01:00
$GOPATH/src
- no version management everything was “latest”
GO15VENDOREXPERIMENT=on
- In mid of 2015 Go 1.5 introduced experimental
/vendor
support
/vendor
supplies manual managed dependencies
- dependencies need to be part of the repository
- (nobody likes to work with git submodules,
git clone --recursive ...
)
When asked about the biggest challenges to their own personal use of Go, users clearly conveyed that lack of dependency management…
[One of] The top three major challenges we identified are:
Package management (e.g., “Keeping up with vendoring”, “dependency / packet [sic] management / vendoring not unified”) …
- many Go developers were unhappy with package management
- no official solution, only more or less stable third-party package management tools
- you still needed to work inside the
$GOPATH
(or use “clever” hacks)
Dave Cheney said in 2016:
In my experience, many newcomers to Go are frustrated with the single workspace $GOPATH model.
With the end of 2016 a default GOPATH
is set (~/go
).
What’s a Module?
- module: collection of related Go packages that are versioned together as single unit
- e.g. a simple webserver using
net/http
from Go 1.1x with gorilla/mux
as router is a collection of packages
- modules add precise dependency requirements (
go.mod
) → reproducible builds
- support will be finalized with Go 1.14
Modules documentation
Terminology
- a repository contains one or more modules
- a module contains one or more packages
- package is a single directory of
.go
files
How to Setup a Modules Project?
$ go mod init github.com/example/project
- migrates existing dependency definitions if present (e.g.
Gopkg.toml
from dep
)
- creates
go.mod
and go.sum
files
Commit both but never edit them manually!
(go get
and friends will do this for you)
Add Dependency in Specific Version
$ go get github.com/example/project@v1.2.3
- the version can be a git reference as well (hash/branch…)
🕵️
- potential information leak
- set
GOPRIVATE=my.secret.git.com[,…]
for private repositories
Advantages of Using a Proxy
- faster builds (less stress on the git server)
- persistent depencies (if upstream is down)
- reproducible builds (no
git push -f
)
Write ony Yourself?
- learn about modules
- get to know the module proxy API
- simpler access to private repositories
Private Repositories
go get
knows nothing about authentication
- common solution HTTPS to SSH rewrite:
$ git config --global url."git@git.company.com:".insteadOf https://git.company.com/
Module Proxy API
go help goproxy
- only
GET
resources
$GOPROXY/<module>/@v/list
- list of known versions
- line by line
$ curl 'https://proxy.golang.org/github.com/gorilla/mux/@v/list'
v1.3.0
v1.7.0
v1.6.0
…
$GOPROXY/<module>/@v/<version>.info
- returns version details and date of creation
$ curl 'https://proxy.golang.org/github.com/gorilla/mux/@v/v1.7.0.info'
{"Version":"v1.7.0","Time":"2019-01-25T16:05:53Z"}
$GOPROXY/<module>/@latest
- not documented in
go help goproxy
- returns details of the latest available version
$ curl 'https://proxy.golang.org/github.com/gorilla/mux/@latest'
{"Version":"v1.7.3","Time":"2019-06-30T04:17:52Z"}
$GOPROXY/<module>/@v/<version>.mod
$ curl -s 'https://proxy.golang.org/github.com/gorilla/mux/@v/v1.7.0.mod' | head
module github.com/gorilla/mux
$GOPROXY/<module>/@v/<version>.zip
- zip archive of the module at
<version>
$ curl -s 'https://proxy.golang.org/github.com/gorilla/mux/@v/v1.7.0.zip' | bsdtar --list -zf- | head -n10
github.com/gorilla/mux@v1.7.0/.github/release-drafter.yml
github.com/gorilla/mux@v1.7.0/.github/stale.yml
github.com/gorilla/mux@v1.7.0/.travis.yml
github.com/gorilla/mux@v1.7.0/AUTHORS
github.com/gorilla/mux@v1.7.0/ISSUE_TEMPLATE.md
github.com/gorilla/mux@v1.7.0/LICENSE
github.com/gorilla/mux@v1.7.0/README.md
github.com/gorilla/mux@v1.7.0/bench_test.go
github.com/gorilla/mux@v1.7.0/context.go
github.com/gorilla/mux@v1.7.0/context_test.go
…
The Zip Archive
is pretty special
root folder must be:
<import-path>@<version>/
it contains no metadata:
- modification timestamps are DOS zero time (1979-11-30)
- everything has
0644
permissions
You Should not Zip them Yourself
- very hard to reproduce
- only achieved archives with equal byte length (compared to proxy.golang.org)
- checksums were still different (official zips used weird file metadata)
So…
Should I Write one Myself?
- For learning, yes!
- For production, maybe?
- documentation is still unfinished and all over the place
- if a project is not using semantic versioning the API falls back to pseudo-versions:
Pseudo-Version
- Example:
v0.0.0-20191025081138-a37363377ac6
- uses a custom date format
20060102150405
😐
Summary
A module proxy gives you:
- faster builds
- reproducible builds
- persistent dependencies
Additionally, the checksum database eliminiates MITM modification of modules.