Go Modules - checksum mismatch 错误解决

Table of Contents

问题描述

本地执行了 go clean -modcache 后,执行 go mod download,出现错误:

verifying git.xxx.com/neirong/app-framework@v0.7.8/go.mod: checksum mismatch
	downloaded: h1:/8A+C1sjRPdK/06I7b2egOVjo8+ECKV3vJ3Cqz5vEzc=
	go.sum:     h1:7HfHuMOcinPkTDMNEf6Otcy4+TBvDQ/+f2UO0N23l3o=
SECURITY ERROR

一般遇到 checksum mismatch 错误,直接删除 go.sum 后,执行 go clean -modcache,再执行 go mod download 即可。我也确实这么做的,执行后,不再报错。当我把新的 go.sum 文件提交到 GitLab,跑 lint 时出现同样的错误:

verifying git.xxx.com/neirong/app-framework@v0.7.8/go.mod: checksum mismatch
	downloaded: h1:/8A+C1sjRPdK/06I7b2egOVjo8+ECKV3vJ3Cqz5vEzc=
	go.sum:     h1:7HfHuMOcinPkTDMNEf6Otcy4+TBvDQ/+f2UO0N23l3o=
SECURITY ERROR

app-framework@v0.7.8/go.mod 下载后计算得到的 hash(老的)与新生成 go.sum 里的 hash(新的)不一致。

解决过程

  1. 判断 GitLab 跑 lint 时是否有模块缓存,结论:GitLab 跑 lint 是新起的容器,没有缓存

  2. 因为 go env 的 proxy 设置了公司的 proxy 地址,且 app-framework@v0.7.8 是私有模块,考虑 proxy 代理中有该模块老的缓存。然而找其他同事,删除 go.sum,执行 go clean -modcache 后,再执行 go mod download,生成新 go.sum 中的 app-framework@v0.7.8 的 hash 还是老的,也就是说不同人执行相同的操作,得到的 hash 是不一致的

  3. 通过 go-checksum 工具,手动计算 app-framework@v0.7.8 模块的 hash,可以确定我自己后来生成的 hash 是正确的:

~/temp/app-framework  go-checksum go.mod
file: go.mod
{
    "Hash": "3efa7cc5648863b16b19e6e8b781f40bf4d0e95137b5a5f97e6468a00dc3f45a",
    "HashBase64": "Pvp8xWSIY7FrGebot4H0C/TQ6VE3taX5fmRooA3D9Fo=",
    "HashSynthesized": "ec77c7b8c39c8a73e44c330d11fe8eb5ccb8f9306f0d0ffe7f650ed0ddb7977a",
    "HashSynthesizedBase64": "7HfHuMOcinPkTDMNEf6Otcy4+TBvDQ/+f2UO0N23l3o=",
    "GoCheckSum": "h1:7HfHuMOcinPkTDMNEf6Otcy4+TBvDQ/+f2UO0N23l3o="
}
  1. 随后发现是 proxy 中缓存了不正确的模块导致的,清理 proxy 缓存,GitLab 的 lint 不再报错,这一问题得到解决

Q&A

1. 是什么原因导致同一个版本的模块,go.sum 文件里前后存在 hash 不一致现象?

go mod 拉取指定版本模块时,先找有没有这个版本的 tag,如果找到该 tag,直接拉取;如果没有对应的 tag,找指定版本同名的 branch,如果找到同名的 branch,直接拉取。如果是先有 branch,有人通过 proxy 拉取后,会缓存到 proxy 上。如果之后该模块在该 branch 上有修改,然后再打 tag,就会导致模块文件发生改变,hash 也会随之该改变,而 proxy 一直缓存是老的模块。

2. 如何避免 1 中的问题?

branch 名与 tag 不要同名,防止其他人使用了未正式发布的版本。

3. 既然 proxy 缓存了老的模块,为什么不同人删除 go.sum 文件,执行 go clean -modcache 后,再执行 go mod download,有的人还是老的模块,有的人获取到的是新的模块?

是因为 go env 中的 gonoproxy 环境变量导致。我本地 GONOPROXY=“git.xxx.com” ,即下载 git.xxx.com 上的模块,不走 proxy,直接从源站获取。而 gonoproxy 环境变量中没有设置 git.xxx.com 内容时,是从 proxy 获取(命中了老的缓存)。GONOPROXY 官方释义:

GONOPROXY — list of glob patterns of module path prefixes that should not be downloaded from a proxy. The go command will download matching modules from version control repositories where they’re developed, regardless of GOPROXY.