Go modules - an introduction

go

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

Before Go Modules

What is a module

A module is defined with the package name in the module definition.

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

A module defines versioned dependencies

Old way

New way

go modules

are Gos take on a dependency manager, so it’s your choice:

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.

Go Center

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:

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)

Version Select

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

  1. valid semantic versioning exists The semantic version will be added to go.mod
  2. 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:

  1. If you already have a v2 your work is considerable higher due to implications of semantic import versioning

  2. The module system is not a all-in now

  3. 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