如何调试 Go mod 的各种异常

Go语言精选

共 3347字,需浏览 7分钟

 ·

2020-11-07 07:08

Go mod 自从诞生之日就带来了太多太多的争议,当然不能否认它的设计初衷是好的。然而在调试其各种异常时,却浪费了太多开发者的时间。可以毫不客气的说,从来没有一种语言的版本管理,能让人如此崩溃

本文记录了一些我的踩坑经验,希望能给还在挣扎中的 Gopher 一些帮助。

go get

先来看看最近我遇到的一个问题:

$ go mod tidygo: foo.bar.com/foo/bar@v0.1.0: git remote add origin -- https://foo.bar.com/foo/bar.git in /root/goforge/pkg/mod/cache/vcs/e8ae9003fdd7f44246f94c535282e58436b3568a39734a76af8fac7b716a59b2: exit status 128:        fatal: remote origin already exists.

光从字面上来看,像是一个 git 的问题。一顿 go mod why 和 go mod graph 操作后,也没有得到什么有价值的信息。

以我的个人摸索经验来看,why 和 graph 的输出就是一坨垃圾,只会给开发者带来更多的心智负担。

实际上,调试 go mod 问题最好的工具是 go get ,这样可以只会输出异常模块的依赖树,去掉那些烦人的干扰信息。如果再加上 -x 选项后,更是屡试不爽。继续拿我遇到的这个问题开刀:

$ go get-x foo.bar.com/foo/bar# get https://foo.bar.com/foo/bar?go-get=1# get https://foo.bar.com/foo/bar?go-get=1: 200 OK (0.508s)mkdir -p /root/goforge/pkg/mod/cache/vcs # git3 https://foo.bar.com/foo/bar.git# lock /root/goforge/pkg/mod/cache/vcs/e8ae9003fdd7f44246f94c535282e58436b3568a39734a76af8fac7b716a59b2.lockmkdir -p /root/goforge/pkg/mod/cache/vcs/e8ae9003fdd7f44246f94c535282e58436b3568a39734a76af8fac7b716a59b2 # git3 https://foo.bar.com/foo/bar.gitcd /root/goforge/pkg/mod/cache/vcs/e8ae9003fdd7f44246f94c535282e58436b3568a39734a76af8fac7b716a59b2; git init --bare0.011s# cd /root/goforge/pkg/mod/cache/vcs/e8ae9003fdd7f44246f94c535282e58436b3568a39734a76af8fac7b716a59b2; git init --barecd /root/goforge/pkg/mod/cache/vcs/e8ae9003fdd7f44246f94c535282e58436b3568a39734a76af8fac7b716a59b2; git remote add origin -- https://foo.bar.com/foo/bar.git0.006s# cd /root/goforge/pkg/mod/cache/vcs/e8ae9003fdd7f44246f94c535282e58436b3568a39734a76af8fac7b716a59b2; git remote add origin -- https://foo.bar.com/foo/bar.gitgo: foo.bar.com/foo/bar@v0.1.0: git remote add origin -- https://foo.bar.com/foo/bar.git in /root/goforge/pkg/mod/cache/vcs/e8ae9003fdd7f44246f94c535282e58436b3568a39734a76af8fac7b716a59b2: exit status 128:        fatal: remote origin already exists.

瞧,go get -x 会帮你把每个步骤的操作都打出来。显然这个问题确实由 git 引起。大概是 go get 拉取模块时,会先创建一个裸仓库,然后 add origin 。问题就发生在 add origin 这步,git 认为已经有一个 origin 存在了。这是为啥呢?不妨去手动复现下:

$ mkdir git_test$ cd git_test$ git init$ git remote add origin -- https://foo.bar.com/foo/bar.gitfatal: remote origin already exists.

继续看看,现在这个仓库的 origin 是啥:

$ git remote -vorigin

看到这里,突然想起我的 git 配置了 origin 默认指向 HEAD

$ git config -l | grep originbranch.master.remote=originremote.origin.push=HEAD

git 2.20 以上已经在一个空的 origin 上继续 add 了

删除了这个选项后:git config --global --unset remote.origin.push, 终于可以拉取成功了。

replace

试想这么一种场景,假设有个项目 foo 依赖 a 的 v1.0.0 版本,而 foo 依赖的 b 依赖了 a 的 v2.0.0 版本。那么这个时候 foo 的 mod 其实最终会依赖 a 的 v2.0.0 版本。如果你需要 foo 强行依赖 v1.0.0 版本,这个时候就派上了 replace 的上场。直接修改 go.mod 文件,添加:replace a => a v1.0.0 即可。

但是实际情况,往往会更复杂。比如 b 需要 a v2.0.0 的一些新特性的话,简单的 replace 往往不能解决这个问题。如何解决呢?自己去处理。比如,Kuma[1] 自己维护了一个 vendored 文件夹 replace 到本地来处理这种问题;Kubernetes[2] 也有个自己的 staging

mod cache

需要注意的是 go 1.12 版本之前,mod cache 并不是并发安全的,同一个环境并发构建可能会产生竞态。而最新的 go 1.15 还提供了环境变量 GOMODCACHE 来指定 mod cache 的位置,CI 工具可以利用这项加快构建速度。

引用链接

[1] Kuma: https://github.com/kumahq/kuma/blob/master/vendored/README.md
[2] Kubernetes: https://github.com/kubernetes/kubernetes/blob/master/staging/README.md



推荐阅读


福利

我为大家整理了一份从入门到进阶的Go学习资料礼包,包含学习建议:入门看什么,进阶看什么。关注公众号 「polarisxu」,回复 ebook 获取;还可以回复「进群」,和数万 Gopher 交流学习。


浏览 58
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报