go mod 的智障版本选择

之前 go mod 用的比较少,而且一直听社区有各种抱怨,所以也兴趣寥寥。新公司的项目直接使用了 go mod,本来觉得无非是个简单的工具,不需要学习,结果在一个简单的依赖上却浪费了很多时间。

先来看看我碰到的例子:

package main
import "fmt"

import registry "github.com/apache/dubbo-go/registry"
import zk "github.com/apache/dubbo-go/registry/zookeeper"
import xds "mosn.io/mosn/pkg/xds"

func main() {
 var r registry.Registry
 fmt.Println(r)
 var z zk.Option
 fmt.Println(z)
 var x xds.Client
 fmt.Println(x)
}

导致冲突的原因,可以随便找个目录 go mod init 一下,然后 go build。然而编译不过去。实际上是因为 go mod 帮我选择了 github.com/envoyproxy/go-control-plane 这个库的错误版本,mosn 中直接依赖了这个库的 0.6.9 版本,但是在 go build 的时候被非常“好心”地升级到了 0.8.0,怎么回事?

dep_conflict

图比较明显,go mod why 现在显然是一坨垃圾。只能通过 go mod graph|grep 来找原因。

这里有个问题,我的程序其实并没有依赖 dubbo-go 里的 consul 这部分代码,我们用 go mod vendor 把程序的外部依赖保存下来,也可以得到验证:

/Users/xargin/test/gomod/vendor/github.com/apache/dubbo-go
~/t/g/v/g/a/dubbo-go git:master ❯❯❯ grep -iR consul ./                       
~/t/g/v/g/a/dubbo-go git:master ❯❯❯

所以这里 go mod 帮我们选择了一个连间接依赖都算不上的外部库指定的版本来进行更新。知道原因的话,解决方法也就简单了,本来对 github.com/envoyproxy/go-control-plane 有依赖的也就只有 mosn,只要 replace 就好:

replace github.com/envoyproxy/go-control-plane => github.com/envoyproxy/go-control-plane v0.6.9

这里的问题还算简单,如果碰上大项目,出了 go build 不出来的问题,还得用 go mod graph 一个一个去 grep,心累。

现在 go mod 提供的工具其实也并不方便。看看前几天的一篇文章:etcd go module 的灾难

用起来真是不省心。

有一些网友已经做了 go mod graph 的可视化,但是碰上大项目,基本出来的图不是给人看的:

graph
对于碰到依赖冲突的用户,其实主要是想看关键包的依赖路径。以及冲突发生的路径。

简单写了一个小工具来支持这个诉求:

https://github.com/cch123/gomod-conflict-detect

上面提到的例子会输出很多可能冲突的 pkg,找一下 go-control-plane:

Conflict in pkg github.com/envoyproxy/go-control-plane paths are:
 cch.com/c -> github.com/apache/dubbo-go@v1.3.0 -> github.com/hashicorp/consul@v1.5.3 -> github.com/envoyproxy/go-control-plane@v0.8.0
 cch.com/c -> mosn.io/mosn@v0.11.0 -> github.com/envoyproxy/go-control-plane@v0.6.9
Xargin

Xargin

If you don't keep moving, you'll quickly fall behind
Beijing