项目逐渐都切到了 go mod,用的时候遇到了各种奇奇怪怪的坑,记录一下。
流程实践
语义化版本
Go mod 在设计时没有支持诸如 >2.0
, ~3.4
, ^4.0
这样场景的语法。而是以语义化版本的约定来处理:
x.y.z
≈ ^x.y
+ <=y.z
实际中,对于内部频繁升级的 common 包,每次改动都需要 y+=1
才能保证兼容性。为此我专门搞了个小玩意来升级 y,可以通过 hook 在 push 前先 gt ft
。
有个比较恶心的问题,common 包的版本号长期维持在 1.y.0
且 y 很大。暂时没用解决的办法,后面再看看 x
能有什么新玩法。
不同分支依赖冲突
如图,一个常见的场景,多人开发项目时,有可能在开发过程中使用了不同的依赖包,在合并代码的时候也会冲突。
以前的办法是有个专门的 vendor 分支,把有关依赖的变动串行化。
但是用了 gomod 后,如果你只在 vendor 分支 里加了依赖没有用,go mod tidy
时又会把依赖给去掉。
好在大部分时候都是需要更新而不是新增依赖,这个思路依然可用。我们给项目里专门准备了一个脚本来升级依赖,这个脚本做的事情也比较简单,切换到 vendor 分支,更新,提交。再合回 feature 分支。
异常问题处理
go replace 替换大小写时不支持同时依赖大小写
P->A P->xx->a
天真以为把 A replace a 就好了。还是会报同时用了大小写的错。
只有当所有依赖都依赖 A,但是 github 已经改成 a 时,才能 replace。
问题最初是由于 github.com/sirupsen/logrus 更换大小写引起,如果项目全用 Sirupsen 是可以 replace 的,后来有了混用就不行了。终极解决办法是干掉 logrus 。。
凡是随意修改包路径、函数签名的 Go Libs,基本上都被我列入黑名单了。
build xx: ambiguous import: xx in multiple modules:
通常是在没有使用语义化版本时,对一个包有不同版本的依赖导致。例如:
build xxx.com/com/projA: cannot load xxx.com/com/projB/model: ambiguous import: found xxx.com/com/projB/model in multiple modules:
xxx.com/com/projB v0.0.0-20181119101949-92ae1f75b49a (.../pkg/mod/xxx.com/com/projB@v0.0.0-20181119101949-92ae1f75b49a/model)
xxx.com/com/projB/model v0.0.0-20190311082816-bfb94e79a84f (.../pkg/mod/xxx.com/com/projB/model@v0.0.0-20190311082816-bfb94e79a84f)
可以通过下面方式来找
fd go.mod | xargs grep -r projB
vendor/xxx.com/com/base/go.mod: xxx.com/com/projB/registry v0.0.0-20190312132550-a101ae8810b6 // indirect
vendor/xxx.com/com/artemis/go.mod: xxx.com/com/projB/app/goprojB v0.0.0-20190312132550-a101ae8810b6
vendor/xxx.com/com/projB/registry/go.mod:module xxx.com/com/projB/registry
go replace 不能替换依赖包的依赖
我的项目P -> A&B,且 B->A
如果把 A replace 成 A‘,只能修改 P->A 的依赖, B->A 是改不了的。
所以 replace 不能解决梯子的问题,要用 goproxy
go get: error loading module requirements
go clean -modcache
清理 modcache 几乎是各种问题的终极大杀器,但是成本也很高,需要你下次重拉所有 mod。如果公司内有 proxy 做 cache 的话会好一些。