众所周知,Go 在做依赖管理时会创建两个文件, 但实际上,日常开发中我们仍然不得不跟 鉴于涉及
或者
其中module是依赖的路径,version是依赖的版本号。hash是以 有些项目实际上并没有 如果只有对于 由于 go 的依赖管理背负着沉重的历史包袱,确定 version 的规则较为复杂。整个过程就像一个调查问卷,需要回答一个接一个的问题: 一、项目是否打tag? 如果项目没有打 tag,会生成一个版本号,格式如下: 比如 引用一个项目的特定分支,比如 develop branch,也会生成类似的版本号: 比如 二、项目有没有用 go module? 如果项目有用到 go module,那么就是正常地用 tag 来作为版本号。 比如 如果项目打了 tag,但是没有用到 go module,为了跟用了 go module 的项目相区别,需要加个 比如 三、项目用的 go module 版本是不是 v2+? 关于 go module v2+ 的特性,可以参考 Go 的官方文档:https://blog./v2-go...。简单而言,就是通过让依赖路径带版本号后缀来区分同一个项目里不同版本的依赖,类似于 对于使用了 v2+ go module 的项目,项目路径会有个版本号的后缀。 比如 之所以 Go 会在依赖管理时引入 (1)提供分布式环境下的包管理依赖内容校验 不像其他包管理机制,Go 采用分布式的方式来管理包。这意味着缺乏一个可供信赖的中心来校验每个包的一致性。 在主流的包管理机制中,通常存在一个中央仓库来保证每个发布的版本的内容不会被篡改。比如在 pypi 里面,即使发布过的版本存在严重的bug,发布者也不能重新发布一个同样版本,只能发布一个新版本。(但是却可以删掉已发布的版本抑或删掉整个项目,参考当年 npm 的 leftpad 事件,所以主流的包管理机制并非严格意义上的 Append Only。不过这并不影响我的论证) 而 Go 并没有一个中央仓库。发布者在 GitHub 上给自己的项目打上 0.1 的 tag 之后,依旧可以删掉这个 tag ,提交不同的内容后再重新打个 0.1 的 tag。哪怕发布者都是老实人,发布平台也可能作恶。所以只能在每个项目里存储自己依赖到的所有组件的 checksum,才能保证每个依赖不会被篡改。 (2)作为 transparent log 来加强安全性 go.sum 还有一个很特别的地方,就是它不仅仅记录了当前依赖的checksum,还保留了历史上每次依赖的 checksum。这种做法效法了 transparent log 的概念。transparent log 旨在维护一个 Append Only 的日志记录,提高篡改者的作案成本,同时方便审查哪些记录是篡改进来的。根据 Proposal: Secure the Public Go Module Ecosystem 的说法,go.sum 之所以要用 transparent log 的形式记录历史上的每个checksum,是为了便于 sum db 的工作。 不得不说的是, (1)容易产生合并冲突 这恐怕是 go.sum 最为人诟病的地方了。由于许多项目都没有通过打tag的方式来管理发布,每个commit都相当于新发布一个版本,这导致拉取它们的代码时会偶尔往 go.sum 文件里插入一条新记录。go.sum会记录间接依赖的特性,更是让这种情况雪上加霜。这一类的项目带来的影响可不小 —— 我粗略地统计下 go.sum 里这类记录的行数,大概占了总数的 40%。比如 如果只是莫名其妙的行数多,那最多不过是让人皱皱眉。在多人协作且用到几个经常升版本号的内部公共库的场景下,go.sum 会让人头疼。想象这种情况: 公共库原来有版本甲。
之后公共库发布了版本丁,包含了版本乙和版本丙的功能。 现在有两个选择:
无论采用哪种方法,都需要手动介入。这无疑带来了不必要的工作量。 (2) 对于胡乱操作的第三方库,缺乏约束能力 go.sum 的本意在于提供防篡改的保障,如果拉第三方库的时候发现其实际内容和记录的校验值不同,就让构建过程报错退出。然而它能做的也就只限于此。go.sum 的检测功能,给库的使用者带来的负担更甚于库的开发者。在有中央仓库保障的其他包管理器里,人们可以在源头上限制那些捣蛋鬼,不让他们随意变更已经发布出去的版本。但是 go.sum 带来的约束纯粹是道德上的。如果一个库乱改已经发布的版本,会让依赖这个库的项目构建失败。对此库的使用者除了咒骂几句,在 issue 或别的地方痛斥作者,然后更新go.sum文件,似乎也没别的解决办法。犯错的本来是库的作者,麻烦的却是库的用户。这种设计可算不上高明。一个可能的解决办法是由官方把知名的库的各个版本镜像起来。虽然知名的库通常不会犯乱改已发布版本的错误,但是如果发生了(或者出于某种不可抗力发生了),至少有个镜像可用。然而这又回到单一中央仓库的路子上去。 (3) 实际情况下,手动编辑go.sum不可避免。比如前面举的,编辑go.sum文件解决合并冲突的情况。我也见过有些项目只在go.sum里保留依赖的最新版本的checksum。如果 go.sum 不是完全由工具管理的,又怎么能保证它一定是 Append Only 呢?如果 go.sum 不是 Append Only 的,又怎么能把它当作 transparent log 使用呢? |
|