go.sum 的基本梳理和理解
引言
为了确保一致性构建,Go引入了go.mod文件来标记每个依赖包的版本,在构建过程中go命令会下载go.mod中的依赖包,下载的依赖包会缓存在本地,以便下次构建。
但是考虑到下载的依赖包有可能被黑客恶意篡改,以及缓存在本地的依赖包也有可能被篡改,单单一个go.mod文件并不能保证一致性构建。
因此引入了go.sum
文件,用于记录每个依赖包的哈希值,在构建时,如果本地的依赖包hash值与go.sum文件中记录得不一致,则会拒绝构建。
go.sum
文件是和 go.mod
文件一起使用的,它自动生成并记录着 Go 语言项目所依赖的所有模块的版本和其对应的哈希校验值。当你在项目中执行 go build
、go test
或 go mod tidy
等命令时,Go 工具链会自动生成或更新这个文件。
go.sum 文件格式
go.sum
的每一行都是一个条目,大致是这样的格式:
1 | <module> <version>/go.mod <hash> |
或者
1 | <module> <version> <hash> |
其中module是依赖的路径,version是依赖的版本号。hash是以h1:
开头的字符串,表示生成checksum的算法是第一版的hash算法(sha256)。正常情况下,每个依赖包版本会包含两条记录:
- 第一条记录为该依赖包版本整体(所有文件)的哈希值,
- 第二条记录仅表示该依赖包版本中go.mod文件的哈希值
如果该依赖包版本没有go.mod
文件,则只有第一条记录。
1 | github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= |
如上面的例子中,v1.1.1表示该依赖包版本整体,而v1.1.1/go.mod
表示该依赖包版本中go.mod文件。
依赖包版本中任何一个文件(包括go.mod
)改动,都会改变其整体哈希值
,此处再额外记录依赖包版本 的go.mod
文件主要用于计算依赖树时不必下载完整的依赖包版本,只根据go.mod即可计算依赖树。
go.sum 里的几种版本号
- 如果项目没有打 tag,会生成一个版本号,格式:
v0.0.0-commitDate-commitID
比如 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
。
- 引用一个项目的特定分支,比如 develop branch,也会生成类似的版本号:
v+1-commitDate-commitID
比如 github.com/DATA-DOG/go-sqlmock v1.3.4-0.20191205000432-012d92843b00 h1:Cnt/xQ9MO4BiAjZrVpl0BiqqtTJjXUkWhIqwuOCVtWo=
。
- 如果项目有用到 go module,那么就是正常地用 tag 来作为版本号。
比如 github.com/DATA-DOG/go-sqlmock v1.3.3 h1:CWUqKXe0s8A2z6qCgkP4Kru7wC11YoAnoupUKFDnH08=
。
- 如果项目打了 tag,但是没有用到 go module,为了跟用了 go module 的项目相区别,需要加个
+incompatible
的标志。
比如 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
- 对于使用了 v2+ go module 的项目,项目路径会有个版本号的后缀。
比如 github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
go.sum 的生成和校验
环境变量GOSUMDB
标识一个checksum database
,即校验和数据库,实际上是一个web服务器,该服务器提供查询依赖包版本哈希值的服务。
该数据库中记录了很多依赖包版本的哈希值,比如Google官方的sum.golang.org
则记录了所有的可公开获得的依赖包版本。
除了使用官方的数据库,还可以指定自行搭建的数据库,甚至干脆禁用它(export GOSUMDB=off
)。
如果系统配置了GOSUMDB,在依赖包版本被写入go.sum
之前会向该数据库查询该依赖包版本的哈希值进行二次校验,校验无误后再写入go.sum
。
如果系统禁用了GOSUMDB
,在依赖包版本被写入go.sum
之前则不会进行二次校验,go命令会相信所有下载到的依赖包,并把其哈希值记录到go.sum
中。
go get
当我们在GOMODULE模式下引入一个新的依赖时,通常会使用go get命令获取该依赖。
在更新go.sum
之前,为了确保下载的依赖包是真实可靠的,go命令在下载完依赖包后会查询GOSUMDB
环境变量所指示的服务器,以得到一个权威的依赖包版本哈希值。
如果go命令计算出的依赖包版本哈希值与GOSUMDB
服务器给出的哈希值不一致,go命令将拒绝向下执行,也不会更新go.sum文件。
go build
go命令会从本地缓存中查找所有go.mod中记录的依赖包,并计算本地依赖包的哈希值,然后与go.sum中的记录进行对比,即检测本地缓存中使用的依赖包版本是否满足项目go.sum文件的期望。
如果校验失败,说明本地缓存目录中依赖包版本的哈希值和项目中go.sum中记录的哈希值不一致,go命令将拒绝构建。
当校验失败时,有必要确认到底是本地缓存错了,还是go.sum记录错了。
需要说明的是,二者都可能出错,本地缓存目录中的依赖包版本有可能被有意或无意地修改过,项目中go.sum中记录的哈希值也可能被篡改过。
当校验失败时,go命令倾向于相信go.sum,因为一个新的依赖包版本在被添加到go.sum前是经过GOSUMDB 验证过的。
go.sum 的优缺点
- 提供分布式环境下的包管理依赖内容校验
在主流的包管理机制中,通常存在一个中央仓库来保证每个发布的版本的内容不会被篡改。
而 Go 并没有一个中央仓库。发布者在 GitHub 上给自己的项目打上 0.1 的 tag 之后,依旧可以删掉这个 tag ,提交不同的内容后再重新打个一样的 tag。发布者和发布平台都可能篡改。所以只能在每个项目里存储自己依赖到的所有组件的 checksum,才能保证每个依赖不会被篡改。
作为 transparent log 来加强安全性
go.sum 不仅仅记录了当前依赖的checksum,还保留了历史上每次依赖的 checksum。这种做法效法了 transparent log 的概念。
transparent log 旨在维护一个 Append Only 的日志记录,提高篡改者的作案成本,同时方便审查哪些记录是篡改进来的。可能产生合并冲突
每个commit都相当于新发布一个版本,这导致拉取它们的代码时会偶尔往 go.sum 文件里插入一条新记录。
在多人协作且用到几个经常升版本号的内部公共库的场景下,go.sum 可能需要手动介入来选择正确的版本。对于第三方库,缺乏约束能力
在有中央仓库保障的其他包管理器里,人们可以在源头上限制那些捣蛋鬼。但是 go.sum 带来的约束纯粹是道德上的。如果一个库乱改已经发布的版本,会让依赖这个库的项目构建失败。
犯错的本来是库的作者,麻烦的却是库的用户。对此库的使用者除了咒骂几句,在 issue 或别的地方痛斥作者,然后更新go.sum文件,似乎也没别的解决办法。
常用实践
依赖获取: 每次运行
go get
、go build
或go mod tidy
时,Go 会检查 go.mod 文件,以确定项目需要的依赖项和版本。然后,Go 会下载这些依赖项并计算它们的哈希值。校验: 计算得到的哈希值将与 go.sum 文件中的值进行比较。如果匹配,说明依赖项未被篡改。如果不匹配,Go 会报错,提醒开发者注意依赖项可能被篡改或版本不匹配。
更新: 如果添加了新的依赖项或修改了现有依赖版本,go.mod 会更新以反映这些变化。同时,go.sum 文件也会更新,加入新的哈希值以确保安全性。运行
go mod tidy
命令,有助于清理无用的依赖项并同步 go.mod 和 go.sum 文件。版本控制: go.mod 和 go.sum 文件通常会一同提交到版本控制系统(如 Git),确保团队成员和 CI/CD 系统获取相同的依赖版本,达到一致的构建结果。
总结
go.sum 文件是 Go 语言模块系统中的一个重要组成部分。它记录了每个模块的依赖和相应的校验和。这确保了在不同的开发环境中,使用相同版本的依赖。
参考
Go版本管理–go.sum - failymao - 博客园
谈谈go.sum - spacewander - SegmentFault 思否
Go Modules: v2 and Beyond - The Go Programming Language
更多内容
最近文章:
随机文章:
更多该系列文章,参考medium链接:
https://wesley-wei.medium.com/list/you-should-know-in-golang-e9491363cd9a
English post: https://programmerscareer.com/golang-go-sum/
作者:微信公众号,Medium,LinkedIn,Twitter
发表日期:原文在 2025-01-11 17:45 时创作于 https://programmerscareer.com/zh-cn/golang-go-sum/
版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
评论