Go Wiki:解决修改的模块路径引起的问题
意外的模块路径
在项目 my-go-project
上工作的用户在 go get -u
期间可能会遇到如下错误
$ cd my-go-project
$ go get -u ./...
[...]
go: github.com/golang/[email protected]: parsing go.mod: unexpected module path "golang.org/x/lint"
[...]
Exit code 1
golang.org/x/lint
是一个模块,在迁移到 git 仓库 golang.org/x/lint
并将其模块名称重命名为 golang.org/x/lint
之前,其 git 仓库和模块名称曾为 github.com/golang/lint
。Go 工具目前在尝试理解新 git 仓库中的旧模块名称时遇到困难:golang/go#30831。
此问题出现在 my-go-project
中,因为 my-go-project
或其传递依赖项在模块图中有一条路径通向旧的 github.com/golang/lint
模块名称。
例如,如果 my-go-project
本身依赖于旧的 github.com/golang/lint
模块名称
$ GO111MODULE=on go mod graph
my-go-project github.com/golang/[email protected]
或者,my-go-project
可能依赖于旧版本的 google.golang.org/grpc
,而旧版本的 google.golang.org/grpc
依赖于旧的 github.com/golang/lint
模块名称
$ GO111MODULE=on go mod graph
my-go-project google.golang.org/[email protected]
google.golang.org/[email protected] github.com/golang/[email protected]
最后,my-go-project
可能依赖于另一个依赖项,而该依赖项需要旧版本的 google.golang.org/grpc
,而旧版本的 google.golang.org/grpc
又依赖于旧的 github.com/golang/lint
模块名称
$ GO111MODULE=on go mod graph
my-go-project some/[email protected]
...
another/[email protected] google.golang.org/[email protected]
google.golang.org/[email protected] github.com/golang/[email protected]
删除对名称的引用
在 Go 工具更新为能够理解已更改其模块路径的模块(在 golang/go#30831 中跟踪)之前,解决此问题的办法是更新图形,以便不再有路径指向旧模块名称。
使用上述示例,我们将探讨如何更新图形,以便不再有路径指向 `github.com/golang/lint`。
修复第一个示例很简单,唯一的链接来自 `my-go-project` - 由用户控制!使用 `go.mod` 中的新位置替换旧位置 - 将 `github.com/golang/[email protected]` 替换为 `golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f` - 从图形中删除链接
$ GO111MODULE=on go mod graph
my-go-project golang.org/x/[email protected]
修复第二个示例涉及更多步骤,但本质上是相同的过程:`google.golang.org/[email protected]` 提供了指向 `github.com/golang/lint` 的链接,因此 `google.golang.org/grpc` 应将其 `go.mod` 从 `github.com/golang/[email protected]` 更新为 `golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f`(幸运的是,这已在 `v1.17.0` 中发生)。然后,`my-go-project` 应更新其 `go.mod` 以包含 `google.golang.org/grpc` 的新版本,以便我们现在拥有
$ GO111MODULE=on go mod graph
my-go-project google.golang.org/[email protected]
google.golang.org/[email protected] golang.org/x/[email protected]
修复第三个示例与第二个示例类似:更新到 `another/dep` 的较新版本,其中引入了 `google.golang.org/grpc` 的较新版本,该版本不包含对 `github.com/golang/lint` 的引用。
万岁!问题已解决 - Go 工具不再考虑指向 `github.com/golang/lint` 的路径,因此在 `go get -u` 期间不会遇到此问题。
更难的问题:删除尾随历史记录
这一切都很好,应该能解决大多数用户的问题。
但是,有一种情况最终会变得更加复杂:当模块依赖关系图中存在循环时。考虑此模块依赖关系图
并且,让我们想象 `some/lib` 曾经依赖于 `github.com/golang/lint`。
让我们查看此模块依赖关系图,其中包含版本
$ go mod graph
my-go-lib some/[email protected]
some/[email protected] some-other/[email protected]
some/[email protected] golang.org/x/[email protected]
some-other/[email protected] some/[email protected]
some/[email protected] some-other/[email protected]
some/[email protected] golang.org/x/[email protected]
some-other/[email protected] some/[email protected]
some/[email protected] some-other/[email protected]
some/[email protected] golang.org/x/[email protected]
some-other/[email protected] some/[email protected]
some/[email protected] some-other/[email protected]
some/[email protected] github.com/golang/[email protected]
使用 golang.org/x/exp/cmd/modgraphviz 可视化
在这里,我们看到即使 `some/lib` 的最后几个版本正确依赖于 `golang.org/x/lint`,但由于 `some/lib` 和 `some-other/lib` 共享一个循环,因此很可能存在一条指向远古的路径。
出现此类路径的原因是,增加版本的过程通常是单独的原子操作:当 `some/lib` 增加其 `some-other/lib` 的版本并发布其自身的新版本时,`some-other/lib` 的最新版本仍然依赖于 `some/lib` 的先前版本。也就是说,这两个库中的任何一个单独的增加都不足以移除历史链。
要移除历史链并从图形中永久移除旧的 `github.com/golang/lint` 引用,这两个库必须同时增加它们彼此的版本。
原子性地增加两个库的版本
删除 github.com/golang/lint
的解决方案是首先确保 some/lib
不依赖于 github.com/golang/lint
,然后将 some/lib
和 some-other/lib
都提升到彼此不存在的未来版本。我们需要这种类型的图表
my-go-lib some/[email protected]
some/[email protected] some-other/[email protected]
some/[email protected] golang.org/x/[email protected]
some-other/[email protected] some/[email protected]
由于 some/lib
和 some-other/lib
在同一版本中相互依赖,因此没有时间倒退的路径,无法提供 github.com/golang/lint
。
以下是实现此原子版本提升的步骤,假设 some/lib
位于 v1.7.0
,而 some-other/lib
位于 v2.5.3
- 验证错误确实存在
- 在
some/lib
和some-other/lib
中运行GO111MODULE=on go get -u ./...
。 - 在两个存储库中,你都应该看到错误 go:
github.com/golang/[email protected]: parsing go.mod: unexpected module path "golang.org/x/lint"
。
- 在
- 验证最新版本的
some/lib
依赖于golang.org/x/lint
,而不是github.com/golang/lint
。如果删除历史痕迹,但保留对github.com/golang/lint
的损坏依赖项,那将是一种耻辱! - 使用 alpha 标记将两个库提升到彼此不存在的未来版本(这更安全,因为在评估模块的最新发布版本时,go 模块不会将 alpha 版本视为较新版本)
some/lib
将其some-other/lib
依赖项从v2.5.3
更改为v2.5.4-alpha
。some/lib
标记提交v1.7.1-alpha
并推送提交和标记。some-other/lib
将其some/lib
依赖项从v1.6.0
更改为v1.7.1-alpha
。some-other/lib
标记提交v2.5.4-alpha
并推送提交和标记。
- 在事物仍处于 alpha 状态时验证结果
- 在
some/lib
中,GO111MODULE=on go build ./...
和go test ./...
。 - 在
some-other/lib
中,GO111MODULE=on go build ./...
和go test ./...
。 - 在两个存储库中,
GO111MODULE=on go mod graph
并断言没有路径到github.com/golang/lint
。 - 注意:
go get -u
仍然不起作用,因为 - 如上所述 - 在评估最新版本时不会考虑 alpha 版本。
- 在
- 如果一切看起来都不错,请继续再次碰撞到彼此不存在的未来版本
some/lib
将其some-other/lib
依赖项从v2.5.4-alpha
更改为v2.5.4
some/lib
标记提交v1.7.1
并推送提交和标记。some-other/lib
将其some/lib
依赖项从v1.7.1-alpha
更改为v1.7.1
。some-other/lib
标记提交v2.5.4
并推送提交和标记。
- 验证错误不再存在
- 在
some/lib
和some-other/lib
中运行GO111MODULE=on go get -u ./...
。 - 不应出现解析
go.mod: unexpected module path "golang.org/x/lint"
错误。
- 在
- 当前,
some/lib
和some-other/lib
的go.sum
不完整。这是因为我们依赖于模块的未来不存在的版本,因此在进程完成之前我们无法生成 go.sum 条目。所以让我们解决这个问题GO111MODULE=on go mod tidy
在some/lib
中。- 提交,标记提交
v1.7.2
,并推送提交和标记。 GO111MODULE=on go mod tidy
在some-other/lib
中。- 提交,标记提交
v2.5.5
,并推送提交和标记。
- 最后,让我们确保
my-go-project
依赖于这些新版本的some/lib
和some-other/lib
,这些版本没有很长的历史记录- 将
my-go-project
go.mod
条目从some/lib v1.7.0
更改为some/lib 1.7.2
。 - 通过在
my-go-project
中运行GO111MODULE=on go get -u ./...
进行测试。
- 将
请注意,在步骤 5.b 和 5.d 之间,用户会中断:已发布了一个 some/lib
版本,该版本依赖于不存在的 some-other/lib
版本。因此,此过程最好实时完成,以便在步骤 5.b 之后尽快完成步骤 5.d,从而尽可能缩小中断窗口。
较大的循环
此示例解释了在图中涉及两个包的循环存在时删除历史记录的过程,但是如果涉及更多包的循环会怎样?例如,考虑以下图表
这些图形都涉及循环(后一个示例)或相互连接的模块(前一个示例),其中涉及四个模块,而不是我们之前看到的简单两个模块示例。不过,该过程在很大程度上是相同的,但这次在步骤 3 和 5 中,我们将把所有四个模块都提升到彼此的未来版本(不存在),类似地,在步骤 4 和 6 中,我们将测试所有四个模块,并在步骤 7 中修复所有四个模块的 go.sum。
更一般地说,上述过程适用于涉及任何 n 个模块的任何相互连接的模块组:每个主要步骤仅涉及 n 个模块协同作用。
此内容是 Go Wiki 的一部分。