Gotchas with pointing Go modules to a fork, when building an installable module
This morning I cut a release of dependency-management-data which ended up horribly breaking all consumers of the application.
As I flagged in the tracking issue for this, trying to install the CLI would lead to the following error:
$ go install dmd.tanna.dev/cmd/dmd@latest go: dmd.tanna.dev/cmd/dmd@latest (in firstname.lastname@example.org): The go.mod file for the module providing named packages contains one or more replace directives. It must not contain directives that would cause it to be interpreted differently than if it were the main module.
This was because as part of this release, I added support for using charmbracelet/log as a
log/slog handler, which isn't yet fully merged upstream so I ended up pointing to my fork of the repo based on this PR.
As mentioned in Pointing to a fork of a Go module this should be trivial to do when you're using a
replace directive in your
go.mod, and was working in dependency-management-data's build pipeline, up until the point that someone tried to install from it.
I've never had this issue before - despite using
replace statements in some projects, and through looking at this Go issue it appears that this is only the case when you have an installable Go module, not when you're building it from the directory that contains the
For instance, I have created a sample project on GitLab.com, where we have two modules:
module gitlab.com/tanna.dev/jvt.me-examples/go-mod-fork-gotcha/with-replace go 1.21.1 require github.com/charmbracelet/log v0.2.4 // indirects omitted for brevity replace github.com/charmbracelet/log => github.com/jamietanna/log v0.2.2-0.20230912205513-bfd7186f150d
module gitlab.com/tanna.dev/jvt.me-examples/go-mod-fork-gotcha/without-replace go 1.21.1 require github.com/jamietanna/log v0.2.2-0.20230920083807-7382cd7fbdd8 // indirects omitted for brevity
When we try and
go install the module that uses
replaces, we encounter the same error:
% go install gitlab.com/tanna.dev/jvt.me-examples/go-mod-fork-gotcha/with-replace@HEAD go: downloading gitlab.com/tanna.dev/jvt.me-examples/go-mod-fork-gotcha v0.0.0-20230920133000-0be73c155bc2 go: downloading gitlab.com/tanna.dev/jvt.me-examples/go-mod-fork-gotcha/with-replace v0.0.0-20230920133000-0be73c155bc2 go: gitlab.com/tanna.dev/jvt.me-examples/go-mod-fork-gotcha/with-replace@HEAD (in email@example.com): The go.mod file for the module providing named packages contains one or more replace directives. It must not contain directives that would cause it to be interpreted differently than if it were the main module.
But when there's not a
replace, and we're pinning to a fork, this works as-is.
Also note that the two projects are pinning to a slightly different set of commits -
without-replace requires we pin to this specific commit, which modifies the module declaration:
-module github.com/charmbracelet/log +module github.com/jamietanna/log go 1.21
Without this, we would receive the error:
go: github.com/jamietanna/log@bfd7186f150d (v0.2.2-0.20230912205513-bfd7186f150d) requires firstname.lastname@example.org: parsing go.mod: module declares its path as: github.com/charmbracelet/log but was required as: github.com/jamietanna/log
And as we can't specify the
replace semantics, we must update the
module directive in our
go.mod, if we want the module to be installable without someone cloning the repository and running
go install from there.