Origin
This was initially planned as content for a talk on the local Gophers meetup in Frankfurt. Unfortunately I had to cancel my attendance and Arne (another organizer) stepped in as the presenter.
The content of the slides are provided here
Go modules - an introduction
Before go modules
What is a module
- Code organization
A module is defined with the package name in the module definition.
- Repository (!git repository)
Multiple modules may be hosted in a repository. Comparable to npm registry in terms of hosting the package versions but not being the place where work on code is conducted
- Dependencies
A module defines versioned dependencies
Old way
-
Packages tied to git repositories (maybe multiple packages in subdirectories within one git repository)
-
Shared dependencies (within GOPATH) or vendored dependencies
-
Updating a package in the GOPATH could break another package
-
Not re-downloading a dependency when getting a new package could lead to a broken package
New way
-
Multiple modules in one repository
-
Multiple packages in one module
-
Explicitly versioned modules
-
‘Pinned’ dependencies
go modules
are Gos take on a dependency manager, so it’s your choice:
- Like
- No like
Remember: they are still an experiment - but they are the future!
Usage
go mod init livingit.de/code/great-module
Remember: no need to be on the GOPATH - actually you should not be
module livingit.de/code/great-module
go 1.12
The above code is the generated module definition stored within go.mod. It is versioned just in case later go tools will need need to handle different behavior
go get
is fully modules aware. So move on and install a module by hand:
go get github.com/rs/xid
Go will track that package:
module livingit.de/code/great-module
go 1.12
require github.com/rs/xid v1.2.1 // indirect
The comment indirect means it is not used by your code
add some code
package main
import "github.com/rs/xid"
import "fmt"
func main() {
guid := xid.New()
fmt.Println(guid)
}
The package is now directly used, so the go.mod file looks like this:
module livingit.de/code/great-module
go 1.12
require github.com/rs/xid v1.2.1
Upgrade / downgrade
Is as simple as go-getting with a version:
go get -u github.com/rs/xid@v1.2.0
Which is a downgrade to v1.2.0 and changes the go.mod to
module livingit.de/code/great-module
go 1.12
require github.com/rs/xid v1.2.0
require github.com/rs/xid v1.2.1
To get the latest patch versions of your dependencies (in case of security releases, etc):
go get -u=patch
I like everything to be tidy
So have your go.mod tidied up:
go mod tidy
Module repositories
Module repositories (or proxies) are a way to cache modules and be more enterprise-ish. There are several implementations (open source and closed source).
https://gocenter.jfrog.com is one you can use provided by JFrog.
As modules are experimental, registries may have some hard edges.
Advanced topics to Go
What makes up a require line
require github.com/rs/xid v1.2.1
Seems obvious, but a single letter means a lot: ‘v’
If you write a module, the leading v for the semantic version is required.
A refresher for semantic versioning:
Given a version number MAJOR.MINOR.PATCH, increment the:
1. MAJOR version when you make incompatible API changes,
2. MINOR version when you add functionality in a backwards-compatible manner, and
3. PATCH version when you make backwards-compatible bug fixes.
Another go module specific behavior:
For major versions 2 or higher, include vN at the end of the package. For v0 or v1 omit
go.sum
is not a lock file. For npm or yarn there is a lock file to pin version numbers and have reproducible builds.
github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
go.sum contains the expected cryptographic checksums of the content of specific module versions. It is used to prevent unexpected changes when you get dependencies later.
Therefore: you want to check in go.sum along with your go.mod file
replacing dependencies
Replace allows to swap one module with another one. Main reasons to do this:
-
replace with a relative path on your machine to be able to work disconnected
-
test new versions developed in a fork
But: keep in mind that portability is reduced and it is not obvious
module example.com/hello
require github.com/gopherjs/gopherjs/v11 latest
replace (
// the work to move to v11 was done in a fork
github.com/gopherjs/gopherjs => github.com/myitcv/gopherjs introduce_v11
)
Another reason is to switch to forks when the original project has vanished
minimal version selection (1)
Minimal version selection always selects the minimal (oldest) module version that
satisfies the overall requirements of a build. If that version is buggy in some way,
an upgrade or downgrade operation can modify the top-level target's requirements
list to force selection of a different version.
minimal version selection (2)
From Russ Cox
Are all packages available as modules?
Not yet. Bets are accepted if we will ever have all packages as modules. We have to distinguish two cases
- valid semantic versioning exists The semantic version will be added to go.mod
- no valid semantic version exists
A pseudo version will be created:
v0.0.0-20171006230638-a6e239ea1c69
This contains a timestamp as well as the commit hash to be able to link to the correct version
Migration (1)
It should be considered:
-
If you already have a v2 your work is considerable higher due to implications of semantic import versioning
-
The module system is not a all-in now
-
Older go version support is something you need to think about
A good advice is: for new projects you should start with modules. You should add module support as early as you can.
Migration (2)
go mod init in an existing project can import from dep, glide, and some others.
No go code will be touched, so the /vN must be added manually (for your module and possibly for dependencies and go Code)
Instructions using go get -u should be updated - go get -u download new minor versions and thus may lead to unexpected behavior
In general go get calls are usually not required - go build, go test, ect will do this for you