go mod更新指定的tag的包后,go vendor内容未更新问题
作者:love666666shen
背景
golang项目使用module进行依赖及版本管理,私有项目或二次开发的项目通过vendor进行管理。
在一次修改代码,打完tag,修改项目go.mod中依赖私有仓库的tag=v6.0.12后,使用 go mod tidy
更新依赖,go.sum中的对应仓库的tag对应为v6.0.12;然后通过 go mod vendor
更新vendor下面的modules.txt及私有仓库,发现modulest.txt中记录的依赖仓库的tag=v6.0.12已更新,但是仓库的内容却不是最新的。
分析
- 可能本地mod有缓存
- 之前已经存在该tag,被删除后,新建了相同的tag,导致该tag的hash相同,命中缓存,进行
go mod tidy
后使用本地缓存中之前的tag的代码,未进行更新。
解决
删除本地配置的$GOPATH环境变量中pkg/mod及pkg/mod/cache/download路径下对应的依赖仓库及其tag版本,重新使用下面的命令更新。
$ go mod tidy go: downloading pkg.xx.com/service/lib/db/v6 v6.0.13 $ go mod vendor
如果通过上面清除缓存的方式,vendor依赖仓库的内容还没有更新到最新的提交,可以在最新提交分支上重新创建一个新的tag,修改go.mod中依赖的tag为新创建的tag,再使用上面的 go mod tidy
和 go mod vendor
分别更新go mod和vendor依赖。
扩展
在go1.11之前的版本中,golang 主要依靠vendor和GOPATH来管理依赖库,vendor相对主流,但现在官方更提倡go mod。
go get
go get之后下载文件的目录位置:
1、GO111MODULE 如果为off,则在pkg目录下;
2、GO111MODULE如果为on,则在src目录下。
GOPATH
GOPATH模式下不方便使用同一个依赖包的多个版本。在GOMODULE模式下这个问题得到了很好的解决。
GOPATH模式下,依赖包存储在 $GOPATH/src
,该目录下只保存特定依赖包的一个版本。而在GOMODULE模式下,依赖包存储在 $GOPATH/pkg/mod
,该目录中可以存储特定依赖包的多个版本。
需要注意的是$GOPATH/pkg/mod目录下有个cache目录,它用来存储依赖包的缓存,简单说,go命令每次下载新的依赖包都会在该cache目录中保存一份。关于该目录的工作机制我们留到GOPROXY章节时再详细介绍。
接下来,我们使用开源项目github.com/google/uuid为例分别说明GOPATH模式和GOMODULE模式下特定依赖包存储机制。在下面的操作中,我们会使用GO111MODULE环境变量控制具体的模式:
- export GO111MODULE=off切换到GOPATH模式
- export GO111MODULE=on切换到GOMODULE模式。
go module
自从go1.11版本后,golang引入go mod 机制来管理项目的依赖库及其版本,其中 go.mod 简要记录了项目直接依赖库的版本信息,
- go.sum详细记录了(项目直接或间接引用到的)各个依赖库的版本及其对应hash。
- go mod用于解决之前没有地方记录依赖包具体版本的问题,方便依赖包的管理。
存放位置
一般第三方依赖库(包括公司内网gitlab上的依赖库),其源码都不被包含在项目内部,而是在编译的时候通过go连接公网或内网下载到本地配置的环境变量$GOPATH中,然后编译成对应的二进制文件,移植到各种系统中使用。
go module存储下载的依赖包,具体位置在$GOPATH/pkg/mod。
GOPATH 在不同平台上的安装路径不同,具体可以通过 go env
查看环境变量配置。
问题
有时候需在无公网、无内网(无法连接内网gitlab)的情况下编译go项目,比如断网的情况,那么需要如何做呢?
面对这种场景,可以通过 go get
或者 go mod vendor
将项目的依赖库下载到项目内部,作为项目的一部分来编译。
除此之外,还有一些使用 go mod vendor
的场景或者优势:
- 虽然通常不会也不需要在无公网、无内网环境实时编译,因为go的可移植性很好,常以可执行文件方式交付部署,但并不能排除此种可能。
- 防止依赖库因为某种原因被删除、移动,导致找不到依赖并编译失败。
- 对新手来说,下载一些墙外的依赖可能略有困难。
- 其他…总之,我们的目的是使用 go mod vendor,将项目的依赖库下载到项目内部,即项目中包含依赖库源码,依赖库如同项目的一部分,也受到项目的版本管控(git、svn…)。
go vendor
通过 go mod tidy
下载并更新依赖仓库的版本后,再执行 go mod vendor
可以将下载的依赖包存放在vendor命令下并更新vendor/modules.txt文件,而vendor/modules.txt文件则自动记录对应的依赖及其tag版本,不需要手动修改。
使用vendor的方式,相对于GOPATH或GOMODULE的方式,即使对应tag的依赖包被删除或丢失,只要vendor目录中存在就可以直接编译使用。
# 在vendor目录下创建依赖包副本 # 用vendor/modules.txt记录依赖包及版本 go mod vendor # 指定构建方式,Go 1.14之后,默认也是vendor模式构建 go build -mod=vendor
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。