执行 go vendor 时第三方包 go 版本冲突问题的解决方法

问题症状
我们使用 jenkins 脚本执行 go build ,用来构建线上服务器使用的二进制文件。构建过程中有这样一个步骤:
go mod vendor
该步骤将以 go.mod 文件中写明的包和版本为准下载第三方依赖并保存到本地的 vendor 目录。下载过程中将校验 go.sum 中的 hash 值是否同文件 hash 一致。
在实际执行中,遇到这样的错误:
internal error: failed to find embedded files of github.com/marten-seemann/qtls-go1-18: //go:build comment without // +build comment
排查经过

  1. 通过 qtls-go1-18 的仓库名可以观察到问题可能跟 go 1.18 的版本有关。
  2. 打开依赖的 github 仓库可见简介:
    Go standard library TLS 1.3 implementation, modified for QUIC. For Go 1.18.
    而我们构建的环境 go env 输出的版本为 1.16
  3. 在 go 1.18 的 release notes 中查找相关信息:
//go:build lines
Go 1.17 introduced //go:build lines as a more readable way to write build constraints, instead of // +build lines. As of Go 1.17, gofmt adds //go:build lines to match existing +build lines and keeps them in sync, while go vet diagnoses when they are out of sync.
Since the release of Go 1.18 marks the end of support for Go 1.16, all supported versions of Go now understand //go:build lines. In Go 1.18, go fix now removes the now-obsolete // +build lines in modules declaring go 1.18 or later in their go.mod files.
For more information, see https://go.dev/design/draft-g...
报错的意思是 //go:build (1.18 版本支持) 必须同 // +build 一起出现。至此确认问题原因。
解决办法
业务代码并没有直接用到 qtls 包,且并没有直接依赖 qtls-go1-18 对应的 go 版本。此库为非直接依赖引入的,需要找出是那个包引入了这个依赖。
go mod why github.com/marten-seemann/qtls-go1-18
可以查看是谁引入该依赖。从输出可以看到:
# github.com/marten-seemann/qtls-go1-18 git.mycompany.com/group/projecta git.mycompany.com/group/projectb github.com/smallnest/rpcx/client github.com/lucas-clemente/quic-go github.com/marten-seemann/qtls-go1-18

通过 go mod graph 可以看到具体那个包的那个版本引入的
最终确认是 quic-go 的 0.27 引入的。
【执行 go vendor 时第三方包 go 版本冲突问题的解决方法】在 go.mod 中排除掉 quic-go 0.27 即可。在 go.mod 中加一行。
exclude lucas-clemente/quic-go v0.27.0

总结和其他
  1. 为什么 go mod vendor 会更新版本,理论上只会使用 go.mod 中制定的版本;
  2. build 机器不需要 go mod vendor ,直接 go mod download 即可;
  3. go mod vendor 同 go mod download 在依赖管理上有什么不同?

    推荐阅读