Go Modules is still a relatively new feature and some of the documentation surrounding it is detailed and technical, but not easily digestible as a quick start.
One issue I ran into is dealing with packages that have a version of 2.x.x or higher. It’s not immediately obvious but Go modules handles packages that have a major version that is not v0.x.x or v1.x.x in a special way.
I won’t go into why this is so. The bottom line is that if you publish a package with a version of v2.0.0+ or publish a new major version there are some extra steps in your package and the packages that depend on it.
In this example I will be publishing v7.0.0 of github.com/kounta/luigi (one of our private repositories, but you can substitute the name with your own). It already uses go modules, but it has never had its major version taken into special consideration. This causes packages that depend on it to force a specific version in their go.mod:
We would rather have the tagged version, like:
The odd thing is that if I change the version manually (in go.mod), Go will just revert it back to the specific hash and checksum. What's going on!?
Before we start there is an important consideration here. If you want to support multiple major versions or not. In my case we only want to support the current major version since it is an internal library. Any projects that depend on it will eventually need to update their dependencies which will trigger them to update luigi to the latest version.
However, if you need to support multiple major versions at the same time I will explain this separately later in Maintaining Multiple Major Versions.
Updating the Package
We already have a go.mod file. We have to modify the first line of go.mod to include the major version (notice we do not care about the minor or patch):
This may seem obscure at right now. The next step will show why this is needed.
Run go build (or go test if you have tests) and you will receive errors like:
./log_entry_test.go:14:14: undefined: luigi.NewLogEntry./log_http_test.go:15:78: undefined: luigi.Environment
This is because we have changed the name of the current package. Technically Go is giving these errors because there is no longer a email@example.com or firstname.lastname@example.org available.
We will need to update our imports to the new package name (including test files):
My first through was, "Oh, no! Will I have to reference v7.x instead of luigi.x everywhere?" Actually, no. Only the import name changes, and Go understands we are talking about the version on the end. Phew.
It could be painful if you have many imports to update. A global search and replace might be necessary.
Once go build/test passes you should run go mod tidy, not only because it’s a good idea but also because it will remove any references to the old version of this package that might have been added from a go build/test while refactoring.
Now commit all changes including the go.mod and go.sum.
Updating The Packages That Depend On Luigi
For any packages that rely on luigi, we have to update them to point to v7 explicitly.
There are two ways to do this:
- Simply run (notice the version is now in the package name):go get email@example.com
- Or, edit the go.mod manually and change the version and package name, from:
When you run go build/test you will get the same kind of error as before. It requires the same fix. That is, changing the import names to include the version number on the end.
Finally, run go mod tidy for good measure. You are done!
Maintaining Multiple Major Versions
If you need to support multiple major versions at the same time you will need to use multiple go.mod files.
Let’s say I had to manage both v6 and v7 (the latest) of luigi. Before embarking on the changes above you should copy the go.mod and go.sum into a folder called v6:
mkdir v6cp go.mod go.sum v6
Any consumers that wish to remain on v6 can with the import:
Go will automatically resolve this to the versioned folder in the repository. Easy. It's also a nice way to indicate how many major versions back your package officially supports.