Go: cmd/go:向 Go 工具链添加包版本支持

创建于 2018-03-07  ·  242评论  ·  资料来源: golang/go

提议:为 Go 工具链添加包版本支持

将版本添加到 Go 开发人员和我们的工具的工作词汇表中已经是很久以前的事了。
链接的提案描述了一种方法。 关于替代方案的讨论,请特别参见基本原理部分。

此 GitHub 问题用于讨论提案的实质内容。

其他参考:

Proposal Proposal-Accepted modules

最有用的评论

该提案已经公开讨论了两个多月: @rsc@spf13进行了反馈会议,并从社区收集了宝贵的意见,从而对提案进行了修订。 @rsc还与@sdboyer举行了每周会议,以获得进一步的反馈。 已就该提案提供了宝贵的反馈意见,从而导致了额外的修订。 越来越多的反馈是针对随附的实施而不是提案。 经过大量审查,我们认为是时候接受这个提议了,让 Go 广泛的工具实施者生态系统开始进行重大调整,以便我们的用户群能够获得最佳体验。

该提案有两个反对意见,我们认为我们应该谈谈:

  1. 该提案将要求人们改变他们在使用和发布库方面的一些做法。
  2. 该提案未能为所有可能出现的涉及不兼容性的情况提供技术解决方案。

这些在他们的观察中是准确的,但按预期工作。 代码的作者和用户_将_必须改变他们在使用和发布库方面的一些做法,就像开发人员已经适应了 Go 的其他细节一样,例如运行 gofmt。 转变最佳实践有时是正确的解决方案。 同样,vgo 不需要处理所有可能涉及不兼容性的情况。 正如 Russ最近在新加坡 Gophercon 的演讲中指出的那样,解决不兼容问题的唯一永久解决方案是共同努力纠正不兼容问题并维护 Go 包生态系统。 像 vgo 或 dep 这样的工具中的临时解决方法只需要足够长的时间来给开发人员时间来解决真正的问题,而 vgo 可以很好地完成这项工作。

我们感谢您为这个关键问题带来的所有反馈和热情。 该提议已被接受。

— Go 提案审查委员会

所有242条评论

经常问的问题

此问题评论回答了最常见的问题,无论是来自下面的讨论还是来自其他讨论。 讨论中的其他问题在下一个问题评论中。

为什么提案不是“使用Dep ”?

大约两年前,在导致这个提议的旅程开始时,我们都认为答案将是遵循 Ruby 的 Bundler 和 Rust 的 Cargo 所示例的包版本控制方法:标记语义版本,手动编辑的依赖约束文件称为清单,单独的机器生成的传递依赖描述称为锁定文件,版本求解器用于计算满足清单的锁定文件,以及作为版本控制单元的存储库。 Dep 几乎完全遵循了这个粗略的计划,最初的目的是作为 go 命令集成的模型。 然而,我越了解 Bundler/Cargo/Dep 方法的细节以及它们对 Go 的意义,尤其是内置在 go 命令中,我与 Go 团队的其他人讨论这些细节的次数越多,其中的一些细节似乎越来越不适合 Go。 该提案对这些细节进行了调整,以期推出一个更易于开发人员理解和使用的系统。 有关我们想要更改的具体细节的更多信息,请参阅提案的基本原理部分,以及宣布提案的博客文章

为什么主要版本号必须出现在导入路径中?

遵循导入兼容性规则,这极大地简化了系统的其余部分。 另请参阅宣布该提案的博客文章,其中更多地讨论了导入兼容性规则的动机和理由。

为什么导入路径中省略了主要版本 v0、v1?

导入路径中省略 v1 有两个原因。 首先,许多开发人员将创建一旦达到 v1 就不会进行重大更改的包,这是我们从一开始就鼓励的事情。 我们不认为所有这些开发人员都应该被迫拥有明确的 v1,因为他们可能无意发布 v2。 v1 变成了噪音。 如果这些开发人员最终创建了 v2,那么额外的精度就会启动,以区别于默认的 v1。 关于将 v1 随处可见的稳定性有很好的论据,如果我们从头开始设计一个系统,也许这将使它成为一个关键的电话。 但是现有代码的权重使平衡强烈倾向于省略 v1。

导入路径中省略了 v0,因为 - 根据semver - 这些版本根本没有兼容性保证。 要求显式的 v0 元素对确保兼容性几乎没有作用; 您必须完全准确地说 v0.1.2,在每次更新库时更新所有导入路径。 这似乎是矫枉过正。 相反,我们希望开发人员只需查看他们所依赖的模块列表,并对他们找到的任何 v0.xy 版本保持适当的警惕。

这具有在导入路径中不区分 v0 和 v1 的效果,但通常 v0 是导致 v1 的一系列中断更改,因此将 v1 视为该中断序列中的最后一步是有意义的,而不是需要与 v0 区分开来. 正如@Merovius所说(https://github.com/golang/go/issues/24301#issuecomment-376213693):

通过使用 v0.x,您接受 v0.(x+1) 可能会迫使您修复代码。 如果将 v0.(x+1) 改为 v1.0,为什么会出现问题?

最后,省略主要版本 v0 和 v1 是强制性的 - 不是可选的 - 以便每个包都有一个规范的导入路径。

为什么我必须为 v2 创建一个新分支而不是继续在 master 上工作?

您不必创建新分支。 不幸的是, vgo 模块帖子在讨论“主要分支”存储库布局时给人留下了这种印象。 但是vgo并不关心分支。 它只查找标签并解析它们指向的特定提交。 如果您在 master 上开发 v1,您认为您已经完全完成了 v1,并且您想开始在 master 上进行 v2 提交,这很好:开始使用 v2.xy 标签标记 master。 但请注意,您的一些用户将继续使用 v1,并且您可能偶尔想要发布一个小的 v1 错误修复。 您可能至少希望在您开始使用 master for v2 时为该工作创建一个新的 v1 分支。

最小版本选择不会阻止开发人员获得重要更新吗?

这是一个普遍的恐惧,但我真的认为如果有任何相反的事情会发生。 引用https://research.swtch.com/vgo-mvs的“升级速度”部分:

鉴于最小版本选择需要每个依赖项的最小允许版本,很容易认为这会导致使用非常旧的包副本,这反过来可能会导致不必要的错误或安全问题。 然而,在实践中,我认为相反的情况会发生,因为允许的最小版本是所有约束中的最大值,所以对构建中的所有模块可用的一个控制手段是强制使用更新版本的能力比其他方式使用的依赖项。 我希望选择最少版本的用户最终会使用与使用 Cargo 等更具侵略性系统的朋友一样最新的程序。

例如,假设您正在编写一个依赖于少数其他模块的程序,所有这些模块都依赖于一些非常常见的模块,例如 gopkg.in/yaml.v2。 您的程序构建将使用您的模块和少数依赖项请求的最新 YAML 版本。 即使只是一个认真的依赖项也可能会迫使您的构建更新许多其他依赖项。 这与我之前提到的 Kubernetes Go 客户端问题相反。

如果有的话,最小版本选择反而会遇到相反的问题,即这个“最小值中的最大值”答案充当了一个棘轮,迫使依赖关系过快地向前发展。 但我认为在实践中,依赖关系会以适当的速度向前发展,最终会比 Cargo 和朋友慢适当的数量。

通过“适当的速度减慢”,我指的是仅在您要求升级时才发生升级的关键属性,而不是在您没有升级的时候。 这意味着代码只有在您预期会发生并准备对其进行测试、调试等操作时才会更改(以潜在的意外和破坏性方式)。

另请参阅@Merovius 的回复https://github.com/golang/go/issues/24301#issuecomment -375992900。

如果不推荐使用 $GOPATH,下载的代码在哪里?

您签出、处理和修改的代码可以存储在文件系统中的任何位置,就像几乎所有其他开发人员工具一样。

Vgo 确实需要一些空间来保存下载的源代码和安装二进制文件,为此它仍然使用 $GOPATH,从 Go 1.9 开始,它默认为 $HOME/go。 所以开发人员永远不需要设置 $GOPATH,除非他们希望这些文件位于不同的目录中。 要仅更改二进制安装位置,他们可以设置 $GOBIN(一如既往)。

你为什么要引入// import评论?

不是。 那是一个预先存在的约定。 游览中该示例的重点是展示go.mod如何从导入注释中推断出正确的模块路径(如果存在)。 一旦所有项目都使用go.mod文件,导入注释将完全多余并且可能已弃用。

讨论总结(最后更新于 2017-04-25)

此问题评论包含以下讨论的摘要。

我们如何处理迁移?

[ https://github.com/golang/go/issues/24301#issuecomment -374739116 @ChrisHines。]

由@rsc 回复https://github.com/golang/go/issues/24301#issuecomment -377529520。 最初的提议假设当兼容性对他们很重要时,迁移是由迁移到子目录的作者来处理的,但当然这种动机是错误的。 兼容性对用户来说是最重要的,他们对作者移动影响不大。 它对旧版本没有帮助。 链接的评论,现在也是 #25069,建议对旧的“go build”进行最小的更改,以便能够使用和构建模块感知代码。

我们如何处理单例注册?

[ https://github.com/golang/go/issues/24301#issuecomment -374791885 @jimmyfrasche。]

由@rsc 回复https://github.com/golang/go/issues/24301#issuecomment -377527249。 完全不同的模块之间的单例注册冲突(例如同一路径的 http.Handle)不受提案的影响。 对于单个模块的不同主要版本之间的冲突,作者可以编写不同的主要版本以期望协调,通常通过将 v1 调用到 v2,然后使用需求循环来确保 v2 不与不使用的旧 v1 一起使用不知道协调。

我们应该如何安装版本化命令?

[ https://github.com/golang/go/issues/24301#issuecomment -375106068 @leonklingele。]

由@rsc 回复https://github.com/golang/go/issues/24301#issuecomment -377417565。 简而言之,使用 go get。 我们仍然使用 $GOPATH/bin 作为安装位置。 请记住,$GOPATH 现在默认为 $HOME/go,因此命令将在 $HOME/go/bin 中结束,并且 $GOBIN 可以覆盖它。

为什么在导入路径中省略了 v0、v1? 为什么其他人必须出现? 为什么 v0、v1 一定不能出现?

[ https://github.com/golang/go/issues/24301#issuecomment -374818326 @justinian。]
[ https://github.com/golang/go/issues/24301#issuecomment -374831822 @jayschwa。]
[ https://github.com/golang/go/issues/24301#issuecomment -375437150 @mrkanister。]
[ https://github.com/golang/go/issues/24301#issuecomment -376093912 @mrkanister。]
[ https://github.com/golang/go/issues/24301#issuecomment -376135447 @kaikuehne。]
[ https://github.com/golang/go/issues/24301#issuecomment -376141888 @kaikuehne。]
[ https://github.com/golang/go/issues/24301#issuecomment -376213693 @Merovius。]
[ https://github.com/golang/go/issues/24301#issuecomment -376247926 @kaikuehne。]

添加到上面的常见问题解答中。

为什么提案中提到了 zip 文件?

[ https://github.com/golang/go/issues/24301#issuecomment -374839409 @nightlyone。]

生态系统将受益于定义具体的交换格式。 这将启用代理和其他工具。 同时,我们正在放弃直接使用版本控制(请参阅本文顶部的基本原理)。 这两者都激发了描述特定格式的动机。 大多数开发人员根本不需要考虑 zip 文件。 除非他们正在构建类似 godoc.org 的东西,否则没有开发人员需要查看它们的内部。

另请参阅关于 zip 与 tar 的 #24057。

将主要版本放在导入路径中不违反 DRY 吗?

[ https://github.com/golang/go/issues/24301#issuecomment -374831822 @jayschwa。]

不,因为导入的语义应该是可以理解的,无需参考 go.mod 文件。 go.mod 文件仅指定更精细的细节。 请参阅提案的语义导入版本部分的后半部分,从块引用开始。

此外,如果你干燥太多,你最终会得到脆弱的系统。 冗余可能是一件好事。 所以“违反[ing] DRY”——也就是说,有限地重复自己——并不总是坏事。 例如,我们将 package 子句放在目录中的每个 .go 文件中,而不仅仅是一个。 这在早期就发现了一些诚实的错误,后来变成了一种区分外部测试包(包 x 与包 x_test)的简单方法。 有一个平衡需要达成。

哪个时区用于伪版本中的时间戳?

[ https://github.com/golang/go/issues/24301#issuecomment -374882685 @tpng。]

世界标准时间。 另请注意,您不必自己键入伪版本。 你可以输入一个 git commit 哈希(或哈希前缀),vgo 将计算并替换适当的伪版本。

vgo 会解决非 Go 依赖项,例如 C 或协议缓冲区吗? 生成的代码?

[ https://github.com/golang/go/issues/24301#issuecomment -374907338 @AlexRouSg。]
[ https://github.com/golang/go/issues/24301#issuecomment -376606788 @stevvooe。]
[ https://github.com/golang/go/issues/24301#issuecomment -377186949 @nim-nim。]

非 Go 开发仍然是go命令的非目标,因此不会支持管理 C 库等,也不会明确支持协议缓冲区。

也就是说,我们当然明白在 Go 中使用协议缓冲区太难了,我们希望看到单独解决这个问题。

至于更普遍的生成代码,真正的跨语言构建系统是答案,特别是因为我们不希望每个用户都需要安装正确的生成器。 作者最好运行生成器并检查结果。

最小版本选择不会阻止开发人员获得重要更新吗?

[ https://github.com/golang/go/issues/24301#issuecomment -375090551 @TocarIP。]
[ https://github.com/golang/go/issues/24301#issuecomment -375985244 @nim-nim。]
[ https://github.com/golang/go/issues/24301#issuecomment -375992900 @Merovius。]

添加到常见问题解答。

### 可以用master开发v1,然后复用它开发v2吗?

[ https://github.com/golang/go/issues/24301#issuecomment -375248753 @mrkanister。]
[ https://github.com/golang/go/issues/24301#issuecomment -375989173 @aarondl。]

是的。 添加到常见问题解答。

时间线是什么?

[ https://github.com/golang/go/issues/24301#issuecomment -375415904 @flibustenet。]

@rsc 在https://github.com/golang/go/issues/24301#issuecomment -377413777 中回复。 简而言之,目标是在 Go 1.11 中实现“技术预览”; 工作可能会在冻结后的几周内继续进行,但不会更进一步。 在提案被标记为已接受并且 cmd/go 的开发副本已更新之前,可能不会向您可以找到的每个库发送添加 go.mod 的 PR。

如何进行向后不兼容的安全更改?

[ https://github.com/golang/go/issues/24301#issuecomment -376236546 @buro9。]

@rsc 在https://github.com/golang/go/issues/24301#issuecomment -377415652 中回复。 简而言之,出于安全原因, Go 1 兼容性指南确实允许进行重大更改以避免与主要版本发生冲突,但最好以尽可能保持现有代码工作的方式这样做。 例如,不要删除函数。 相反,只有在调用不当时才使函数 panic 或 log.Fatal。

如果一个 repo 在子目录中包含不同的模块(例如 v2、v3、v4),vgo 可以混合和匹配来自不同提交的模块吗?

[ https://github.com/golang/go/issues/24301#issuecomment -376266648 @jimmyfrasche。]
[ https://github.com/golang/go/issues/24301#issuecomment -376270750 @AlexRouSg。]

是的。 它将每个版本标签视为仅对应于整个存储库的一个子树,并且它可以为每个决策使用不同的标签(因此也可以使用不同的提交)。

如果项目滥用 semver 怎么办? 我们应该在导入路径中允许次要版本吗?

[ https://github.com/golang/go/issues/24301#issuecomment -376640804 @pbx0。]
[ https://github.com/golang/go/issues/24301#issuecomment -376645212 @powerman。]
[ https://github.com/golang/go/issues/24301#issuecomment -376650153 @pbx0。]
[ https://github.com/golang/go/issues/24301#issuecomment -376660236 @powerman。]

正如@powerman 所指出的,我们绝对需要提供一个 API 一致性检查器,以便至少可以告知项目何时它们即将发布明显的重大更改。

你能确定一个构建中是否有多个包吗?

[ https://github.com/golang/go/issues/24301#issuecomment -376640804 @pbx0。]

最简单的做法是在生成的二进制文件上使用goversion -m 。 我们应该做一个 go 选项来显示相同​​的东西而不构建二进制文件。

担心 vgo 依赖代理与供应商,尤其是开源与企业。

[ https://github.com/golang/go/issues/24301#issuecomment -376925845 @joeshaw。]
[ https://github.com/golang/go/issues/24301#issuecomment -376936614 @kardianos。]
[ https://github.com/golang/go/issues/24301#issuecomment -376947621 @Merovius。]
[ https://github.com/golang/go/issues/24301#issuecomment -376979054 @joeshaw。]
[ https://github.com/golang/go/issues/24301#issuecomment -376988873 @jamiethermo。]
[ https://github.com/golang/go/issues/24301#issuecomment -377134575 @Merovius。]

响应:[ https://github.com/golang/go/issues/24301#issuecomment -377411175 by @rsc] 代理和供应商都将受支持。 代理对企业很重要,厂商对开源很重要。 我们也想建立一个可靠的镜像网络,但只有在 vgo 变成 go 之后。

对 protobuild 的担忧取决于 GOPATH 语义。

[ https://github.com/golang/go/issues/24301#issuecomment -377601170 @stevvooe。]

响应 [ https://github.com/golang/go/issues/24301#issuecomment -377602765 by @rsc] 要求在新问题中提供更多详细信息,但该问题似乎尚未提交。

建议添加特殊的vgo-v1-lock标签。

[ https://github.com/golang/go/issues/24301#issuecomment -377662150 @kybin。]

起初它似乎很吸引人,但会导致可能不值得承担的特殊情况。 https://github.com/golang/go/issues/24301#issuecomment -384344659 中的完整回复。

如何在没有供应商的情况下修补深度依赖关系?

[ https://github.com/golang/go/issues/24301#issuecomment -378255833 @chirino。]

响应 [ https://github.com/golang/go/issues/24301#issuecomment -378261916 by @kardianos。] 通过使用替换指令。

我们将如何处理模块名称更改?

[ https://github.com/golang/go/issues/24301#issuecomment -379020339 @jimmyfrasche。]

响应 [ https://github.com/golang/go/issues/24301#issuecomment -379307176 @rsc]
这些是真实存在的问题,vgo 提案并未试图直接解决这些问题,但显然我们最终应该解决这些问题。 代码消失的答案是拥有缓存代理(镜像)以及信任它们的理由; 那是未来的工作。 (或者如果您愿意,也可以在顶级项目中使用供应商。)代码移动的答案是添加模块或包重定向的明确概念,就像类型别名是类型重定向一样; 这也是未来的工作。

连接到本地 Git 服务器怎么样?

[ https://github.com/golang/go/issues/24301#issuecomment -383168012 @korya。]

直接 git 访问已添加回计划中。 请参阅#24915。

仅二进制包呢?

[ https://github.com/golang/go/issues/24301#issuecomment -382793364 @sdwarwick。]

仅在将某种带外安装到 GOPATH/pkg 的有限情况下才支持仅二进制包。 Go get 从未支持获取和安装仅二进制包,它将继续不支持。 仅二进制包仅适用于一个特定的编译器和一个特定的依赖项副本,这严重限制了它的支持程度。 正确的答案几乎总是使用源代码。

我们应该在 go.mod 文件中使用path@version语法吗?

[ https://github.com/golang/go/issues/24301#issuecomment -382791513 @sdwarwick。]

这是#24119。 起初这似乎是一个好主意,但仔细考虑后,不是。

.

.

更改https://golang.org/cl/101678提到这个问题: design: add 24301-versioned-go

这个提议令人印象深刻,我最喜欢它的一切。 但是,我在邮件列表上发布了以下问题,但从未收到任何回复。 与此同时,我在 Gophers 的 vgo 松弛频道中看到了其他人提出的这个问题,也没有看到令人满意的答案。

来自: https ://groups.google.com/d/msg/golang-dev/Plc42fslQEk/rlfeNlazAgAJ

我最担心的是 pre-vgo 世界和 vgo 世界之间的迁移路径会很糟糕。 我认为如果没有顺利的迁移路径,我们可能会给 Go 社区带来重大痛苦。 显然迁移不能在整个社区中是原子的,但是如果我已经理解了你迄今为止所写的关于 vgo 的所有内容,那么可能存在一些情况,即现有的广泛使用的包将不能被 pre-vgo 工具和 post 使用-vgo 工具。

具体来说,我相信已经标记了具有主要版本 >= 2 的版本的现有软件包将无法与 vgo 一起使用,直到它们具有 go.mod 文件并且还使用 /vN 增强导入路径导入。 但是,一旦对存储库进行了这些更改,它将破坏包的 pre-vgo 使用。

这似乎产生了一种不同类型的 diamond 导入问题,其中位于 diamond 中间的两个同级包导入了一个通用的 v2+ 包。 我担心兄弟包必须自动采用 vgo 导入路径,以防止菱形顶部的包处于不可构建状态,无论它是使用 vgo 还是 pre-vgo 工具。

我还没有看到任何可以解释这种情况下迁移路径的东西。

该提案指出:

模块感知构建可以导入非模块感知包(那些在带有 go.mod 文件的树之外的包),只要它们被标记为 v0 或 v1 语义版本。 他们还可以使用 v0.0.0-yyyymmddhhmmss-commit 形式的“伪版本”来引用任何特定的提交。 伪版本形式允许引用未标记的提交以及使用 v2 或更高版本的语义版本标记但不遵循语义导入版本控制约定的提交。

但是我看不到非模块感知包导入具有传递依赖项> = v2的模块感知包的方法。 这似乎以尚未解决的方式导致生态系统碎片化。 一旦你有一个模块感知依赖,它的传递依赖中某处有一个包 >= v2,这似乎迫使它的所有依赖项也采用 vgo 来保持构建工作。

更新:另见https://github.com/golang/go/issues/24454

Go 项目从项目一开始就鼓励这种约定,但是这个提议给了它更多的好处:包用户的升级只会在包作者遵循导入兼容性规则的范围内成功或失败。

我不清楚这意味着什么,以及它与当前情况有何变化。 在我看来,这也描述了当前的情况:如果我违反此规则,升级和 go-get 将失败。 AIUI 并没有真正改变,我建议至少删除“更多牙齿”的提及。 当然,除非这一段是为了暗示有额外的机制来惩罚/防止破损?

这也会影响数据库驱动程序和图像格式等在初始化期间向另一个包注册的内容,因为同一包的多个主要版本最终可能会这样做。 我不清楚这会产生什么影响。

如果主版本是 v0 或 v1,则必须省略版本号元素; 否则必须包括在内。

为什么是这样? 在链接的帖子中,我只看到这是开发人员当前在进行重大更改时创建备用路径的基本原理 - 但这是一种解决方法,因为他们最初并未计划工具而不是为他们处理版本. 如果我们要改用新的做法,为什么不允许并鼓励(甚至强制)新的支持 vgo 的软件包包括v0v1呢? 似乎缺少版本的路径只是混淆的机会。 (这是一个 vgo 风格的包吗?模块边界在哪里?等等)

我通常喜欢这个提议,但我对在导入路径中要求主要版本感到困惑:

  1. 当可以从go.mod中知道主要版本时,它违反了 DRY 原则。 理解如果两者之间不匹配会发生什么也很难凭直觉。
  2. 允许v0v1不存在的不规则性也是不直观的。
  3. 升级依赖项时更改所有导入路径似乎很乏味。

我知道像moauth示例这样的场景需要可行,但希望不要以牺牲更常见场景的简单为代价。

首先:令人印象深刻的工作!

一件事对我来说完全不清楚,而且似乎有点不明确:

为什么这个提案中有一个 zip 文件?

布局、约束和多个用例,例如它何时创建以及如何管理它的生命周期、哪些工具需要支持、像 linter 这样的工具应该如何与之交互也不清楚,因为它们没有包含在提案中。

因此,如果您打算在本提案的范围内根本不讨论它,我建议您在此处参考一个稍后仍未成文的提案并删除 zip 一词或从提案文本中删除整个部分。

稍后讨论这一点也可以让不同的受众在这里做出更好的贡献。

哪个时区用于伪版本(v0.0.0-yyyymmddhhmmss-commit)中的时间戳?

编辑:
https://research.swtch.com/vgo-module 中所述,它采用 UTC。

@rsc你会解决 C 依赖关系吗?

看起来最小版本选择使非破坏性更改的传播非常缓慢。 假设我们有一个流行的库 Foo,它被项目 A、B 和 C 使用。有人在不更改 API 的情况下提高了 Foo 的性能。 当前接收更新是一个选择退出过程。 如果项目 A 提供了 Foo,但 B 和 C 没有,则作者只需将更新的 pr 发送到 A 的 vendored 依赖项。因此,非 api 破坏性贡献不会对社区产生太大影响,并且与当前相比有些气馁情况。 这对于安全更新来说问题更大。 如果某个废弃/小型/不太活跃的项目(不是库)声明直接依赖于旧版本的例如 x/crypto,那么该项目的所有用户都将容易受到 x/crypto 中的缺陷的影响,直到项目更新,可能永远存在。 目前此类项目的用户将收到最新的固定版本,因此这使安全情况变得更糟。 IIRC 有一些建议如何在邮件列表讨论中解决这个问题,但是,据我所知,这个提案没有提到它。

IIRC 在邮件列表讨论中提出了一些如何修复 [获取安全补丁] 的建议,但据我所知,该提案并未提及。

请参阅go get -p的提及。

请参阅 go get -p 的提及。

我已经看到了,但这仍然是一种选择加入机制。
我正在考虑让库将所有以前的版本标记为不安全的方法,以强制用户运行 go get -p 或明确选择加入不安全的库。

如果我们今天所知道的对go get的支持将被弃用并最终被删除,那么获取和安装(未标记)Go 二进制文件的推荐方法是什么? 它是否需要先git clone 'ing 项目,然后手动go install来安装二进制文件?
如果不推荐使用$GOPATH ,这些二进制文件将安装到哪里?

@leonklingele :据我了解,相反, go get不会被弃用。
它将通过自动和透明的版本控制功能得到增强。 如果一个项目依赖于一个未标记的项目,它只需要主人并在这个确切的版本中“供应商”它。
再次,我通过阅读一点点关于 vgo 的理解。 我仍在完全理解它的过程中。

我想知道这将如何影响一般使用 Git 存储库的流程,也是基于提案中的这句话:

如果主版本是 v0 或 v1,则必须省略版本号元素; 否则必须包括在内。

目前,在 master 上工作似乎很常见(对我来说,这包括短暂的功能分支)并时不时地用新版本标记提交。 当我发布我的库的 v2 时,我觉得这个工作流程与 Go 模块更加混淆,因为现在我有一个master和一个v2分支。 我希望master是当前分支,而v2是维护分支,但恰恰相反。

我知道默认分支可以从master更改为v2 ,但这仍然让我有任务在每次发布新的主要版本时更新它。 就个人而言,我宁愿有一个master和一个v1分支,但我不确定这是否适合该提案。

新的主要版本会导致流失。 如果每次发布新版本时都必须更改 Git 存储库(默认分支)中的一个设置,那么与库的用户切换到新版本相比,这是非常小的成本。

我认为提案的这一方面设置了正确的激励:它鼓励上游作者思考如何以向后兼容的方式进行更改,从而减少整体生态系统的流失。

现在我有一个 master 和一个 v2 分支

您可以改为在 master 中创建一个v2/子目录。

@mrkanister

我宁愿拥有一个 master 和一个 v1 分支,但我不确定这是否适合该提案。

根据我对https://research.swtch.com/vgo-module的理解,vgo 使用标签而不是分支来识别版本。 因此,只要标签指向正确的分支并提交,您就可以继续在 master 上进行开发并从 v1 分支。

新的主要版本会导致流失。 如果每次发布新版本时都必须更改 Git 存储库(默认分支)中的一个设置,那么与库的用户切换到新版本相比,这是非常小的成本。

这是一种有问题的思维方式,我认为它在过去一直困扰着 Go。 对于一个项目的一个人来说,现在切换默认分支很简单,是的。 但是违反工作流程约定意味着人们会忘记,尤其是当他们使用多种语言工作时。 这将是 Go 如何以完全不同的方式完成新人必须学习的事情的又一个古怪的例子。 违背常见的程序员工作流程约定_根本不是_小成本。

违背常见的程序员工作流约定并不是一个小成本。

不走常规路线有时是创新的必要条件。

如果我正确理解了提案的部分内容,则您永远不必创建子目录或新分支。 只要确保将 go.module 更新为库的正确导入路径,您就可能只有一个 master 分支和 git 标记您的 repo 从 0.0 到 1.0 到 2.0 等等。

@mrkanister我认为,对于开发人员,您克隆您的主(或任何开发分支)并使用“替换”指令(参见 vgo-tour)指向它。 (如果我明白你的意思,不确定)。

@rsc我想请您更准确地了解路线图以及我们现在应该做什么。
它会遵循 Go 政策并在 3 个月(现在是 2 个月)冻结 vgo 吗?
我们现在应该带着朝圣者的指挥棒去要求每个 libs 维护者添加一个 go.mod 文件,还是应该等待提案被正式接受(以确保名称和语法不会改变)?

@flibustenet工具不在 1.0 政策范围内,因此任何事情都可能发生变化。

https://golang.org/doc/go1compat

最后,Go 工具链(编译器、链接器、构建工具等)正在积极开发中,可能会改变行为。 这意味着,例如,依赖于工具位置和属性的脚本可能会被单点发布破坏。

也来自提案

该计划在获得提案批准后,将在 Go 1.11 中发布模块支持,作为可能仍会更改的可选功能。 Go 1.11 版本将让用户有机会“真正”使用模块并提供关键反馈。 尽管细节可能会发生变化,但未来的版本将能够使用与 Go 1.11 兼容的源代码树。 例如,Go 1.12 将了解如何使用 Go 1.11 的 go.mod 文件语法,即使到那时文件语法甚至文件名已经改变。 在以后的版本中(比如 Go 1.12),我们将声明模块支持已完成。 在以后的版本中(比如 Go 1.13),我们将结束对非模块的 go get 的支持。 对在 GOPATH 中工作的支持将无限期地继续下去。

感谢您的反馈。

@AlexRouSg

根据我对https://research.swtch.com/vgo-module的理解,vgo 使用标签而不是分支来识别版本。 因此,只要标签指向正确的分支并提交,您就可以继续在 master 上进行开发并从 v1 分支。

你是对的,这将继续像以前一样工作(只需仔细检查以确保),很好!

除此之外,我(显然还有其他人)不理解的是不允许v1包存在的原因。 我尝试在导入结束时使用/v1导入一个,并将其添加到正在导入的包的go.mod中,但vgo将查找名为v1的文件夹

@mrkanister
我认为在导入路径中不允许 v1 或 v0 的主要原因是确保每个兼容版本的包只有一个导入路径。
使用普通导入路径而不是 /v1 可以简化转换,因此您不必更新所有导入路径以在末尾添加 /v1。

你好,

虽然提案中的许多要点都非常受欢迎,并且将有助于驯服随着时间的推移而出现的大型 Go 代码库,但“使用最小版本”规则是非常有害的:

  • 你希望你的代码生态系统能够进步。 这意味着您希望人们测试和使用新版本,并在问题积累之前及早发现问题。
  • 您希望尽快应用修复安全问题的新模块版本
  • 您希望能够尽快应用修复安全问题的新模块版本。 它们并不总是被标记为安全修复。 如果您避免发布新版本,您也会避免那些修复
  • 即使新版本不包含安全修复程序,尽早应用其更改也意味着在发布包含安全修复程序的下一个版本时,审核的更改将更少(以及发布此类版本时您想要的最后一件事,您需要快点就是陷入你以前没有看过的中间变化中)。
  • 应用中间版本只有在它们破坏兼容性时才有害,并且它们不应该破坏兼容性,并且如果它们确实破坏了兼容性,最好检测它并在模块作者养成下一个版本的习惯之前告诉模块作者,你最终绝对会需要。
  • 您不希望旧代码拖累您,因为它们仍然指定一个古老的依赖版本,并且没有人找到时间更新其清单。 使用主要版本的最新版本可以满足其他代码生态系统中的这种社会需求:强制开发人员测试最新版本,不要因为“有更重要”(即更有趣)的事情要做而推迟到为时已晚。

    • 虽然从理论上讲,您可以发布无限数量的模块版本,因此每段代码都可以使用它想要的版本,但实际上,一旦您组合了两个使用相同 dep 的模块,您就必须选择一个版本,这样您的代码越复杂软件是,您对多个版本的容忍度越低。 因此,您很快就遇到了如何处理拖慢整个车队的落后者的老问题。 我从来没有遇到过一种人类文化可以通过告诉落后者“你是对的,尽可能慢,每个人都会等你”来解决这个问题。 这可能是好的和无私的,但它没有生产力。

与人类的惰性作斗争是艰难而痛苦的,我们与它作斗争是因为它需要进步,而不是因为它令人愉快。 制作令人愉快的工具来避免问题并煽动人们更多地拖延根本没有帮助,它只会加速项目沉淀和技术债务积累。 github 上已经有几十个 Go 项目,其中大部分自述文件专门用于作者恳求他的用户升级,因为他做了重要的修复,默认使用最旧的版本会概括问题。

一个好的规则是“使用与主要版本匹配的最新版本,而不是每个中间提交”。 这将是一个妥协的前进和稳定。 它将最了解代码库的原始项目置于指挥之下,并且可以明智地决定何时将其用户切换到新的代码状态。

我从邮件列表中复制的未回答问题:

我们预计大多数开发人员会更喜欢遵循通常的“主要分支”约定,其中不同的主要版本存在于不同的分支中。 在这种情况下,v2 分支中的根目录将有一个 go.mod 指示 v2,如下所示:

似乎有 vgo 支持的子目录和这个主要的分支约定。 在我的轶事经验中,没有任何存储库遵循 Go 或其他语言中的这种约定(实际上无法想到除了 gopkg.in 强制要求的存储库,这些天似乎相对未使用)。 Master 分支是最新的,并且在它的历史中具有 v2.3.4 标签。 存在标签以分隔所有内容(不仅仅是次要版本)。 如果需要对旧版本进行修补,则从最后一个 v1 标签临时创建一个分支,推送提交,推送新标签,然后立即删除该分支。 版本没有分支,它只是当前的 master/dev/feature 分支 + 版本标签。 我知道 Git 中的“一切都是参考”,但对于其他 VCS,区别可能不那么模糊。

话虽如此,我已经用 vgo 测试了上述工作流程(只有标签显示 v2.0.0、v2.0.1 并且没有分支),它似乎确实有效。 所以我的问题是:虽然这现在有效,但它是有意的吗? 因为它似乎没有像博客中的其他两个工作流程那样详尽地描述,我想确保在没有v2/v3 的情况下工作......分支不是偶然的功能,因为正如我上面解释的那样,我从来没有在帖子中看到这个(或另一个)描述的工作流程被任何人(尤其是在 Go 社区之外)大规模采用。

当然,我的论点归结为偏好和轶事,所以如果需要,我愿意做一些回购数据来证明所有语言的这一点。 到目前为止,我真的很喜欢这些提案帖子,并且总体上同意这些变化,将继续跟进并使用 vgo。

感谢您的所有努力。

有人可以澄清提议的 MVS 替代模型将如何改善升级节奏吗? 因为我不清楚。 我对替代(广泛使用)模型的理解是

  • 开发人员创建手工清单,列出所有使用的依赖项的版本约束
  • 开发人员运行 $solver,它创建一个锁文件,列出满足指定约束的传递依赖版本的一些选定子集
  • 此锁定文件被提交并在构建和安装时使用以保证可重现的构建
  • 当新版本的依赖项发布并使用时,开发人员可能会更新清单,重新运行求解器并重新提交新的锁定文件

据我所知,提议的 MVS 模型是

  • 开发人员根据模块中的导入路径集自动生成go.mod ,选择任何传递依赖项的当前最新版本
  • go.mod被提交并用于在构建和安装时获取版本的下限。 MVS 保证可重现的构建
  • 当发布并使用新版本的依赖项时,开发人员运行vgo get -u ,它获取传递依赖项的最新版本并用新的下限覆盖go.mod 。 然后提交。

看来我必须严重忽略某些事情,如果有人能指出什么会有所帮助。 因为这种理解似乎暗示由于锁定文件指定了确切的版本以及在实际构建中使用的版本,所以 MVS擅长增加升级节奏 - 因为它通常不允许保留版本。

显然我错过了一些东西(大约 5m 会感到愚蠢),那是什么?

@tpng

使用普通导入路径而不是 /v1 可以简化转换,因此您不必更新所有导入路径以在末尾添加 /v1。

这实际上应该没有必要。 让我举个例子:

用户当前正在使用例如v1.0.0的库,由依赖管理器和上游存储库中的标记固定。 现在上游决定创建一个go.mod并调用模块/v1 。 这应该会产生一个新的提交和一个新的标签(例如v1.0.1 )。 由于vgo永远不会尝试自行更新依赖项,这不应该对用户造成任何破坏,但他/她也可以通过更改导入路径来有意识地更新(pr vgo可以做到这一点为他/她)。

我认为在导入路径中不允许 v1 或 v0 的主要原因是确保每个兼容版本的包只有一个导入路径。

是的,我想我确实可以看到这一点,不要让图书馆的新用户感到困惑。

如果主版本是 v0 或 v1,则必须省略版本号元素; 否则必须包括在内。

有人可以解释一下这背后的原因吗? 作为库的用户,当我还不想使用 v1 时,我该怎么办,因为它引入了一个重大更改,这对于语义版本控制(新的主要版本)完全没问题?

我宁愿只省略版本 1 之前的版本,这表示一个不稳定/未完成的包。 从版本 1 开始,我希望能够依靠获得稳定版本这一事实。 在导入路径中省略 v1 会造成混淆,因为您不知道您跟踪的是不断变化的库还是稳定版本。 我认为这也不适用于语义版本控制方案,其中 v1 确定了第一个稳定版本,用于清楚地区分该版本与 0.x 版本。

@kaikuehne

作为库的用户,当我还不想使用 v1 时,我该怎么办,因为它引入了一个重大更改,这对于语义版本控制(新的主要版本)完全没问题?

据我了解, vgo永远不会自行更新依赖项,甚至不会更新到补丁版本,而是将其作为一个有意识的决定让您做出。 因此,如果您依赖于 v0.4.5 的库(它有一个标签),理论上您可以永远使用它。 您还可以在go.mod文件中手动固定版本。

如果我使用的另一个依赖项依赖于同一个包,但版本 v1 怎么办? 两个包都使用相同的导入路径导入。 将它们编译成同一个二进制文件时不会发生冲突吗?

如果我们要求 v1 也成为导入路径的一部分,那么两者都将被视为不同的包,它们就是这样。

@kaikuehne然后它将更新到可以工作的最小通用版本。 (据我了解)

@kaikuehne我不明白你的推理。 您正在使用 v0,所以大概您可以接受重大更改; 考虑到您已经在使用没有稳定性保证的版本,如果 v1 中断,为什么会有问题? 此外,假设上游不会从 v0.1->v1.0 进行重大更改,而是将中断添加到 v0.2,然后发布(非破坏性)v1.0。 这似乎在语义版本的预期范围内,但对您来说相当于完全相同的工作量(无论使用什么包管理器)。 即,我真的不明白“作者突然停止破坏他们的 API”如何构成不是由使用 1.0 之前的版本引起的问题。

换句话说:通过使用 v0.x,您接受 v0.(x+1) 可能会迫使您修复代码。 如果将 v0.(x+1) 改为 v1.0,为什么会出现问题?

关于讨论点4:明确采用导入兼容规则和“新包必须向后兼容旧包”...

就我而言,我有一个安全包https://github.com/microcosm-cc/bluemonday并且我最近(去年年底)有一个场景,我了解到公共 func 从根本上不适合目的。 所以我删除了它。

根据这个提议,如果我删除了这个函数,它会影响版本,并且永远不会删除不安全/不安全的代码。

为了避免这种情况,我可能会在我希望删除的函数中改为 log.Fatal(),纯粹是为了确保现有代码没有使用不安全的端点,而是为了保持兼容性。

鉴于这些都不是理想的......我们如何预见需要公共功能被硬弃用的安全修复程序? (如果不是让开发人员对运行时 log.Fatal() 感到惊讶的话?)

对于那些希望看到提交的人: https ://github.com/microcosm-cc/bluemonday/commit/a5d7ef6b249a7c01e66856b585a359970f03502c

@Merovius感谢您在使用 0.x 版本时的澄清。 就像您说的那样,在使用 1.0 之前的版本时不能保证可以依赖,并且 0.x 版本是 semver 中的一个特例。 如果我理解正确,概述的规则实际上根本不适用于 0.x 版本。 我的问题是在代码中反映这种区别是否有意义——尤其是在命名包时。 例如,如果一个包只导入了没有版本的包,你一眼就能看出它是建立在不稳定的代码之上的。 如果所有导入都包含版本 v1,您会看到它使用的是稳定版本。

@buro9该提案建议大致遵循 go1 兼容性保证,其中包含与安全相关的 API 损坏的豁免。

@kaikuehne谢谢,这澄清了您的担忧。

我关心的是功能的交互(假设我理解正确)。

如果您有一个使用具体版本的模块 M(源中的文字 vN 目录,而不是从标签派生的合成导入路径元素),并且您正在构建一个程序 P 可传递地依赖于 M 的多个主要版本,不是吗在某些情况下必须违反最小版本选择?

也就是说,假设 P 依赖于 M 的主要版本 2、3 和 4。对于 M 的每个主要版本,都指定了一个最小的完整版本。 由于 M 的版本共享源代码的目的是为了能够透明地使用具有类型别名的相同类型定义,因此所有三个主要版本只能包含一个 M 副本,而不是每个主要版本一个副本。 对一个主要版本的完整版本的任何选择都会修复对其他两个主要版本的完整版本的选择,并可能导致为其他主要版本中的一个或两个选择非最小版本。

vgo 是如何处理这个问题的? 除了有时略低于最低限度之外,这是否会导致任何问题? (比如有可能意外地构建一组没有产生解决方案或导致求解器循环的模块?)

@jimmyfrasche

如果您使用的是主要版本目录,vgo 仍将使用标签,然后只获取该标签的匹配版本文件夹。 例如,您依赖于版本 2、3、4。vgo 将签出标签 v2.nm、v3.nm、v4.nm
然后从标签 v2.nm 只获取 v2 文件夹,依此类推。 所以最后一切都还是跟着标签。

我在这个邮件列表帖子中问了一个问题,但还没有看到具体的答案。 我会在这里重复一遍:

非 Go 资源会发生什么,例如 protobufs 或 c 文件? 如果您的帖子中已经回答了这个问题,我们深表歉意,但我们确实使用供应商路径来分发和设置 protobuf 文件的导入路径。 虽然我们会根据预编译的 protobuf 输出编译 Go 包,但我们还必须考虑从依赖于 vendored 依赖项的 protobuf 文件编译新 Go 包的情况(即从另一个 protobuf 文件引用 rpc.Status)。 如果该描述过于密集,我可以提供一些更具体的示例。

具体来说,我有一个名为protobuild的项目,它允许将 protobuf 文件导入映射到 GOPATH 位置。 我没有看到该提案将如何处理 GOPATH 中的资源解析,以及我们如何将其映射到其他编译器导入空间。

对于我们来说,使用 protobufs 是一个巨大的痛点,这个项目缓解了很多这些问题。 如果这与提案完全不相容,那将是一种耻辱。

再次道歉,如果有什么我错过了。

我喜欢这个提议。 我只担心太多的项目经常在次要版本之间引入小的破坏性更改,并且只为非常大的破坏性更改保留主要版本的颠簸。 如果较小的版本破坏性更改频繁,则会出现菱形依赖问题,因为它违反了 SIV 的假设。 我想知道在导入声明中包含次要版本是否会帮助更多项目遵守非破坏性变更合同。 我认为缺点是额外的导入流失,并且更新次要版本变得更加困难(并且更加明确)。

这提出了我的另一个问题:(今天)使用 vgo 是否容易确定构建中是否有超过 1 个版本的包? 虽然有时允许同一个包的两个版本对于向前发展至关重要,但似乎大多数项目都希望避免这种情况,除非由于可​​能无法预料的 init 副作用而暂时避免这种情况。 有一种简单的方法来检查这可能很有用,并且某些项目可能希望在代码签入期间强制执行它。

我只担心太多的项目经常在次要版本之间引入小的破坏性更改,并且只为非常大的破坏性更改保留主要版本的颠簸。

突破性的变化是一个突破性的变化。 它不能小或大。 我想这样的包最终会被社区拒绝,因为太不可靠并且不遵循 semver。 即使他们会遵循 semver 并迅速获得像 42​​.xx 这样的大型主要数字,他们无论如何都会非常不方便使用。 因此,如果您想进行大量重大更改,则只需继续使用主要编号 0。这样它将继续像当前的go get一样工作,但存在相同的问题。 如果您想在发布非 0 主要版本后尝试 API - 将这些实验移动到将“实验”包与永远的主要 0 分开,并在您完成“小的重大更改”时增加主包的主要版本并最终获得下一个稳定的 API。

在导入路径中包含次要违反了 semver 意识形态,并且不提供任何超过包含主要的新功能。

虽然我知道鉴于 semver 的语义,这是作者应该做的,但我担心的是,对于已经超过版本 1 的人来说,小版本之间的微小破坏性变化是一个常见且诱人的案例。根据我的轶事经验,包作者不会遵循 semver 的确切语义,并且通常公司保留主要版本的颠簸用于营销目的。 所以我的诉求不是关于什么是理想的,而是关于做出某些改变以更好地处理 semver 的混乱人类现实是否可行。

也许它甚至可以选择包含不服从 semver 的软件包的次要版本,也许这会搞砸整个提案,我需要更多地考虑它。 我并不是说这是一个更好的方法,只是我有兴趣进一步探索在进口中选择包含或强制包含次要版本的权衡。

它可能会从 go 语料库(以及一个寻找明显重大变化的工具)中生成更好的数据,以确定在使用 semver 标签的现有流行项目中最常遵循 semver 的频率。

vgo 提案允许主要版本优雅地中断 API 更改。 它(本身)并没有阻止它们。

vgo 提议允许一个主要版本引用另一个主要版本,允许优雅的代码重用。 它并没有强迫这样做。

vgo MVS 提议允许更新到更新的包。 它不会强迫您更新。


以下是我们可以在 vgo 世界中更轻松地构建的东西:

  1. 在推送到模块主机之前捕获 API 制动变化的工具。 每个版本都在一个 zip 中,可以在没有许多不同的 vcs 工具和命令的情况下进行比较。
  2. 安全问题注册表。 在模块托管旁边托管以实现增值。 工具可以手动或自动查询它们,类似于go list ,并在查询过滤的问题时得到通知。

vgo 通过以下方式使这些更容易:

  • 限制问题空间(仅适用于 zip 文件,无需存储任意 git/hg/svn 历史记录来进行 API 比较)。

    • 定义问题空间(MVS,版本定义)

  • 工具构建部分。

vgo 遵循一个主要原则:一组输入应该始终构建相同的东西。 如果我们依靠随机重建来捕捉安全更新,那我们就错了。 作为项目的维护者,一些 Go 项目只是运行了多年而没有重建。 我同意及时的安全更新很重要:vgo 满足了这种需求的构建部分。

我们不应该混淆 vgo 允许什么和 vgo 是什么。

@pbx0我不确定这是否适合进行此类讨论,也许邮件列表更合适。 在任何情况下,都会发生重大更改而不更改主要编号。 甚至偶尔。 Semver 在常见问题解答中回答了这个案例:

一旦你意识到你已经破坏了语义版本规范,请修复问题并发布一个新的次要版本来纠正问题并恢复向后兼容性。

但我认为这里有改进的地方。 我想它应该很容易实现自动化 API 检查器(甚至可能已经存在),它将获取 godoc.org 上列出的所有包并定期检查新版本,如果检测到新版本,检查新版本是否与以前兼容版本的 API,以防它使用 semver 标签并且专业没有更改。 如果检测到不兼容的更改,然后尝试联系作者 - 可能会在 github 上自动打开问题或使用来自 github 帐户的电子邮件。 这不会涵盖所有可能的情况,但可能对社区非常有帮助。

我坚信使用路径来确定主要版本,并希望表达我的支持,FWIW。

一年前,我认为将版本放在这样的导入路径中是丑陋的、不可取的,而且可能是可以避免的。 但在过去的一年里,我开始了解它们为系统带来了多少清晰和简单。

近二十年前,我的创业公司需要一个 VCS,并尝试了大约十种不同的 VCS。 我立即排除了 Perforce,因为它使用目录暴露了分支。 所以 v2 分支将是所有相同的代码,但在 v2 文件夹下。 我讨厌这个主意。 但是在与其他九个运行试验后,我发现我经常希望在我的本地文件系统上拥有两个版本的分支,结果证明 Perforce 让这变得非常简单,而其他人却让它变得异常复杂。 文件系统是我们的包容隐喻。 让我们使用它。

从提案中:

定义用于从代理获取 Go 模块的 URL 模式,用于使用自定义域名安装模块以及设置 $GOPROXY 环境变量时。 后者允许公司和个人出于安全性、可用性或其他原因通过代理发送所有模块下载请求。

我在其他地方提到过这一点,但我对该提案最大的担忧是它似乎放弃了(或至少没有解决)当今供应商的最大优势之一:始终可用的代码和完全可重现的构建。 我认为提议的代理系统不足以解决或取代这些优势。

今天使用供应商的项目只需要 Go 工具链和 Git(或其他版本控制系统)。 仅存在一个单点故障:git 存储库。 一旦代码被签出,它就可以完美地构建和重建,而无需再次接触网络——这是一个重要的安全考虑。 对于我们大多数人来说,一个供应商项目不需要额外的基础设施——只需要你的本地计算机和一个免费的 GitHub 帐户。

vgo对代理的依赖引入了一个重要的基础设施开销,我认为这并没有得到充分强调。 必须提供、实施、维护和监控新的基础设施。 这对于软件组织来说可能是合理的,但对于个人和分散的开源项目来说却是一种负担。 此外,像 CI 这样的东西经常外包给 Travis CI 这样的第三方,这些第三方不在与公司开发环境相同的基础架构中运行。 这使得重用代理基础设施变得更加困难。

其他语言允许使用缓存代理。 Python 是我最有经验的一种,根据我的经验,由于设置它的开销,它很少使用。 也许 Go 项目可以让这变得更简单,但如果它不是默认设置,我们会发现它的使用频率会降低,并且 Go 构建在野外的可用性将大幅下降。 left-pad NPM 事件的影响是这方面的一个很好的案例研究。

如果模块和代理系统允许我们在我们自己的项目代码旁边检查依赖项(类似于今天的供应商,但不一定是相同的实现)并且vgo代理实现可以使用它,那将解决我的担忧。 但如果这是意图,我认为需要在提案中更全面地解决它。

@joeshaw您将继续能够使用供应商目录来创建自包含的构建。

从提案中:

不允许使用供应商目录,除非在一个有限的用途中:正在构建的顶级模块的文件树顶部的供应商目录仍应用于构建,以继续允许自包含的应用程序存储库。 (忽略其他供应商目录可确保 Go 返回构建,其中每个导入路径在整个构建中具有相同的含义,并确定在给定构建中仅使用具有给定导入路径的包的一个副本。)

今天使用供应商的项目只需要 Go 工具链和 Git(或其他版本控制系统)。 仅存在一个单点故障:git 存储库。

今天,如果你在自己的域下托管包,你需要 a) 托管 git-repository 和 b) 提供 go-get 找到它所需的<meta>标签。 将来,您需要 a) 提供 vgo 获取代码所需的.zip.json文件。 纯托管所需的故障点和基础设施似乎更少。 当然,您仍然需要托管存储库以进行开发,但这不仅会使我们在最坏的情况下达到与以前相同的水平,而且存储库本身也需要更少的可扩展性和可靠的托管,因为它仅由实际开发人员使用。

因此,对于虚荣进口,在开销方面似乎没有太大差异。

OTOH,如果您没有使用虚荣导入,那么您将忽略github正在为您运行的所有基础设施。 所以这似乎不是一个苹果对苹果的比较:你脱颖而出的唯一方法是让其他人解决难题。 但是没有什么能阻止你在未来这样做。 AIUI 微软和其他公司已经自愿将工程时间和服务能力投入到这项任务中。 我希望在未来,托管 Go 包的工作量将大致低于托管 npm 包或 gem 或 crates 的工作量:你有一个 github 存储库,然后单击某个中心的“使其可用”按钮托管 zip 的托管服务。

vgo对代理的依赖

就个人而言,我不喜欢用“代理”这个词来表示所需的基础设施的作用。 “镜子”似乎更合适。 镜像可以作为代理实现,但也可以只是由您选择的网络服务器提供的一堆文件,隐藏在 cloudflare 后面,以实现几乎无限的可扩展性和正常运行时间。

其他语言允许使用缓存代理。

我认为其他语言可以作为一个完美的模型来解释为什么这不是一个真正的问题。 他们倾向于依靠集中托管来托管他们的包 - vgo不仅非常好地支持这个模型,它还使它成为可选的(所以你得到了所有的优点而不是缺点)并且非常简单实施、扩展和运营。


IMO,如果你直接比较 Go 和其他语言之前和之后发生的事情,应该很清楚有很多 1:1 的等效性。 感觉未来会更加努力的唯一原因是因为我们认为现有的基础设施是理所当然的,并且看到新的基础设施还不存在。 但我认为我们没有充分的理由怀疑它会这样做。

因此,对于虚荣进口,在开销方面似乎没有太大差异。

没错,但 _vast_ 少数软件包使用虚域,所以我不认为这是一个强有力的对立面。 (我还有其他关于虚域的实际问题,那就是它们的可用性往往比仅使用 GitHub 更糟糕。)

如果,OTOH,您没有使用虚荣导入,那么您将忽略 github 正在为您运行的所有基础设施。

对,就是这样! 我觉得这对我来说很重要。 您可以免费或以合理的成本获得基础设施和维护正常运行时间方面的所有出色工作。 更重要的是,它不涉及你自己的任何时间。

如果 GitHub 最终运行了一个vgo兼容的镜像,那么也许这不是一个问题,尽管我喜欢单个 Git 提取的优雅——从用户的角度来看一个原子操作——包含所有代码我需要建立一个项目。

我认为其他语言可以作为一个完美的模型来解释为什么这不是一个真正的问题。 他们倾向于依靠集中托管来托管他们的包来托管包 - vgo 不仅非常好地支持这个模型,它还使它成为可选的(所以你可以在没有任何缺点的情况下获得所有优点)并且非常易于实现、扩展和操作。

问题在于,这会在构建项目时增加单点故障 (SPOF)。 无论如何,代码都将存在于 VCS 中(可能还有 GitHub),所以这是一个 SPOF。 在其他语言中,集中式存储库是第二个 SPOF。 对于 Go,每个导入路径都是一个额外的 SPOF(github.com、golang.org、honnef.co、rsc.io 等),增加 SPOF 会降低整体可用性。

当然,运行镜像可能会将其减少到两个,但我认为不需要存在它的基础设施。 供应您的部门(或拥有本地磁盘镜像)将其简化为一个:您的 VCS。

无论如何,这可能是一个有争议的问题。 我最初并不理解提案中关于顶级vendor目录的部分,这似乎可以解决我的主要问题——感谢您指出这一点, @kardianos——尽管与旧的完全不同销售系统可能会更好。

我很高兴“顶级vendor ” 仍然是受支持的配置,因为我非常喜欢git grep

当然,运行镜像可能会将其减少到两个,但我认为不需要存在它的基础设施。

企业开发人员在这里。 也许我们不是目标人群,但我们想使用 Go。 如果我们在企业内部没有镜像,我无法看到这种情况发生,就像我们为 java 和 javascript 所做的那样,以确保我们今天构建的任何东西都可以在明天构建。

企业开发人员在这里。 也许我们不是目标人群,但我们想使用 Go。 如果我们在企业内部没有镜像,我无法看到这种情况发生,就像我们为 java 和 javascript 所做的那样,以确保我们今天构建的任何东西都可以在明天构建。

@jamiethermo今天供应商不能为您解决这个问题吗?

对代理或镜像的支持很好,如果这有助于 Go 的采用,我完全赞成。 我关心的是镜像作为本地、独立源代码树的替代品(我们今天称之为供应商)。

@乔肖

今天,供应商不能为您解决这个问题吗?

好吧,我必须承认我的无知,以及对“我应该如何组织我的代码”的一定程度的挫败感。 “自包含源代码树”是指我有一个巨大的 git 存储库,其中包含我的所有代码以及供应商树中的所有代码? 所以我不需要镜像,因为我已经将所有其他代码检查到我自己的仓库中?

关于“我应该如何组织我的代码”,这个页面,如何编写 Go 代码,说明了目录结构,但没有提到 vendoring。

采用语义导入版本控制,其中每个主要版本都有不同的导入路径。 具体来说,导入路径包含模块路径、版本号和模块内特定包的路径。 如果主版本是v0 或 v1 ,则版本号元素必须省略; 否则必须包括在内。

导入为my/thing/sub/pkg 、 my/thing/v2/sub/pkg 和 my/thing/v3/sub/pkg 的包来自模块 my/thing 的主要版本v1 、 v2 和 v3,但是该构建将它们简单地视为三个不同的包。

my/thing/sub/pkg 也可以是 v0。

我跟着游览。 该示例是在 GOPATH 树之外构建的,并且可以正常工作,而在这种情况下dep init barfs。 我认为,我现在与 80 个团队合作,不必拥有一棵巨树真是太好了。 (当我打字的时候,有人带着他们的笔记本电脑和一张皱着眉的脸走了过来,“我有这个非常奇怪的错误......”)

@乔肖

您可以免费或以合理的成本获得基础设施和维护正常运行时间方面的所有出色工作。

我说得不好。 我的意思是说“如果你声称如果你不使用虚荣进口,你过去需要更少的基础设施,那么你就是在无视 github 为你运行的所有基础设施”。 我的意思是指出基础设施仍然存在并且仍然需要运行。 它也可以在未来继续为您运行。

因此,您的担忧将在我认为最有可能的情况下得到解决:一个受资助的机构(目前微软正在形成这种情况)正在托管 zip 文件。

尽管我喜欢单个 Git fetch 的优雅——从用户的角度来看是一个原子操作

vgo get也是一个原子动作,它做的工作更少,性质相同(HTTP 请求)更容易理解。

问题在于,这会在构建项目时增加单点故障 (SPOF)。

那么,我最后的评论在哪里不正确? 因为在我看来,故障点的数量似乎很明显,如果有的话,更少,它们更简单,更容易扩展并且运行成本更低。

在其他语言中,集中式存储库是第二个 SPOF。 对于 Go,每个导入路径都是一个额外的 SPOF(github.com、golang.org、honnef.co、rsc.io 等),增加 SPOF 会降低整体可用性。

我认为这过于简单化了。 如果您将代码托管在出现故障的镜像上,那么您仍然可以开发(github 仍在运行)并且用户仍然可以安装(通过使用不同的镜像)。 当您镜像您的依赖项时,您的一个依赖项的主机发生故障并不重要。 如果您不相信“集中式”社区运行的镜像能够保持正常运行,您可以自己运行或花钱请人来做,对用户没有任何影响,让您完全控制受停机时间影响的程度集中式回购。

实际上提到的所有事情都不是 SPOF,因为您至少仍然会得到降级的操作。 除此之外,实际的镜像操作起来相对简单(因为它们只是无状态的 HTTP 服务器),我不相信可靠性和可用性会显着降低。

FTR,现在你的 git-hostingSPOF:如果你的 git-host 宕机,用户就不能安装,你也不能开发。

当然,运行镜像可能会将其减少到两个,但我认为不需要存在它的基础设施。

而我上面的(措辞不佳)的观点是,它已经发生了:)你只是a)忽略它现在发生并且b)假设它不会在未来发生:)

给出不同的观点:

  • 我们正在使用我们的系统范围的跨语言软件组件管理器(Fedora、Centos 和 RHEL 上的 rpm/dnf)构建许多 Go 项目
  • 它允许我们使用与 vgo 提案相同的技巧,通过在 rpm 层组件命名空间上运行(通常,将 rpm 命名空间级别的项目从 project 重命名为 compat-project-x 以允许区分不兼容的版本相同的导入路径,就像 vgo 一样)。
  • 这些技巧对于帮助构建复杂的 Go 项目绝对有用
  • 尽管它不像在语言级别上那样完整和健壮
  • 我们宁愿将语言约束中继到 rpm/dnf,也不愿在原始代码上添加约束的 rpm/dnf 覆盖。
  • 我们已经厌倦了目前说服 Go 工具查看 Go 项目源代码所需的所有文件系统变通方法。 物理 GOPATH 根本不会错过。

因此,我们对 vgo 感到非常兴奋,并希望它能够尽快部署(我们多年前就需要它)。

话虽如此,我们使用类似 vgo 的工作流程暴露了以下困难。

vgo 提案祝贺自己使 makefile 变得不必要。 这并不完全正确。

  • 许多 Go 项目都爱上了自动生成的 go 文件,并且没有标准的方式来要求重新生成这些文件或表达生成成功所需的内容
  • 太糟糕了,许多项目必须附带预生成的文件(有时组成完整的单独的预生成文件存储库)
  • 这些文件是版本不兼容的重要来源。 当暴露于新的依赖版本时,人类会注意编写不需要每天更改的代码。 自动生成的文件 OTOH 通常与精确的生成环境密切相关,因为生成器工具作者假设您只需根据需要重新生成它们。
  • 要使 vgo 成功,它需要一种方法来识别这些文件,从 .zip 模块中剥离它们,并向 .zip 模块的用户说明如何重新生成它们

当前的“一切都必须在 GOPATH 中”规则加剧了这种情况

  • 当生成依赖于多语言项目在 GOPATH 之外发布的 proto 文件时,一切都不能在 GOPATH 中
  • Go 工具链需要学习读取其 GOPATH 之外的资源
  • Go 工具链需要学习在 GOPATH 之外发布资源,当它们暴露给不一定用 Go 编写的项目时
  • 当前使用 GOPATH 在副本中复制资源的解决方法是版本不兼容的另一个原因
  • 每个人都不会同时复制相同的文件
  • 有时项目会从多个来源复制,创建独一无二的混音。
  • 每个项目都会对自己的副本进行 QA,这使得 Go 项目组合很危险
  • 如果 vgo 版本求解器计算代码版本之间的关系但忽略资源版本之间的关系,它将无法按预期工作。
  • vgo 需要使在 GOPATH 之外发布的东西的私有副本变得不必要,以避免不兼容的模块版本爆炸。

很多项目利用“每个 Go 目录都是一个单独的包”的规则,在单独的子目录中发布具有不兼容或完全损坏的代码的项目

  • 许多其他 Go 项目使用相同的规则来挑选特定的子目录,并在与原始项目单元测试和 QA 不兼容的上下文中使用它们
  • 只要原始项目或项目消费者没有使用原始项目的其他部分,它就“有效”并产生“可复制的构建”。 当这种情况发生时,整个沙堡都会在累积的技术债务下崩溃,
  • 您会看到项目拒绝更新到较新的项目版本,因为这需要清理他们以前对其他项目的滥用。
  • 你有一种奇怪的精神分裂症,Go 项目认为自己不对他们重用的代码库中的错误负责,同时拒绝应用那些相同代码库发布的修复程序(我想我们都是人类,不愉快的现实否认是硬连线的在人脑中)
  • 修复不会传播,并且一切都“很好”,直到您遇到首先需要修复的错误或因挑选樱桃而无效的单元测试。
  • 将项目的安全状态与供应商目录中相同项目的安全状态进行比较的自动安全检查器将有一个现场日
  • 项目越大,就越有可能在其供应商中隐藏这种滴答作响的炸弹
  • 通过在 zip 模块中明确定义项目边界,vgo 将结束这种做法
  • 但是它也需要对许多 Go 代码库进行重大清理
  • 要使 vgo 成功,它需要提供工具来帮助现有的 Go 项目重构并将其代码库拆分为从软件兼容性角度来看有意义的模块。 否则使用 vgo 的项目要么产生不可调和的约束,要么只是锁定现状(这适用于现有代码,但从代码演化的角度来看是可怕的)。

当前将整个 Go 代码库暴露在单个 GOPATH 树中且项目之间几乎没有限制的做法已经从软件工程 POW 中产生了有害的副作用

  • 代码没有提交到具有技术意义的项目中,而是提交给代码作者最方便的仓库(他有权访问的仓库)
  • Go 项目相互流血,一个项目的一部分取决于另一个项目的一部分,而另一个项目的一部分取决于原始项目
  • 有效地产生锁定版本约束图,其中您不能移动一个部分而不移动所有其他部分
  • 没有人有资源在一次操作中改变所有其他人
  • 项目陷入僵局并停止进展
  • 使用具有明确边界的模块并尝试使模块间版本约束显式化将暴露这些情况
  • 这将非常不受欢迎(即使今天存在问题,隐藏在供应商的面纱下)
  • 很多时候,这不仅仅是分离子目录的问题:问题项目依赖关系可能出现在 A 和 D 级别的 A/B/C/D 目录树中,但不会出现在 B 和 C 中。
  • 为了使 vgo 成功,它需要提供工具来帮助现有的 Go 项目重构并将其代码库拆分为遵循依赖关系图线的单独模块

Testfing/fixtures、example 和 testdata 完全是另一种蠕虫,它们有自己的依赖和依赖版本需求

  • 它们需要在单独的模块中以一种或另一种方式隔离,否则它们的需求会毒化求解器版本分辨率
  • 如果您忽略他们的需求,您实际上没有决定任何人,但原始项目将运行单元测试
  • 当您允许版本升级时,这是非常危险的。 问题会发生。 你需要检测它们
  • 集成测试不仅依赖于其他代码,而且依赖于特定环境,只有原始项目才能复制(例如,要与之通信的特定服务器或网站),可能需要特定隔离

我可能忘记了更多的事情,但这些是最关键的。

最后:

  • Go 项目不是孤立存在的
  • 一些 Go 项目将连接器发布到其他语言
  • 其他语言的一些项目发布 Go 连接器
  • 这使得以与其他语言不同的方式处理 semver 成为一个问题。
  • 当多语言项目发布时,除了 Go 部分更新到最新的次要版本之外,您无法将所有内容都更新到最新的次要版本。
  • 对于运营管理战俘来说,那将是地狱。
  • 自动升级到最新的子版本通过让每个人都清理旧版本(好处是技术性的,机制是社会性的),在简化软件约束图方面发挥了巨大作用。
  • 高保真重建类似于像素完美的网站。 它们看起来显然是一个好主意,它们奖励了原作者的自我,但对于任何实际尝试使用结果的人来说,它们都是 PITA,因为它们不可进化且不适应本地(非原创)环境。

在代理服务器上:

为了便于调试,$GOPROXY 甚至可以是指向本地树的 file:/// URL。

请使其与 https:/// URL 的 file:/// 一起工作,该 URL 指向包含 zip 形式的第三方模块的目录(并排除其他所有内容)

这是一个实体协调依赖于相同第三方项目列表的子集的多个开发团队的工作的最简单方法:让 QA / 法律 / 安全人员负责审查“良好”的第三方版本并部署它们在一个公共目录中,让其他人的工作依赖于这个公共目录中可用的模块。

这样,您就可以确定没有人开始研究与其他人的工作不兼容的版本,并且没有人停留在另一个团队已经确定的错误或危险的版本上,并且您的开发站不会一直等待相同副本的下载的软件已经在本地可用。

@Merovius我不同意,但我认为我们偏离了主题,不想让问题讨论陷入困境。 不过,我很高兴在不同的媒体上跟进。 (我的电子邮件在我的 github 个人资料中,我是 Gophers 松弛的 joeshaw。)

https://github.com/golang/go/issues/24301#issuecomment -374882685, @tpng

哪个时区用于伪版本(v0.0.0-yyyymmddhhmmss-commit)中的时间戳?

正如您在编辑中指出的那样,UTC。 但还要注意,您永远不必输入这些内容。 您只需键入一个 git commit hash(前缀),vgo 就会计算并替换正确的伪版本。

https://github.com/golang/go/issues/24301#issuecomment -374907338, @AlexRouSg

你会解决 C 依赖关系吗?

https://github.com/golang/go/issues/24301#issuecomment -376606788, @stevvooe

非 Go 资源会发生什么,例如 protobufs 或 c 文件?

https://github.com/golang/go/issues/24301#issuecomment -377186949,@nim-nim:

vgo 提案祝贺自己使 makefile 变得不必要。 这并不完全正确。 [讨论生成的代码。]

非 Go 开发仍然是go命令的非目标,因此不会支持管理 C 库等,也不会明确支持协议缓冲区。

也就是说,我们当然明白在 Go 中使用协议缓冲区太难了,我们希望看到单独解决这个问题。

至于更普遍的生成代码,真正的跨语言构建系统是答案,特别是因为我们不希望每个用户都需要安装正确的生成器。 作者最好运行生成器并检查结果。

https://github.com/golang/go/issues/24301#issuecomment -375248753, @mrkanister

我知道默认分支可以从 master 更改为 v2,但这仍然让我有任务在每次发布新的主要版本时更新它。 就个人而言,我宁愿拥有一个 master 和一个 v1 分支,但我不确定这是否适合该提案。

正如@AlexRouSg和其他人指出的那样,您可以这样做。 我只是想确认他们的答案。 我也会将此添加到常见问题解答中。

https://github.com/golang/go/issues/24301#issuecomment -375989173, @aarondl

尽管现在可以使用,但这是有意的吗?

绝对没错。

@jamiethermo ,非常感谢您对 Perforce 和不同目​​录中的分支的评论。 我已经忘记了这个功能,但也许这让我确信允许在 vgo 中很重要。

https://github.com/golang/go/issues/24301#issuecomment -376925845 开始就供应商与代理进行了很好的讨论。 这里显然有两组不同的关注点。

正如@joeshaw所写,开源开发人员往往希望避免依赖基础设施,因此他们需要供应商。 为了确认,我们将以有限的形式保持该工作(仅在您运行 go 命令的整个目标模块的顶层的供应商目录)。

企业开发人员依赖基础设施没有问题——这只是另一个成本——特别是如果它带来了更大的成本降低,比如不在每个 repo 中复制他们所有的供应商代码,并且必须花时间保持所有同步。 基本上,我们交谈过的每家公司都想要代理/镜像,而不是像@jamiethermo 所要求的那样出售。 我们将确保它也有效。

我们也非常希望建立一个开发人员有理由信任和依赖的共享镜像网络,这样所有开源开发人员都不会觉得他们必须供应商。 但那是以后了。 首先vgo需要变成go。

https://github.com/golang/go/issues/24301#issuecomment -377220411,@nim-nim:

请使其与 https:/// URL 的 file:/// 一起工作,该 URL 指向包含 zip 形式的第三方模块的目录(并排除其他所有内容)

我不确定您说“排除其他所有内容”的要求是什么。 如果设置了 $GOPROXY,vgo 会询问该代理。 它永远不会回到其他任何地方。 该代理可以由静态文件树提供服务,该文件树主要是 zip 格式的第三方模块。 不过,文件树还必须包含一些其他元数据文件,用于导航和查找。 那些额外的文件是不可避免的,因为 HTTP 没有给我们一个标准的方法来做像目录列表这样的事情。

https://github.com/golang/go/issues/24301#issuecomment -377186949,@nim-nim:

哇,好长的评论。 我想我同意你写的大部分内容。 我想回复您帖子中的最后一个项目符号:

  • 高保真重建类似于像素完美的网站。 它们看起来显然是一个好主意,它们奖励了原作者的自我,但对于任何实际尝试使用结果的人来说,它们都是 PITA,因为它们不可进化且不适应本地(非原创)环境。

我想我会争辩说锁定文件就像像素完美的网站。 相比之下,高保真构建会随着上下文的变化而优雅地降级。 默认情况下,构建将使用 B 1.2 和 C 1.4。 但是,如果它是需要 B 1.3 的较大构建的一部分,那很好,它会与 B 1.3 相处,但在没有具体升级要求的情况下保持 C 1.4(即使存在更新的版本)。 所以真正的高保真构建是两全其美的:尽可能忠实于原作,但在不可能的情况下不坚持像素完美。

https://github.com/golang/go/issues/24301#issuecomment -375415904, @flibustenet

@rsc我想请您更准确地了解路线图以及我们现在应该做什么。
它会遵循 Go 政策并在 3 个月(现在是 2 个月)冻结 vgo 吗?

Vgo 是一个原型,永远不会单独发布。 它不受冻结或其他任何影响。 但这个提议是将想法和大部分代码移到主 cmd/go 中。 作为发布的一部分,cmd/go 肯定会被冻结。 因为它是可选的,并且因为 vgo 特定的代码与 go 命令的其余操作完全隔离,所以在 vgo 特定的部分上工作的风险相当低,我可以看到其中的一部分持续几个星期进入冻结状态。 现在我专注于提案讨论和调整。 一旦提案看起来状态良好且没有重大问题,那么我们将转向将代码移动到 cmd/go 中。

我们现在应该带着朝圣者的指挥棒去要求每个 libs 维护者添加一个 go.mod 文件,还是应该等待提案被正式接受(以确保名称和语法不会改变)?

我认为 go.mod 语法可能会改变(观看这个问题)。 但正如我在提案中指出的那样,我们将永远接受旧语法,而 vgo 只会更新现有文件,所以这不是什么大问题。 也就是说,在代码进入 cmd/go 的开发副本之前,我不会尝试将 PR 发送到您可以找到的每个库。

https://github.com/golang/go/issues/24301#issuecomment -376640804, @pbx0

使用 vgo 是否容易(今天)确定您在构建中是否有超过 1 个版本的包?

今天最简单的事情是构建二进制文件,然后在其上运行 goversion -m(参见 https://research.swtch.com/vgo-repro)。 当我们有一个更通用的模块感知 go 列表时,它应该能够在不首先构建二进制文件的情况下做同样的事情。

https://github.com/golang/go/issues/24301#issuecomment -376236546, @buro9

[我可以进行向后不兼容的安全更改,例如 microcosm-cc/ bluemonday@a5d7ef6? ]

正如@Merovius所说,如果我们要采用Go 1 兼容性指南,那么明确允许安全更改而不会影响主要版本。

也就是说,即使您必须为安全做一些事情,您仍然应该努力将干扰降到最低。 可以说,您链接到的提交比必要的更具破坏性,并且在未来的情况下,我鼓励您从“如何在消除安全问题的同时尽可能少地破坏客户端? "

例如,不要删除函数。 相反,只有在调用不当时才使函数 panic 或 log.Fatal。

在这种情况下,我不会删除 AllowDocType,而是保留它并且肯定会继续接受 AllowDocType(false),因为这是安全设置。 如果有人为您的库编写了一个包装器,可能是作为带有 -allowdoctype 标志的命令行程序,那么至少使用没有该标志的程序将继续工作。

除此之外,似乎担心的是文档类型完全未经检查,但我可能会进行最少的检查以保持最常见的用途正常工作,然后保守地拒绝其他用途。 例如,至少,我会一直允许并且可能还会打扰允许 ..> 带有没有 &#<>\ 字符的带引号的字符串。

https://github.com/golang/go/issues/24301#issuecomment -375090551, @TocarIP

[担心没有及时获得更新。]

我真的认为相反的情况会发生,程序可能会更新,因为在最小版本选择中,一个依赖项_不可能_阻止整个构建更新到更新版本。

@Meroviushttps://github.com/golang/go/issues/24301#issuecomment -375992900 中写的内容对我来说听起来完全正确。 关键是您只有在请求更新时才能获得更新,因此(可能)只有在您期望更新并准备好进行测试、调试等时才会中断。 您确实必须要求它们,但不会比其他具有锁定文件的系统更频繁。 我们还希望更容易显示警告,例如“您正在使用已弃用/不安全的版本进行构建”。 但重要的是不要仅仅将更新作为非更新操作的副作用。

也添加到常见问题解答中。

感谢大家迄今为止的精彩讨论并回答彼此的问题。 很多人的回答真的很棒,但特别感谢@Merovius和@kardianos。 我已经更新了常见问题解答https://github.com/golang/go/issues/24301#issuecomment -371228664 和讨论摘要https://github.com/golang/go/issues/24301#issuecomment -371228742。 有三个重要问题尚未回答(他们在摘要中说 TODO),我将在接下来的工作中解决这些问题。 :-)

@rsc,# 24057有一些关于使用 tar 代替 zip 的讨论。

https://github.com/golang/go/issues/24301#issuecomment -375106068, @leonklingele

如果我们今天所知道的对 go get 的支持将被弃用并最终被删除,那么推荐的获取和安装(未标记)Go 二进制文件的方法是什么?

它仍然会去获取。 如果二进制文件的 repo 未标记,go get 将使用最新的提交。 但实际上应该鼓励发布二进制文件的人将包含的 repos 标记为与包含库(或混合)的 repos 相同。

如果不推荐使用 $GOPATH,这些二进制文件将安装到哪里?

您不再需要在 $GOPATH 中工作,但代码仍会写入 $GOPATH 中列出的第一个目录 - 它是源缓存,使用 vgo 后请参阅 $GOPATH/src/v。 二进制文件安装到 $GOPATH/bin。 在几个版本之前,您不必设置 $GOPATH - 它有一个默认值 $HOME/go。 所以应该发生的是,开发人员不再担心设置 $GOPATH 甚至不知道它是什么,他们只是知道他们的二进制文件在 $HOME/go/bin 中。 他们可以使用 $GOBIN 覆盖该位置。

@dsnet ,谢谢我在讨论摘要中添加了一个链接。 让我们继续讨论。

如果设置了 $GOPROXY,vgo 会询问该代理。 它永远不会回到其他任何地方。 该代理可以由静态文件树提供服务,该文件树主要是 zip 格式的第三方模块。 不过,文件树还必须包含一些其他元数据文件,用于导航和查找。 那些额外的文件是不可避免的,因为 HTTP 没有给我们一个标准的方法来做像目录列表这样的事情。

只要原始模块以 zip 格式保存,消除任何篡改它们的诱惑,并且索引器是健壮且轻量级的,就可以了。

尽管列表约束不适用于文件,但像 lftp 这样的实用程序已经能够列出 http 目录很长时间了(如果它在主要的 http 服务器上工作,它是否是非标准的并不重要)。 因此,对于不希望投资基础设施的小型实体,无索引操作可能是可能的,也是更可取的。 yum/dnf/zipper 也依赖于自定义索引,并且在某些组织中获取共享目录的索引并不总是像您想象的那么简单。

正如@joeshaw所写,开源开发人员往往希望避免依赖基础设施,因此他们想要供应商

并非如此,开源开发者大多希望整个过程是公开透明的,而不是必须依赖别人的好意。 所以 infra 完全没问题,只要它本身是开源的并且在本地部署简单且便宜。 依赖像 github 这样的大型黑盒专有网站显然不属于这一类,但这与 infra 不同。 开源的人比其他人早了几十年做镜像。 他们不接受的是关闭和设置镜像的成本高(在开源术语中,成本是以人力时间衡量的)

供应商开放的简单和廉价的性质受到赞赏,供应商过程本身以及它鼓励代码库在第三方代码的过时版本上逐渐僵化的方式,而不是那么多。

企业开发人员对基础设施的依赖没有问题——这只是另一项成本

在 Google 工作一定很棒 :(。除了少数拥有大型 Go 运营的企业,投资 Go 基础设施是轻而易举的事,其他所有人都必须经过漫长而乏味的审批流程, 如果只是为了证明花钱请人来研究这个问题是合理的。所以任何基础设施成本都会减少 Go 的覆盖范围并阻止它被新结构采用。

企业就像开源者,他们关心的是便宜和容易。 他们过去根本不关心开放,但现在这种情况正在慢慢改变,因为他们意识到它与廉价相关(这让传统的企业供应商感到沮丧,这些供应商过去专注于昂贵的黑盒解决方案和昂贵的咨询来帮助部署)。

将内部 IT 外包给最低出价者的企业肯定会坚持使用镜像,因为他们绝对不希望他们的廉价开发人员从互联网上下载那些开发人员无法理解的损坏或危险代码。 他们将支付人力和工具来扫描本地镜像内容以查找问题,并强制内部 IT 专门使用它。

我们也非常希望建立一个开发人员有理由信任和依赖的共享镜像网络,这样所有开源开发人员都不会觉得他们必须供应商。

只需在某处发布包含模块的索引目录的参考副本。 忘记任何需要特定 Web 服务器配置的类似代理的设置。 这就是开源者所做的,他们可以毫不费力地让自己镜像。 只要镜像只是复制目录的内容并且不需要特定的网络服务器配置,就有很多组织愿意镜像。

至于更普遍的生成代码,真正的跨语言构建系统就是答案,

你和我一样知道这永远不会发生,总有人想发明一种新的语言来做自己的事情。 这是稻草人的论点。

这并不妨碍 Go 规范化启动任何项目特定进程生成代码的标准命令,并严格指导它可以做什么和不能做什么(通常它不应该执行标准 Go 命令已经涵盖的任何事情,因为这些命令应该已经很好了)。

特别是因为我们不希望每个用户都需要安装正确的生成器。 作者最好运行生成器并检查结果。

这将需要对现有生成器的实现方式进行重大重新思考,因为现在他们根本不关心版本可移植性,并期望软件环境在生成之前被冻结。 生成的直接效果是使任何以后的版本更改而不会重新生成危险。 结果是否由人类检查并不重要,因为人类只会检查原始版本集。 vgo 依赖于能够进行更高版本的更改。

所以vgo迟早要解决再生问题。 稍后意味着等待项目发现在生成代码的情况下 vgo 更新是危险的。

https://github.com/golang/go/issues/24301#issuecomment -374791885, @jimmyfrasche

这也会影响数据库驱动程序和图像格式等在初始化期间向另一个包注册的内容,因为同一包的多个主要版本最终可能会这样做。 我不清楚这会产生什么影响。

是的,假设“程序中只有其中一个”的代码问题是真实的,这是我们所有人都必须解决的问题,以建立新的(更好的)最佳实践和约定。 我不认为这个问题是由 vgo 引入的,而且 vgo 可以说使情况比以前更好。

我知道有些人认为 vgo 应该采用 Dep 的规则,即 1.x 和 2.x 甚至不能同时存在,但这显然不能扩展到我们使用 Go 所针对的大型代码库。 正如vgo-import帖子所示,期望整个大型程序一次从一个 API 升级到另一个 API 是行不通的。 我相信基本上所有其他包管理器都出于相同的原因允许 1.x 和 2.x 一起使用。 货运当然可以。

一般而言,vgo 与 vendoring 相比确实减少了重复。 通过供应商,很容易在一个二进制文件中得到一个给定包的 1.2、1.3 和 1.4,甚至可能没有意识到这一点,甚至可能是 1.2 的三个副本。 至少 vgo 将可能的重复减少到一个 1.x、一个 2.x 等等。

不同包的作者已经需要确保不要尝试注册相同的东西。 例如,expvar 执行 http.Handle("/debug/vars") 并且基本上已经对该路径进行了声明。 我希望我们都同意,像 awesome.io/supervars 这样的第三方包不应该尝试注册相同的路径。 这会在单个包的多个版本之间留下冲突。

如果我们引入 expvar/v2,那么它最终会成为第二个不同的包,就像 awesome.io/supervars,它可能与大型构建中的 expvar 冲突。 但是,与 supervars 不同的是,expvar/v2 与 expvar 属于同一个人或团队,因此这两个包可以协调以共享注册。 这将按如下方式工作。 假设 expvar v1.5.0 是我们决定编写 v2 之前的最后一个,所以 v1.5.0 中有一个 http.Handle。 我们希望 v2 替代 v1,因此我们将 http.Handle 移至 v2.0.0 并在 v2 中添加 API,允许 v1 将其调用转发到 v2。 然后我们将创建使用此转发实现的 v1.6.0。 v1.6.0 不调用http.Handle; 它将其委托给 v2.0.0。 现在 expvar v1.6.0 和 expvar/v2 可以共存,因为我们是这样计划的。 剩下的唯一问题是如果构建使用 expvar v1.5.0 和 expvar/v2 会发生什么? 我们需要确保不会发生这种情况。 我们通过使 expvar/v2 在 v1.6.0(或更高版本)需要 expvar 来做到这一点,即使在那个方向上没有导入,当然 expvar v1.6.0 也需要在 v2.0.0 或更高版本的 expvar/v2 来调用其 API . 这个需求周期让我们确保 v1.5.0 和 v2 永远不会混合。 规划这种跨主要版本的协调正是最小版本选择允许需求图中循环的原因。

“只有一个主要版本”规则与需求图中的允许循环相结合,为作者提供了他们需要的工具来管理其模块的不同主要版本之间的单件的适当协调(以及单件所有权的迁移)。 我们无法消除问题,但我们可以为作者提供解决问题所需的工具。

我知道协议缓冲区尤其存在注册问题,并且这些问题因协议 .pb.go 文件被传递并复制到项目中的非常特殊的方式而加剧。 这又是一个主要独立于 vgo 的东西。 我们应该修复它,但可能通过更改 Go 协议缓冲区的使用方式,而不是 vgo。

https://github.com/golang/go/issues/24301#issuecomment -374739116, @ChrisHines

我最担心的是 pre-vgo 世界和 vgo 世界之间的迁移路径会很糟糕。 [更多详情]

我很高兴这是对该提案的第一个评论。 显然,正确处理是最重要的事情。

从旧的 go get 和无数的供应商工具到新的模块系统的过渡必须非常顺利地运行。 我最近一直在思考这将如何运作。

最初的提议允许开发人员选择将模块的版本 2 放在名为 v2/ 的 repo 子目录中。 执行该选项将允许开发人员创建一个使用语义导入版本控制的存储库,与模块兼容,并且还与旧的“go get”向后兼容。 描述此选项的帖子承认绝大多数项目都不想行使此选项,这很好。 如果项目已经是 v2 或更高版本,则仅出于兼容性需要。 同时,我低估了 v2 或更高版本的广泛使用的大型项目的数量。 例如:

  • github.com/godbus/dbus 为 v4.1.0(由 462 个包导入)。
  • github.com/coreos/etcd/clientv3 为 v3.3.3(由 799 个包导入)。
  • k8s.io/client-go/kubernetes 为 v6.0.0(由 1928 包导入)。

为了避免破坏他们的客户,这些项目需要移动到一个子目录,直到可以假定所有客户都使用模块,然后再回到根目录。 要问的太多了。

另一种使用 git 子模块玩技巧的选择。 旧的 go get 命令更喜欢名为“go1”的标签或分支而不是 repo 的默认分支,因此想要实现平滑过渡的项目可以在仅设置了“v2”子目录的“go1”分支上创建提交作为一个 git 子模块,指向真正的 repo 根目录。 当“go get”检查 go1 分支并填充子模块时,它将获得正确的文件树布局。 不过,这有点糟糕,每次发布新版本时都需要更新子模块指针。

这两者都有一个不幸的结果,即作者必须采取措施避免破坏他们的用户,即使这样也仅限于新版本。 相反,我们希望用户的代码即使在作者没有额外工作的情况下也能继续工作,理想情况下即使是旧版本也是如此。

但这些是我能想到的唯一方法,在我们过渡到模块世界时保持不变,旧的去工作。 如果不是这些,那么替代方法是修改旧的 go get,这实际上意味着修改旧的 go build。

从根本上说,有两种不同的导入路径约定:旧的没有主版本,新的有 v2 或更高版本的主版本。 旧树中的代码可能使用旧约定,而新树中的代码(位于包含 go.mod 文件的目录下)可能使用新约定。 我们需要一种方法让这两个约定在过渡期间重叠。 如果我们教 old go get 少量关于语义导入版本控制,那么我们可以显着增加重叠量。


提议的更改:将“新”代码定义为在同一目录或父目录中具有 go.mod 文件的代码。 旧的 go get 必须像往常一样继续下载代码。 我建议“开始构建”步骤在“新”代码中调整其对导入的处理。 具体来说,如果新代码中的导入显示 x/y/v2/z 但 x/y/v2/z 不存在且 x/y/go.mod 显示“模块 x/y/v2”,则 go build 将读取而是导入为 x/y/z。 我们会将此更新作为 Go 1.9 和 Go 1.10 的一个点发布。

更新:复制到 #25069。


这种变化应该使模块不感知包可以使用更新版本的模块感知包,无论这些包选择“主要分支”还是“主要子目录”方法来构建它们的存储库。 他们将不再被迫进入子目录方法,但它仍然是一个可行的选择。 并且使用旧版 Go 版本的开发人员仍然能够构建代码(至少在更新到单点版本之后)。

@rsc我一直在试图弄清楚我们如何才能使向 vgo 的过渡也能正常工作,我得出的结论与您在回复中提出的相同,您的建议与我提出的最佳方法相匹配靠我自己。 我喜欢你提议的改变。

https://github.com/golang/go/issues/24301#issuecomment -377527249, @rsc

然后我们将创建使用此转发实现的 v1.6.0。 v1.6.0 不调用http.Handle; 它将其委托给 v2.0.0。 现在 expvar v1.6.0 和 expvar/v2 可以共存,因为我们是这样计划的。

这听起来比它更容易。 实际上,在大多数情况下,这意味着 v1.6.0 必须以 v2 包装器的形式完全重写 v1 _(对 http.Handle 的转发调用将导致注册另一个处理程序 - 来自 v2 的处理程序 - 这反过来意味着所有相关代码也应该来自 v2 以正确与注册的处理程序交互)_。

随着 v2 的发展,这很可能会改变有关 v1 行为的微妙细节,尤其是随着时间的推移。 即使我们能够补偿这些细微的细节变化并在 v1.6.x 中足够好地模拟 v1 - 仍然有很多额外的工作,并且很可能会在未来支持 v1 分支(我的意思是 v1.5.0 的继任者这里)没有意义。

@powerman ,我绝对不是说这是微不足道的。 而且您只需要在 v1 和 v2 争夺一些共享资源(如 http 注册)的范围内进行协调。 但是参与这个打包生态系统的开发者绝对需要明白,他们的包的 v1 和 v2 将需要在大型程序中共存。 许多包不需要任何工作——例如,yaml 和 blackfriday 都在 v2 上,它们与 v1 完全不同,但没有要争夺的共享状态,因此不需要显式协调——但其他包需要。

@powerman @rsc
我正在开发一个 GUI 包,这意味着由于使用“主”线程,我什至不能拥有 2 个以上的实例。 因此,从最坏的单身情况来看,这就是我决定要做的。

  • 只有 v0/v1 版本,因此无法导入 2+ 版本

  • 在它自己的 api 版本文件夹中有公共代码,例如 v1/v2 假设 vgo 允许,或者可能是 api1/api2。

  • 然后,这些公共 api 包将依赖于内部包,因此不必在 v2 上重写,而是随着包的增长而滚动重写,并且更容易处理。

https://github.com/golang/go/issues/24301#issuecomment -377529520 中,提议的更改将“新”代码定义为在同一目录或父目录中具有 go.mod 文件的代码。 例如,这是否包括通过从 Gopkg.toml 读取依赖项创建的“合成”go.mod 文件?

@zeebo ,是的。 如果你的树中有一个 go.mod 文件,那么假设你的代码实际上是用 vgo 构建的。 如果没有,那么 rm go.mod (或者至少不要将它签入你的仓库,其他人可能会找到它)。

@AlexRouSg ,你的 GUI 包计划对我来说很有意义。

@rsc嗯.. 我不确定我是否理解,如果我不清楚,我很抱歉。 文件树中只有 Gopkg.toml 的包是否算作定义的“新”?

@rsc

至于更普遍的生成代码,真正的跨语言构建系统是答案,特别是因为我们不希望每个用户都需要安装正确的生成器。 作者最好运行生成器并检查结果。

我们设法通过将 protobuf 映射到 GOPATH 来解决这个问题。 是的,我们有这样的临时用户不需要工具来更新,但对于那些修改和重新生成 protobufs 的人来说,protobuild 中的解决方案非常有效。

这里的答案相当令人失望。 寻找一个不存在的新构建系统只是一个非答案。 这里的现实是我们不会重建这些构建系统,我们将继续使用有效的方法,避免采用新的 vgo 系统。

vgo是否只是为那些喜欢并采用 GOPATH 并解决其问题的人宣布破产?

@zeebo ,不,拥有 Gopkg.toml 不算是新的; 这里的“新”意味着预期使用 vgo 样式(语义导入版本控制)导入。

@stevvooe

我们设法通过将 protobuf 映射到 GOPATH 来解决这个问题。 ...
vgo 是否只是为那些喜欢和采用 GOPATH 并解决其问题的人宣布破产?

我没有看过你的 protobuild,但总的来说,是的,我们正在转向非 GOPATH 模型,GOPATH 可能启用的一些技巧将被抛在后面。 例如,GOPATH 使原始的 godep 能够在没有 vendoring 支持的情况下模拟 vendoring。 那再也不可能了。 乍一看,protobuild 似乎是基于这样的假设:它可以将文件 (pb.go) 放入您不拥有的其他包中。 不再支持这种全局操作,不。 我非常认真和真诚地希望确保 protobuf 得到很好的支持,与 vgo 分开。 @neild可能有兴趣听取建议,但可能不在这个问题上。

@stevvooehttps://github.com/golang/go/issues/24301#issuecomment -377602765 中给出了@rsc的评论我已经交叉引用了https://github.com/golang/protobuf/issues/526以防万一该问题最终涵盖了vgo角度。 如果事情最终在其他地方得到处理,我相信@dsnet等人会为我们指明方向。

注意:我没有仔细查看之前的评论,似乎问题通过不同的方法解决了。 下面是我的想法。

只是一个想法。

如何让vgo get感知特定标签,如 vgo-v1-lock?
当存储库有标签时,它可能会忽略其他版本标签,并固定到该标签。

因此,当存储库将 v2.1.3 标记为最新版本时,
而且所有者也将 vgo-v1-lock 标记推送到标记为 v2.1.3 的同一提交
它可以写 go.mod

require (
    "github.com/owner/repo" vgo-v1-lock
)

即使vgo get -u也不应该更新,直到存储库所有者更改或删除标签。
它可以使大型存储库更容易准备移动。

当图书馆作者准备好时,作者可以向用户宣布
他们可以通过将“/v2”放入其导入路径来手动更新。

我们如何处理需要修补深度依赖关系的情况(例如应用原作者尚未在标签中发布的 CVE 修复)。 似乎供应商策略可以处理此问题,因为您可以将补丁应用于原始作者版本。 不明白 vgo 是如何处理这个问题的。

@chirino您可以使用 go.mod 文件中的 replace 指令指向已修补的包。

@rsc

乍一看,protobuild 似乎是基于这样的假设:它可以将文件 (pb.go) 放入您不拥有的其他包中。

这根本不是该项目所做的。 它从GOPATH和供应商目录构建导入路径。 然后,您的项目中的任何 protobuf 文件都将使用该导入路径生成。 它还可以将导入映射到特定的 Go 包。

这样做的好处是它允许在叶子项目中生成依赖于依赖项中定义的其他 protobuf 的 protobuf,而无需重新生成所有内容。 GOPATH 有效地成为 protobuf 文件的导入路径。

这个提议的最大问题是,我们完全失去了在文件系统上解决与 Go 包相关的项目中文件的能力。 大多数包装系统都有能力做到这一点,尽管它们很难做到。 GOPATH 的独特之处在于它很容易做到这一点。

@stevvooe对不起,但我想我仍然对 protobuild 的作用感到困惑。 您能否提交一个新问题“x/vgo:与 protobuild 不兼容”并给出一个简单的工作示例,说明目前存在的文件树,protobuild 添加到树中的内容,以及为什么这不适用于 vgo? 谢谢。

如果模块名称必须更改(丢失域、所有权变更、商标争议等)怎么办?

@jimmyfrasche

作为用户:
然后作为临时修复,您可以编辑 go.mod 文件以用新模块替换旧模块,同时保持相同的导入路径。 https://research.swtch.com/vgo-tour

但从长远来看,您可能希望更改所有导入路径并编辑 go.mod 文件以使用新模块。 基本上,无论有没有 vgo,你都必须做同样的事情。

作为包维护者:
只需更新 go.mod 文件以更改模块导入路径并告诉您的用户更改。

@jimmyfrasche

如果模块名称必须更改(丢失域、所有权变更、商标争议等)怎么办?

这些是真实存在的问题,vgo 提案并未试图直接解决这些问题,但显然我们最终应该解决这些问题。 代码消失的答案是拥有缓存代理(镜像)以及信任它们的理由; 那是未来的工作。 代码移动的答案是添加模块或包重定向的明确概念,就像类型别名是类型重定向一样; 这也是未来的工作。

代码消失的答案是拥有缓存代理(镜像)

IMO 真的是为企业服务的。 大多数小公司和其他公司都可以很好地进行供应并将所有依赖项提交到同一个仓库中

为我在上面的评论中提到的兼容性提交了#24916。
还提交了 #24915 建议直接使用 git 等,而不是坚持使用 HTTPS 访问。 似乎很明显,代码托管设置还没有为仅 API 做好准备。

使用计划中的 vgo get 命令在 mod 文件中创建一致性的小建议

在“vgo-tour”文档中, vgo get命令显示为:

vgo get rsc.io/[email protected]

在 mod 文件中镜像这种格式怎么样? 例如:

module "github.com/you/hello"
require (
    "golang.org/x/text" v0.0.0-20180208041248-4e4a3210bb54
    "rsc.io/quote" v1.5.2
)

可能很简单:

module "github.com/you/hello"
require (
    "golang.org/x/[email protected]"
    "rsc.io/[email protected]"
)
  • 提高与命令行的一致性
  • 单个标识符完全定义项目
  • 更好的结构来支持 mod 文件中定义的需要多个版本包标识符的操作

寻求更清楚地了解此提案如何处理“仅二进制”包分发。

二进制库版本控制/分发似乎没有出现在 vgo 周围的任何描述文档中。 是否需要更仔细地看待这个问题?

今天的工作方式,如果我可以使用普通的git工具, go get就可以正常工作。 不管是私有的 Github 仓库还是我自己的 Git 服务器。 我很喜欢。

据我了解,要继续这样工作是不可能的。 真的吗? 如果是,是否可以保留使用本地安装的git二进制文件来检查代码的选项? (如果它使用显式 CLI 标志)

@korya请查看最近提交的问题https://github.com/golang/go/issues/24915

@sdwarwick ,重新https://github.com/golang/go/issues/24301#issuecomment -382791513(go.mod 语法),请参阅 #24119。

re https://github.com/golang/go/issues/24301#issuecomment -382793364,我可能不明白你的意思,但是 go get 从来不支持二进制包,我们也没有计划添加支持。 枚举一个人可能需要的所有可能的不同二进制文件太难了。 更好地要求源并且能够在依赖项或编译器更改时重新编译。

@AlexRouSg是的。 “go get”不支持这些(但“go build”确实支持它们作为构建类型),这就是@rsc所指的。 这些类型的包的分发必须在“go get”工具的外部完成,因此对于这个新提案来说可能是一样的。

我预计当前对仅二进制包的支持将继续像以往一样糟糕。 我不会特意删除它。

我再次更新了讨论摘要。 我之前还提交了 #25069 以获取关于旧 cmd/go 中最小模块意识的建议。

@kybin ,重新https://github.com/golang/go/issues/24301#issuecomment -377662150 和vgo-v1-lock标签,我看到了吸引力,但为此添加一个特殊情况意味着在整个过程中添加更多特殊情况模块的其余部分支持。 在这种情况下,我认为收益与成本不成正比。 人们已经可以使用伪版本来获得类似的效果。 我还担心标签会移动和/或人们不会正确尊重向后兼容性(例如,将 vgo1-v1-lock 从 v2.3.4 移动到 v3.0.0 只是为了避免语义导入版本控制)。 所以总的来说,我认为我们可能不应该这样做。

我认为是时候标记这个提议被接受了。

对于它是否会以某种形式被接受,从来没有任何悬念。 相反,这里讨论的目标是制定确切的形式,以确定我们应该调整什么。 正如我在文中所写:

我知道 Go 团队和我看不到它存在的问题,因为 Go 开发人员以许多我们不知道的巧妙方式使用 Go。 提案反馈过程的目标是让我们所有人共同努力,识别和解决当前提案中的问题,以确保在未来的 Go 版本中发布的最终实现对尽可能多的开发人员都有效。 请指出提案讨论问题上的问题。 随着反馈的到来,我将保持讨论摘要和常见问题解答的更新。

截至目前,讨论摘要和常见问题解答是最新的。 此处和问题外的讨论促成了以下重要变化:

  • 旧 cmd/go 中的最小模块意识,#25069。
  • 恢复最小的供应商支持,#25073。
  • 恢复对直接 Git 访问的支持,#24915。
  • 在 go.mod 中去掉引号,#24641。
  • 更好地支持 gopkg.in,#24099,其他。
  • 支持通过分支标识符命名提交,#24045。

它还引发了关于可能更改 go.mod 文件名和语法的讨论,但唯一导致的更改是删除引号。

讨论已经平息,而且它已经变得足够长,以至于在 GitHub 上加载非常痛苦(显然 100 条评论太多了!)。 通过将提案标记为已接受,我们可以专注于使用 vgo 并准备好将其作为“预览”包含在 Go 1.11 中,并且我们可以转移到 GitHub 可以更快加载的问题。

当然,还有更多的错误需要发现和修复,还有更多的设计调整需要进行。 这些细节的讨论可以针对特定于这些细节的新问题进行。 请使用“x/vgo:”前缀和vgo 里程碑

感谢大家。

@rsc现在尝试 vgo 的方法是什么? 我们应该获取并构建 github.com/golang/vgo 还是 github.com/golang/go?

@ngrilly继续使用go get -u golang.org/x/vgo

@rsc感谢您为此提案付出的辛勤工作和时间。 我真的希望我们最终能够达到一个关于 Go 依赖管理的好故事的地步。

我认为是时候标记这个提议被接受了。

我认为提交提案的个人在准备好被接受时声明它是不合适的。 我认为那些提交提案的人,尤其是一个如此庞大和固执己见的提案,不应该对是否被接受有发言权。 他们提交的提案传达了他们的偏见。 另一方面,我认为如果作者在提出提案后改变了主意,他们肯定应该在拒绝该提案方面有发言权。

在这个时候,感觉就像在vgo内部围绕技术决策存在深刻的分歧,我担心这些分歧会分裂生态系统_和_社区。 考虑到这一点,我认为我们应该留出更多时间来完成竞争提案并获得关注。 一旦发生这种情况,我们需要一个由多家公司和社区的代表组成的_中立_政党,以促进讨论和最终决策。

我越来越担心 Go 领导层的很大一部分已经与 Go 背后的社区(包括其他使用它的公司)过于孤立,因此语言和生态系统开始受到伤害。 这就是为什么我认为我们需要一个中立的团体,其代表反映 Go 编程语言的用户,帮助批准这样的提案。

最终,与行业的大部分人相比,在谷歌内部担任软件工程师会产生不同的观点。 在 Google 内部拥有足够多的核心开发人员并不能促进我们在推动 Go 前进时所需的多样性。

@theckman您的思路似乎是:

  1. 关于围棋的决定是由一个主要由谷歌工程师组成的小团队做出的。
  2. 谷歌工程师与社区的其他成员隔离,因为谷歌的需求非常具体。
  3. 这会导致有利于 Google 视角且不适合其他 Go 用户的决策。
  4. 这可能会伤害和分裂 Go 社区。
  5. 为了解决这个问题,围棋应该采用一个正式的决策过程,由一个反映围棋社区多样性(“民主”)的群体共同做出决定。

理论上,我倾向于喜欢任何“民主”的东西,但实际上:

  • 我不认为 Go 团队做出的决定偏向于 Google 的需求,而牺牲了“小”Go 用户的利益。 作为一家很小的商店(与 Google 完全相反)的开发人员,我觉得 Go 非常适合我们的需求,我知道我周围的其他小团队也很高兴地使用 Go。 就个人而言,Go 团队承认了我对 Go 语言的唯一抱怨(依赖管理、缺乏泛型、冗长的错误处理),我相信他们正在努力解决这个问题。 如果我们有一个“民主”的过程,你能否提供 Go 团队做出的决策的例子,这些决策会有所不同并且更好地服务于“社区的其他人”?

  • 我不相信采用“民主”决策过程会自动解决您提到的问题并消除“分裂”社区的风险。 它可以是对BDFL模型的改进,但它本身并不能保证稳定性。 开源历史以及整个人类历史提供了大量民主社会被分歧和冲突破坏的例子。

@theckman ,虽然@ngrilly试图保持礼貌,但我会说得具体一点:如果您发现任何技术问题,为什么 vgo 提案还没有准备好被接受 - 请尽快告诉我们,就在这里! 如果您认为某些已知问题没有得到充分解决,请告诉我们。 如果没有这样的情况 - 谁会说“是时候接受提案了”有什么区别? 我们有两个月的时间来讨论它,如果您认为这还不够技术原因 - 请告诉我们。

听起来你想无缘无故地在这里添加更多政治,这只会减慢一切,如果我们足够幸运并且不会造成其他伤害的话。 即使您认为这是有原因的——这不是讨论这个问题的合适地方——请改为在邮件列表中开始这个讨论。

抱歉,大家又发表了一篇题外话!

@powerman对不起,我很抱歉并引用了我之前的评论:

目前感觉在 vgo 内部围绕技术决策存在深刻的分歧,我担心这些分歧会破坏生态系统和社区。 考虑到这一点,我认为我们应该留出更多时间来完成竞争提案并获得关注。

我知道还有其他的提议即将到来,我的要求是让 kibosh 接受这个,直到他们有时间为止。 我们已经有一段时间没有进行依赖管理了,所以我认为在允许对其他提案进行最后润色的同时要求短暂停留并不是不合理的。

我不确定在这个问题上是否容易表达清楚,只是因为格式有点受限。 即使那样我也会犹豫,因为我基本上是在复制 WIP 提案的部分内容。 我不想分散这些想法或窃取作者的风头。

然而,知道这些提案正在进行中以及谁在为它们工作,感觉宣布准备就绪是一种积极尝试扼杀那些相互竞争的意见。 由于多年来我作为 Gopher 观察到的模式,我感到有必要提出这些担忧。 我真的希望语言、生态系统和社区能够成功,而这些都是为了这个目标而提出的。

编辑:澄清一下,我所说的短期逗留并不是指两个月或类似的东西。 我的意思是几个星期。

@ngrilly对不起,我不是故意忽略您的评论。 我打算把它写给前面的那个,但最终比我想要的更冗长。

我认为有两个问题。 虽然我觉得有一些事情需要在不同的论坛上讨论这些决定是如何做出的,这就是为什么我在这方面添加了一些背景信息,但我真的想专注于暂时暂停接受这个提案,直到其他提案有有机会公开。

我知道有替代的提议即将到来,我的要求是让 kibosh 接受这个,直到他们有时间为止。 ... [K] 既然这些提案正在进行中并且谁在为它们工作,感觉就像宣布准备就绪一样,是一种扼杀那些相互竞争的意见的积极尝试。

我向你保证不是。 我已经很清楚这里的时间线了。 我从 2 月中旬开始的第一篇文章说目标是将 vgo 提案集成到 Go 1.11 中; 发展冻结临近。 我对正在进行的任何其他提案或正在处理这些提案的人一无所知。 这对我来说是个新闻。 如果人们想参与这个提案或提出反提案,这个提案已经开放了一个半月,所以有点晚了。

需要明确的是,尽管 Golang Weekly 和其他人报道过,我并没有将提案标记为接受。 我只是说我认为是时候这样做了。 也就是说,我没有使用 Proposal-Accepted 标签,正是因为我想首先检查是否存在普遍共识。 至少从这里的整体讨论以及https://github.com/golang/go/issues/24301#issuecomment -384349642 上的表情符号计数器来看,普遍共识似乎确实支持接受。

我知道有替代提案即将到来

@theckman ,如果你知道这样的事情,你可能是唯一知道的人。 到目前为止,我还没有看到有人提出这个问题。 我认为 Russ 关于想要在 Go 1.11 中尝试这个的声明从一开始就非常明确,所以如果有人正在研究替代提案,他们有大约 2 个月的时间来提出它们,即使是作为单挑。

我也认为我们可以接受这样一个事实,即 Go 团队在随心所欲地做出决定方面有着良好的记录,如果我们看看他们是如何在最后一刻从 Go 1.8 中拉出 Alias 的,因为它是不正确的当时要做的事情,那么我们可能应该给予他们至少允许他们建立自己的实验/解决方案的礼貌。

归根结底,该提案带来的不仅仅是关于如何选择使用哪个版本的依赖项的算法。 如果有人想出改进它的方法,那么有两种选择:通过常规 CL 流程提交或构建自己的工具并让社区使用它,如果他们选择这样做的话。 Go 团队仍然可以提供他们自己版本的工具恕我直言,所以我不认为这是一个封闭的问题。

但是,请记住,到目前为止,大多数分裂行动都是由社区而不是 Go 团队采取的。 让我们给 Go 团队构建一个工具的机会,然后在可行的情况下对其进行评估,然后就如何改进它和前进而不是写它有多糟糕提出论据。

请将此视为另一种体验报告的一部分:我曾是 Alias 提案的 _very_ 直言不讳的反对者,然后理解并现在看到该提案的实际效果。

编辑:原始消息有一个非常不幸的遗漏record of *not* making decisions on a whim应该是文本,不幸的是“不是”部分丢失了。 我为此道歉。

我详细记录了该提案的基本问题。 我一直在努力完成这篇文章,以便我可以一次将它们全部介绍——这是一个复杂而微妙的领域,因此,必须全面处理这些问题——但生活和工作使这变得困难。

虽然我在 Slack 中提到了这篇文章,并直接与@rsc讨论了其中的部分内容,但直到现在我都选择不在这里提及这些内容。 在我看来,在我准备好完全发布之前为这篇文章做广告并不是非常有建设性的。 但是,正如已经指出的那样,已经两个月了,所以我将大力推动下周系列的开始。

(编辑:这是@theckman所指的“替代品”)

@sdboyer您提到您有多个问题。 你能至少现在公布他们的名单吗?
我正在使用几个将依赖地狱提升到另一个层次的系统(chef、go、npm、composer),从经验来看,这个提议是所有关于 go 的解决方案。

@theckman ,你能确认你只是指@sdboyer的反馈吗? 这不是秘密。 Sam 在 vgo 发布的第一天就直接提到了它(“我正在写更详细的文件来说明我的担忧”- https://sdboyer.io/blog/vgo-and-dep/)。 但那是反馈,而不是提案,您多次提到“其他提案”,复数形式。 你知道的还有更多吗?

vgo 对 go/types API 用户有何影响? go/types 支持的当前状态是什么?

我收到了一个 PR mdempsky/gocode#26 来添加一个 vgo-aware go/types.Importer 实现,但我不清楚是否/为什么这是必要的。

假设有必要,我们是否可以在其他地方添加一个规范的 vgo 感知 go/types.Importer(例如,x/vgo 或 x/tools 存储库),以便基于 go/types 的工具不需要每个都重新实现这种支持?

我没有真正关注 vgo 的详细信息,所以也许这只是“没有影响”,但我没有看到上面提到 go/types。 谷歌搜索“vgo go/types golang”也同样没有信息。

谢谢。

@mdempsky ,计划是有一个 vgo 感知(并且为此构建缓存感知)包加载器,可能是 golang.org/x/tools/go/packages,但它还不存在。 人们应该等待那个包,而不是编写需要丢弃的代码。 不要合并 PR。 我对此发表了评论。

@mdempsky将在https://github.com/mdempsky/gocode/pull/26中回复,但我现在会在这里回复。

https://github.com/mdempsky/gocode/pull/26完全是一次性的; 只是一个使用现已废弃的 CL 对抗vgo的概念验证。

我刚刚看到@rsc回复,所以我会简单地指出在https://github.com/golang/go/issues/14120#issuecomment -383994980 中也有一个讨论。

总结:经过几年的版本经验,最后一次最好的尝试是一个非常复杂的算法,一个 sat 求解器。 但是,如果您对输入数据进行一些简单的修改,NP 完全决策问题就会变得不仅易于管理,而且速度非常快。

作为一个使用 NPM 的 Go 小团队用户,我非常喜欢 vgo 提案。 FWIW,我期待 vgo 在go中正确实施,并且越早实施对我和我的团队越好。

并不是说我很特别,只是因为我看到关于小团队问题的讨论,我想我会插话。

这是我的评论。

从表面上看,提案文档的最后修订版的_Proposal_ 部分似乎很好,即使不是很具体(例如,尚不清楚Subversion可以支持的程度)。 一个例外是“禁止使用供应商目录,除非在一种有限的使用中”似乎表示非模块包根本不支持供应商目录; 是这样吗?

另一方面,该提案暗示了某些设计和实施决策,这些决策破坏了当前go get的各种好处。 损失可能是可以接受的,有些可能是可以避免的,但是如果vgo get要替换go get ,则应将其作为设计考虑加以解决并进行讨论,因为否则我们最终可能会得到一个工具不是适当的替代品, vgo不会合并到gogo get将不得不作为第三方工具复活。

_Implementation_ 部分说:“在以后的版本中(例如,Go 1.13),我们将终止对非模块的 go get 的支持。 对在 GOPATH 工作的支持将无限期地继续下去。” 这很麻烦。 首先,有很多好项目多年没有更新。 由于 Go 1 的兼容性承诺,它们仍然可以工作,但许多不会添加go.mod文件。 其次,它迫使没有依赖项或不关心版本的项目的开发人员添加模块文件或弃权新的go get生态系统。 您可能有理由想要这个,但请解释原因。 (对我来说,这似乎不必要地麻烦;我宁愿使用旧的go get的分支。我同意其他语言的包管理器更加麻烦,我确信vgo比他们好,但它不能比当前的go get更好地处理我的用例,偶尔会得到govendor的帮助)。

我对 vgo 与 go 的主要关注是他们支持的工作流程。 我已经在 vgo-intro 帖子中表达了这一点。 这可能大致属于提案的_兼容性_部分,或者可能超出其范围,但它与此处提出的其他问题和问题相对应。

作为参考,这里是我的 vgo-intro 评论的副本。

在以后的一些版本中,我们将删除对旧的、未版本化的 go get 的支持。

虽然提案的其他方面听起来不错,但不幸的是。 (如果我必须在将版本控制合并到 go 工具链和继续使用版本控制工具之间做出选择,我会选择后者。) vgo 的优势在于它促进了可重复的构建并延迟了项目的中断由于不兼容的更新,直到您作为项目的作者(使用 go.mod 文件)想要面对它; 但是 go get 的优势在于它为多存储库世界带来了 monorepo 的好处:由于依赖项的完整克隆,您可以像使用自己的项目一样轻松地使用它们(检查历史记录、编辑、差异更改;去定义任何事情并责备它),它促进协作(你只需推动并提出你的改变)并且通常会强加这样一种观点,即任何时候世界只有一个当前状态——每个项目的尖端——以及一切否则就是历史。 我认为这种独特的方法(在实际的 monorepos 之外)是 Go 生态系统的一个独特的好处,它的好处多于坏处,不应该被抛弃。

该提案的一个更微妙的负面后果是它使版本控制变得不可治愈和可继承:一旦项目标记了一个版本,它就不能期望未来的更改会在不标记新版本的情况下影响到用户。 即使原作者仍然决心继续标记,分支的作者现在也被迫标记它们(如果源项目仍然处于活动状态,这尤其尴尬),或者删除旧标签。

总的来说,我想强调当前的 Go 依赖管理方法总体上优于版本控制。 它与期望所有提交都是可见的现代、动态和协作开源更好地对齐,并且仅发布发布源(或在巨大的非描述性提交中“集成内部更改”)是不够的(因为它严重降低了可见性、协作和活力)。 在 monorepos 和当前的 Go 生态系统中都可以看到,大多数项目不需要版本。 当然,这种方法不是终极的,它有缺点,而且支持版本化项目也很重要,但这样做不应该损害无版本。

总而言之,当前的go get (带有辅助工具,例如godef )支持具有以下特点的工作流:

  • 依赖项的可编辑源代码
  • 其 VCS 下的依赖项的源代码
  • 依赖项的最新版本

我想我可以假设依赖项的源代码将保持可编辑状态,即godef将链接到_files_,这些_files__not write protected_ 和_在构建期间使用_。 然而, vgo将背弃其他两点。 关于第二点,#24915 延长了对 VCS 工具的支持,但仍然宣称要放弃它; 并且工作流不仅需要从 VCS 签出依赖项,而且还需要签出对开发人员有用(例如,不是浅的 git checkout,不是删除.git的 git checkout)并在构建期间使用, 但vgo可能无法满足此要求。 关于第三点,我在 vgo-intro 评论中证明了它的价值,但vgo似乎完全放弃了它。

Go 版本控制工具不必放弃对当前工作流程的支持,也不能放弃它以保持在 Go 生态系统中工作的独特优势并充分替代go getvgo的设计使这具有挑战性,但并非显然不可行。 另一方面,提案的 _Proposal_ 部分似乎与当前的工作流程几乎兼容。 它为支持第三点(查看最新版本)引入的唯一挑战(这是一个很大的挑战)是很难决定模块 ≥v2.0.0 是否可以以master的价格签出master处于另一个主要版本。 这对当前使用gopkg.ingo get来说不是挑战,因为默认情况下,所有内容都在master处签出,而 gopkg.in 中的内容在匹配的标签或分支处签出; 但是vgo模糊了这种区别并将 gopkg.in 模型传播到所有包上。 (此外,它会停止匹配分支。)实际上,无法确定且有必要猜测如何获得指定主要版本的最新版本。

我可能错过了,但是 vgo 在这种情况下如何工作?

  • 我在服务 A 和服务 B 上工作都取决于 lib X(我也在工作)
  • 我需要对 lib X 进行重大更改

以目前的做法,我只是做我的更改,编译服务 A 和服务 B,他们为 lib X 获取我的 $GOPATH 中的任何内容,我修复了一些东西,然后我用一个主要的 semver 凹凸推送 lib X,然后推送这两个服务A 和 B 告诉他们在Gopkg.toml中使用 lib X 的新专业。

现在当 vgo 接管时,我的服务上的go build会尝试从 github 上找到不存在的新版本的 lib X,我可以预见到各种麻烦。

那么,我是否遗漏了一些明显的东西?

您可以对此类事物使用 replace 指令。

2018 年 4 月 30 日星期一 12:15 Antoine [email protected]写道:

我可能错过了,但是 vgo 在这种情况下如何工作?

  • 我在服务 A 和服务 B 上工作都取决于 lib X(我
    也工作)
  • 我需要对 lib X 进行重大更改

以目前的方式,我只是做我的改变,编译服务 A 和
服务 B,他们为 lib X 获取我的 $GOPATH 中的任何内容,我修复了一些东西,
然后我用一个主要的 semver 颠簸推送 lib X,然后推送服务 A 和 B
告诉他们在 Gopkg.toml 中使用 lib X 的新专业。

现在当 vgo 接管时,go build on my services 将尝试找到非
来自github的现有新版本的lib X,我可以预见到各种
麻烦。

那么,我是否遗漏了一些明显的东西?


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/golang/go/issues/24301#issuecomment-385499702或静音
线程
https://github.com/notifications/unsubscribe-auth/AAuFsfnD8_kbUj8fSXvGgeN77ki6KYM6ks5tt2LLgaJpZM4Sg3bp
.

@kardianos是的,但这仍然意味着我必须推动我的库更改才能尝试?

编辑:看来您可以使用路径进行替换(https://github.com/golang/go/issues/24110),这很好。 但我也可以预测,这最终会被提交很多次。

有没有计划能够创建一个额外的文件,比如go.mod.replace或类似的东西,所以我们可以在开发环境中定义覆盖并 gitignore 它们?

@primalmotion为了防止错误提交,您可能应该使用 git 挂钩。

但我认为正确的答案是不要太频繁地这样做(替换为本地路径)。 如果您的库是如此紧密耦合,那么它不应该存在于单独的存储库中(因此假装它是松散耦合的)。 在一般情况下,您应该能够在不考虑这些服务的当前实现的情况下修复、测试和发布该库。 尤其是 vgo,它保证两个服务都将继续使用旧的 lib 版本(它们以前使用过),直到您手动将它们更新到该 lib 的新版本。 如果您偶尔会每年提交一次本地路径的替换——这没什么大不了的,CI 将帮助您在几分钟内注意到并解决这个问题。

@primalmotion带有go.mod.replace未版本控制,您可以提交一个不可重现的项目。 相反,使用go.mod中的替换,您可以确定您提交的正是您当前使用和测试的内容。
提交替换可能是一个错误,但您会注意到并纠正它。
或者它可以是自愿的,我用子模块来做,当你一起处理一个项目和一个库时很好,如果你提交子模块它是可重现的。 我经常在 Python 中这样做,而在 Go 中错过了它。 有了 vgo,我很高兴我能再次像这样工作。 _(希望我的英语不好,对不起)._

好吧,问题是我们不关心可重现的构建,直到我们决定关心(即当我们准备发布时)。 我们有一个非常灵活的开发环境,其中更新库只是检查、重建、运行测试。 我们不会在我们服务的主分支中提交 Gopkg.lock,只提交带有外部库固定版本的 toml,以及对我们的主要限制。 一旦我们创建了一个发布分支,然后我们提交 Gopkg.lock 并且只有这样我们才有可重现的构建(这是由我们的 ci 完成的)。

Vgo 基本上打破了我们多年来建立的所有工作流程。 现在只是尝试一些愚蠢的东西,比如在 lib 中进行一点打印调试(因为我们都这样做,不要撒谎:)),或者一点优化,我们将不得不检查几十个服务,添加replace指令无处不在,测试,然后回来并删除所有这些。

什么可以让 vgo 为我们工作:

  • 我提到的覆盖系统
  • 一种根本不使用它并回退到良好的旧 GOPATH 进行开发的方法。

我们真的希望它为我们工作,因为它太棒了。

我们可以在 go 1.11 中测试一些东西(它现在处于冻结状态)或者 go 1.12 吗?
它已经被标记为实验性的。 而且我认为在实际开发中测试它的人越多,反馈就越有价值。

我阅读了有关版本化软件包的问题。 对于一个简单的场景,如果我编写一个库说使用 dep 来解析名为foo-plist的 plist。 对于解析的结果,该 plist 库公开了它自己的某些类型。 现在库升级到 v2,如果我碰巧返回了这些 plist 类型的任何对象,我的库将被迫升级到 v2。

这似乎很难解决,例如,如果我希望我的库在这个提议的印象下支持上述 plist 库的 v1 和 v2。

例如,在 npm 下,我的库可以简单地指定一个对等依赖项,例如>=1.0.0|>=2.0.0 ,由我的库的用户决定使用哪个版本的 plist。 因此,如果我的库foo-plist的用户还使用另一个依赖于plist的库,那么如果两个库都对 plist 的 v1 和 v2 感到满意,那么用户可以选择要使用的库实际导入。 更重要的是,如果两个库都导出 plist 类型,那么这些类型实际上是兼容的。

如果它们最终成为不同的导入路径,我认为没有任何方法可以支持它。

@itsnotvalid foo-plist.2 可以导入 foo-plist 并使用类型别名重新导出其类型。 可以在这里找到对这种技术的良好描述https://github.com/dtolnay/semver-trick

Aram 在这里指出了将更改向后移植到先前版本分支(或目录)的难度。 因为同一模块中对源的“自我”引用也包括导入路径中的版本,所以补丁不会干净地导入,或者可能会无意中引入跨版本导入。

想法?

需要明确的是,我对使用导入路径中的版本进行跨模块导入非常满意; 我认为拉斯的论点非常有说服力。 对于模块内导入,我不太清楚它们。 我理解在构建中为给定包提供单个导入路径的目标(无论是跨模块导入还是在其自己的模块中导入),但如果我必须选择,我宁愿有一个解决方案Aram 的问题莫过于保持这个属性。 (另外,我想构建系统可以在构建顶级模块时从 mod 文件中注入版本,并在下载模块依赖项后将其注入源代码)

@rsc我们有没有机会让这件事向前发展? 我看不出有什么重大阻碍。

如果我对此感到不耐烦,我很抱歉,但是有越来越多的工具致力于支持 vgo,我们越拖延,工具维护者就越会在这个问题上来回走动.

@sdboyer计划在本周发表他的文章。 我会说等待他们是公平的。

我认为选择哪些依赖版本的解析算法不应该成为阻止整个提案的原因,它还包含模块、代理支持、版本控制等内容。

如果我们稍后决定改进/更改下载这些依赖项的算法,那么我们可以这样做而不会影响其他任何事情。

我同意@dlsniper的观点——我们已经完全冻结了,自从引入 vgo 设计以来已经快三个月了。 如果 1.11 的工作进一步延迟,我担心它会被推迟到 1.12。

将成为系列的第一篇文章终于上线了。 很抱歉,这篇文章花了这么长时间才出版,而且距离该系列结束还有更长的时间。 但是,第一篇文章提供了我打算在整个系列中涵盖的主题的广泛概述,因此人们至少应该能够了解范围。

简而言之:vgo 有很多很棒的东西,其中很多是我们社区长期以来一直想要的。 但是,我认为 MVS 不适合目的,不应该将其投入生产。 很遗憾,我们想要的很多东西都围绕着 MVS,尤其是当它们中很少有专门针对它的时候。 我正在研究另一种方法,该方法在整个博客系列中用于比较目的,并将在最后一篇文章中阐明。

我正在研究的替代核心算法可能会非常简单地将go.mod迁移到,所以我预计我们不会遇到问题,如果它可能会按原样进行,如果添加了一个单独的锁定文件,其中包含依赖项的传递闭包,并且_that_ 是编译器从中读取的内容,而不是构建列表算法。 (锁定文件还有其他原因,尽管这将在第 5 篇文章中。)至少,这给了我们一个逃生阀。

但是,如果我们说 MVS 还可以,即使作为权宜之计,它也会进入并获得惯性优势。 在这一点上,我们必须证明它对@rsc是不够的(尽管实际上,他甚至在合并之前就已经将其设置为标准),并且他认为这是关于go get的真实陈述,现在:

今天,许多程序员大多不关注版本控制,一切都正常工作。

考虑到上述所有情况,我担心现在让它继续下去会产生“泛型,第二轮”——除了这一次它是围绕着管理我们如何相互交互的规则,而不是与机器交互。

但是,如果我们说 MVS 还可以,即使作为权宜之计,它也会进入并获得惯性优势。

请注意,现在,dep 具有惯性优势(既可以与其他语言的版本管理器建立在相同的前提下,又可以在广泛的社区支持下存在更长时间)。 至少对我来说,vgo 提案仍然设法通过良好的设计克服了这种惯性,并得到了良好的论据的支持。

我个人不介意 Go 中的版本控制是否会延迟,我都赞成做正确的事情而不是快速做事。 但至少目前,vgo 对我来说仍然是正确的解决方案(AIUI 社区中的许多人确实认为这是一个紧迫的问题)。

@mvdan此更改并非没有影响。 我们都想要这个,但我认为花额外的时间来平息疑虑也是明智的,特别是当提出这些疑虑的人显然已经对问题进行了很多思考时。

我一直听到有人提到冻结及其对在1.11中预览vgo $ 的影响。 目前关于这是否会被集成到1.11的官方说法是什么? 考虑到潜在的影响,在我看来这个问题出奇地安静。

@sdboyer考虑到您只有_just_ 正式公开表明您在MVS 上的立场,您对是否应该将其合并到1.11的立场是什么?

如果这不能进入1.11 ,那么我们将在 2019 年 2 月的1.12之前将其正式提供预览,并在 8 月至少1.13之前正式发布2019 年。这将在@rsc首次开始讨论它的 18 个月后发布最早的潜在版本。 当然,我们不应该过分仓促,但正如@Merovius上面所说的,包括我自己在内的许多人都认为官方对依赖管理的回应是一个“紧急”问题。 等待 18 个月似乎有些过分。

看起来dep肯定会成为官方回应,我们已经将我们的存储库转换为它(并且对它的结果感到满意)。 由于该提案有效地弃用dep以供长期使用,但没有正式的方式开始集成vgo (至少在 #25069 被合并之前),我们被迫处于令人不满意的境地使用我们知道保质期非常有限的工具(带有dep )。

FWIW,我绝对认为我们应该通过将vgo作为提案整合到1.11中并在1.11中包含 #25069(以及作为1.9的补丁发布)来推进这一点1.101.11发布时)。

老实说,我不理解 MVS 的全部含义和@sdboyer 对此的担忧。 然而,考虑到他在这个领域的经验,我认为这些担忧值得认真考虑。 也就是说,如果他愿意在1.11中将 $ vgo与 MVS 集成(同时理解他的 [仍在发展] 提案,如果 [for 1.12 ] 被接受,则不得破坏模块最初是为 MVS 设计的),那么我认为没有理由不前进。

我还要感谢@rsc的提议。 我很欣赏 Go 不只是复​​制另一个工具的方法,而是试图以一种看似惯用的方式解决这个问题。 虽然依赖管理从来都不是一件有趣的事情,但通过这个提议,Go 似乎有可能推动行业向前发展,甚至可能超越目前被认为是同类最佳的系统。

加上我的 $.02 美元,我认为 MVS 是依赖管理的“啊哈”时刻。 我很欣赏人们对它的大量思考,但仍然坚信 MVS 是它需要去的地方。

我特别同意其他人提出的观点:“自动安全修复”充其量只是一个白日梦,最坏的情况是一大堆蠕虫。

此外,我和@joshuarubin 在一起:对依赖管理的官方回应是一个紧迫的问题。 其他人评论说,我们现在可以继续使用 MVS,然后在需要时更改为其他解决方案; 如果这确实可能,我认为这是更好的方法。

我建议通过以下方式将主要版本与导入路径分离。 (我相信我已经解释了vgo-import中的原因,并且我并没有降低那里所说的 vgo 成就。)这是受到 #25069 的想法的启发,即 Go 1.9 和 1.10 中的go build应该学会创造性地解释导入路径(通过删除版本部分); 在我的建议中,旧的go build没有改变,但vgo学到了类似的技巧。


从语法上讲,唯一的变化是:

  1. .go文件中,如果v2是目录,则import "repo/v2/pkg"仍为import "repo/v2/pkg" ,否则变为import "repo/pkg" 。 这保持了与当前go get的兼容性。
  2. go.mod文件中,如果module "repo/v2"位于v2子目录中,则它保持不变,但如果它位于顶层,则变为module "repo" 。 (这是规范的导入前缀。)
  3. go.mod文件中,您也可以编写require repo v2.3.4 as repo2 。 然后在.go文件中,您将使用import "repo2/pkg" (如果v2是目录,则使用 $ import "repo2/v2/pkg" )。 当前的go get将无法导入它(除非您使用类似require github.com/owner/project v2.3.4 as gopkg.in/owner/project.v2的东西),但这仅在您想在同一模块中使用多个主要版本并且依赖项不依赖时才需要将主要版本存储在子目录中,当前的go get无论如何都无法支持。

从技术上讲,这允许您编写go.mod

require repo v1.0.0
require repo v1.1.1 as repo1
require repo v2.2.2 as repo2
require repo v2.3.3 as repo3

但是最小版本选择将解决这个问题,这样reporepo1都引用了v1.1.1的仓库,并且repo2repo3v2.3.3 。 我不知道是否应该允许或禁止这种别名。


好处:

  • 模块感知代码将与当前的go get兼容,甚至超过 v2.0.0; 最后:

    • 无需让go get最小化模块感知 (#25069)

    • 超过 v2.0.0 的项目将不必破坏与模块未知go get的兼容性

  • 项目在成为模块之前不必等待它们的依赖项成为模块 [1]
  • 无需弃用不支持模块的项目或阻止作者开始新的不支持模块的项目
  • 更容易保持对当前go get的无版本工作流的支持(在此处上面进行了解释)

缺点:

  • 可能不方便遵守已经写入的go.mod文件将继续工作的承诺(除非新模块文件的名称与go.mod不同)

矛盾心理:

  • 不同模块中相同的导入路径可能引用不同的主要版本

    • 好:更易于维护过去 v2.0.0 和主要版本更改

    • 不好:不看go.mod你不知道你用的是哪个主要版本

  • 模块可以定义任意导入前缀以在其代码中使用

    • 一些用户会选择通过短名称导入所有内容(例如import "yaml"require gopkg.in/yaml.v2 v2.2.1 as yaml

[1] 目前vgo可能仅在模块的非模块化传递依赖超过 v2.0.0 的情况下正确支持非模块化依赖。 否则,项目必须等待所有间接依赖于 v2.0.0 之后项目的依赖项成为模块。

我对从 https://github.com/rsc/corpus 中的包中找到的 Gopkg.toml 文件进行了分析,并在https://github.com/zeebo/dep-analysis中写了一个摘要 根据那里的数据,似乎没有太多证据表明 vgo 无法处理几乎所有已确定的用例。

我真的希望这将有助于减少社区中的恐惧,并帮助它达成协议,我们应该按原样推进提案,记住将有额外 6 个月的时间来获得使用该工具的真实经验,并任何必要的更改以解决可能出现的任何问题。

如果我引用你的话:

几乎有一半的约束实际上根本不是约束:它们指向主分支。

这可能是因为只有 master 存在无标签或“命名分支,如 v2、v3”如果是这种情况,那么比较是不公平的,因为你别无选择!

@mvrhov我不确定您所说的“不公平”是什么意思。 在我看来, vgo 和 dep 可以很好地处理这种情况。 或者更确切地说,任何现实的替代方案都必须很好地处理这种情况。 特别是:如果还没有发布版本,在 vgo 世界中,它们可以被标记为 v1.0/v0.x,并且不需要更改任何导入路径(vgo 的主要特性)。

据我所知,分析的重点是尝试估计不同方法造成的现实世界的痛苦。 我看不出这种情况如何给任何人带来真正的痛苦。

该提案已经公开讨论了两个多月: @rsc@spf13进行了反馈会议,并从社区收集了宝贵的意见,从而对提案进行了修订。 @rsc还与@sdboyer举行了每周会议,以获得进一步的反馈。 已就该提案提供了宝贵的反馈意见,从而导致了额外的修订。 越来越多的反馈是针对随附的实施而不是提案。 经过大量审查,我们认为是时候接受这个提议了,让 Go 广泛的工具实施者生态系统开始进行重大调整,以便我们的用户群能够获得最佳体验。

该提案有两个反对意见,我们认为我们应该谈谈:

  1. 该提案将要求人们改变他们在使用和发布库方面的一些做法。
  2. 该提案未能为所有可能出现的涉及不兼容性的情况提供技术解决方案。

这些在他们的观察中是准确的,但按预期工作。 代码的作者和用户_将_必须改变他们在使用和发布库方面的一些做法,就像开发人员已经适应了 Go 的其他细节一样,例如运行 gofmt。 转变最佳实践有时是正确的解决方案。 同样,vgo 不需要处理所有可能涉及不兼容性的情况。 正如 Russ最近在新加坡 Gophercon 的演讲中指出的那样,解决不兼容问题的唯一永久解决方案是共同努力纠正不兼容问题并维护 Go 包生态系统。 像 vgo 或 dep 这样的工具中的临时解决方法只需要足够长的时间来给开发人员时间来解决真正的问题,而 vgo 可以很好地完成这项工作。

我们感谢您为这个关键问题带来的所有反馈和热情。 该提议已被接受。

— Go 提案审查委员会

为了给记录增添一些色彩,与@sdboyer的每周会议不应被视为一种认可。 Sam 最近开始写关于 MVS 的问题以及他喜欢的关于 vgo 的事情。 我添加这个是为了确保不会与其他任何人发生误解。 如果你想要他的意见,请阅读他的话。 我的看法是,它们与当前的预期方法存在相当大的分歧。

@mattfarina FWIW,我把这句话读得更多是因为“我们知道他的批评(正如他私下表达的那样),但这并没有改变我们的看法”。 令人遗憾的是,他的观点和论点目前还没有公开。

在该方法仍然存在基本问题未解决的情况下接受提案是不负责任的。 作者和社区领域专家@sdboyer之间的共识似乎是在提案被接受之前达到的合理最低标准。

@merovius我们中的一些人公开和私下分享了意见。 许多人认为提出的问题被压倒了(有时是粗鲁的),而不是给出了足够的解决方案。 我开始公开分享实际问题,以便我们尝试解决它们。 例如,就在今天,我在这里对一个实际问题进行了一些细节分析。 有趣的旁注,这是在黑客新闻的头版,同时被标记为接受。

@peterbourgon作为一个进行 Go 依赖管理调查并在 Glide 工作的人,我倾听了人们的需求并试图实现这一目标,我可以展示实际问题(而不仅仅是意见)。 也就是说,我可以将用户的愿望、需求和期望与这些问题的解决方案相匹配。 我担心的是与 vgo 的当前路径不匹配。 由于人们进行依赖管理的方式不同,存在未满足的需求和实际问题。

开始减轻我的担忧的一个简单方法是让 vgo 为 Kubernetes 工作,让 Tim Hockins 满意。

开始减轻我的担忧的一个简单方法是让 vgo 为 Kubernetes 工作,让 Tim Hockins 满意。

让 vgo 今天为 Kubernetes 工作,还是让 vgo 在未来几年为 Kubernetes 工作? 据我了解,vgo 和 dep 之间的基本设计分歧之一是我们是否需要与当今存在的生态系统合作(dep 的假设),或者我们是否可以将社区转向进行标记发布并保持兼容性(vgo 的假设) .

因此,在 Go 社区规范发生变化之前,vgo 可能在一段时间内无法用于使用多依赖的 Kubernetes。

@mattfarina当然。 不过,就个人而言,我觉得把它描绘成“蒸汽卷”非常令人沮丧。 @sdboyer 几个月来基本上都在公开讨论中弃权,但他仍然没有真正的、具体的论点。 他有他的理由,这是公平的。 但是共识仍然需要讨论,至少就公共记录而言,我个人不知道有任何问题被提出并被明显忽略(虽然还没有阅读你的帖子)。

就我而言,任何讨论都是闭门进行的。 鉴于我们没有任何信息,我认为假设双方都给予了适当的考虑是公平的。

@bradfitz SemVer 今天被 Go pacakges 使用,通常用在 PHP、node.js、Rust 和许多其他语言中。 这是很常见的事情。 我遇到了跨这些语言的问题,以及更多包因 SemVer 兼容性问题而中断的问题。 有时是故意的,有时是偶然的。 Go 会采取哪些不同的措施来避免所有这些其他语言中出现的问题,因为人们容易犯错?

如果我们不能清楚地表明始终保持兼容性并且开发人员不应该有可供他们访问的旋钮来调整它并将该信息传递到依赖关系树上,这是一个糟糕的假设。

我想每个人都会同意:在任何语言/平台上,依赖管理的现状都很糟糕。 我相信@bradfitz正确地解释了冲突的要点。 也许 vgo 不会成功,但对我来说很明显我们必须改变一些东西(我的意思不仅仅是在 Go 中,而是在一般情况下),而且 vgo 看起来很有希望尝试一下。

@mattfarina我们计划尝试实施一项服务,该服务将自动控制实际维护的兼容性。 将其与 godoc.org 集成,为 README 提供标记,将其用作 go get 的代理 - 我们可以通过多种方式尝试使其工作得足够好。 当然, @sdboyer关于 API 兼容性并不能保证实际兼容性是正确的,但这是一个好的开始,在大多数情况下应该足够好。

因此,在 Go 社区规范发生变化之前,vgo 可能在一段时间内无法用于使用多依赖的 Kubernetes。

希望不是一种策略,尤其是当现有的行为和期望已经确立时。 如果我们在五年前进行讨论,Go 可能会有创新代币可以在这里使用,而且事情更容易受到影响。 但是由于长期忽视这个问题,我似乎很清楚现在提出的任何工具都必须满足用户所在的位置。

Go 会采取哪些不同的做法来避免所有这些其他语言中出现的问题,因为人们容易犯错?

我们一直在讨论某种go release命令,它既可以简化发布/标记,也可以检查 API 兼容性(例如我为 Go 版本编写的 Go 内部go tool api检查器)。 在推送任何标签之前,它还可以查询godoc.org并找到你的包的调用者,并在预发布时间对你的新版本运行他们的测试。 等等。

在推送任何标签之前,在预发布时间找到您的包的调用者并针对您的新版本运行他们的测试。 等等。

对于不是谷歌的人来说,这实际上并不实用。

对于不是谷歌的人来说,这实际上并不实用。

随着所有云提供商开始提供按秒付费的容器即服务,我认为我们没有理由不能将其作为开源工具提供,任何人都可以运行并支付他们需要的 0.57 美元或 1.34 美元在几分钟内对一堆主机运行无数次测试。

在运行测试方面,谷歌没有太多秘诀。

随着所有云提供商开始提供按秒付费的容器即服务,我认为我们没有理由不能将其作为开源工具提供,任何人都可以运行并支付他们需要的 0.57 美元或 1.34 美元在几分钟内对一堆主机运行无数次测试。

这需要一个特定云提供商的帐户,要求您接受特定云提供商的服务条款(出于法律原因,您可能会也可能不会这样做,即使世界上大多数人都认为它没有问题),要求您居住在云提供商服务的区域(例如,如果您在伊朗并且云提供商在美国,由于出口法律,您可能无法使用它),并要求您有钱支付云提供商(大概是每次发布)。 这笔钱可能不多,但这并不意味着每个人都可以支付。 如果我们希望 Go 具有包容性并可供不同的受众使用,这似乎不是一个好的解决方案。

/两美分

@SamWhited MeteorJS 与 Galaxy 一起工作,这是一个内置的meteor publish命令,用于在云提供商中运行您的项目。 也许我误解了所提出的问题,但他们的摆动似乎很好。

@bradfitz如果 API 没有改变但其背后的行为发生了变化怎么办? 这是一个脱离 SemVer 并影响导入它的案例。 您如何发现这种情况? 我问是因为我不止一次经历过。

任何人都可以运行的开源工具,只需支付 0.57 美元或 1.34 美元即可在一堆主机上运行数分钟的无数测试。

现在涉及到成本。 对于美国科技城的人们来说,这可能感觉很好。 非洲、中美洲或其他全球分布的人呢? 在“技术精英”圈子之外通常如何获得这样的工具?

而且,所有不做公共云工作的情况呢? 内部部署(很多人都这样做)或专有且存在信任问题。 这些东西将如何为他们工作? 如果您有内部工具,您可能不想将您正在使用的导入内容泄露给公共服务。 假设您从 GitHub 获取导入,但此服务在 Google 中运行。 人们觉得把他们的依赖树交给谷歌可以吗? 一堆不会。

它还可以查询 godoc.org 并找到你的包的调用者,并在预发布时间,在推送任何标签之前,针对你的新版本运行他们的测试。 等等。

让我们以 Kubernetes 为例。 有人编写了一个导入 Kubernetes 的包。 所以一个工具必须得到它并运行所有的测试。 它的一部分设计用于在 Windows 和 POSIX 上运行。 我们可以测试多操作系统/多架构(因为 Go 可以处理)。 那会是什么样子(和成本)?

--

我认为这样的工具很有用。 我的意思不是让人们有其他想法。 我只是不明白他们如何解决许多人的问题。 它们不够实用或不适合所有设置。

感觉就像我们正在尝试解决具有已知和可控约束的数学问题。 但是,人很乱,所以我们需要容错的解决方案。

今天早些时候引用@technosophos

“版本管理器实际上不是编译器或链接器或任何东西的工具……版本管理器是供人们协作的。”

值得一提的是,他写了不止一个依赖管理器,研究了其他人,并与写得更多的人交谈。

在该方法仍然存在基本问题未解决的情况下接受提案是不负责任的。 作者和社区领域专家@sdboyer之间的共识似乎是在提案被接受之前达到的合理最低标准。

只是在这里堆积一点:我们在包装方面有着非标准和次优的历史( go get生态系统)。 放弃一个标准的包管理器比发布另一个go get更好,这会以与“标准”工具兼容的名义将人们推向不良实践。 作为自 Go 公开发布以来一直在使用 Go 的人,我发现这次谈话令人沮丧和沮丧,因为 Go 团队的领导似乎没有从go get所犯的错误中吸取教训(nee goinstall )。

这个提议存在合理和实际的问题。 我们应该改变提案以解决它们,而不是简单地说:“按预期工作”。 如果我们不能为 1.11 或 1.12 或 1.13 做正确的事情,那么我们应该等到它可以正确完成。 该提案提出了一个系统,该系统的行为与大多数其他系统显着不同,而且表现不佳。

MVS 背后的动机似乎是传统方法是 NP-Complete。 我发现这是一个非常糟糕的动机。 在处理 NP-Complete 问题时,主要问题是:“困难实例多久出现一次”。 对于包管理,答案似乎是“很少”。 我们不应该仅仅为了避免问题上的 NP-HARD 标签而满足于不完整和无用的问题表述。

大多数具体观点已由其他更接近该问题的人表达( @sdboyer @peterbourgon @mattfarina等......)。 我的主要抱怨是,当这些具体问题没有得到充分解决时,我们正在接受这个提议。

@SamWhited ,您对假设设计的可选功能提出了质疑。 不信任任何云提供商或无法在其国家的防火墙内使用任何云提供商或不想付费的假设用户总是可以在他们自己的机器上运行测试(或其中的一小部分)一夜之间。 或者只是使用go release签名检查,它可以让您免费完成 95% 的行程。

@mattfarina@SamWhited ,让我们将讨论移至https://github.com/golang/go/issues/25483。

@mattfarina

我遇到了跨这些语言的问题,以及更多包因 SemVer 兼容性问题而中断的问题。 有时是故意的,有时是偶然的。 Go 会采取哪些不同的措施来避免所有这些其他语言中出现的问题,因为人们容易犯错?

我仍然不清楚为什么 vgo 在这些情况下的性能比 dep 差。 相反,在我看来, vgo 表现得更好。 在的博文中,您提到了 helm 的一个特定问题,作为 vgo 模型失败的证据。 但是,在 vgo 世界中,vgo 选择使用 v1.4.0 的 grpc 有两种情况:

  1. helm 的开发者选择在go.mod中指定grpc >= v1.4.0 #$ 作为需求。 在这种情况下,他们可以简单地恢复此要求,从而回滚到适用于他们的先前版本的grpc
  2. helm 依赖项的开发人员选择指定grpc >= v1.4.0 。 在这种情况下, dep 也会安装它,如果 helm 尝试通过限制grpc < v1.4.0来回滚,则由于需求冲突,dep 将不得不发牢骚。

所以在我看来,vgo 至少和 dep 一样解决了这个问题。 同时,在深度世界中,还有另一种选择:

  1. helm 的传递依赖需要grpc >= v1.x.0 ,还有一些x < 4 ,然后grpc发布v1.4.0并且 dep 决定在安装时使用较新的版本,而不被询问. 在这种情况下,helm 会坏掉,需要挤在一起进行错误修复发布(如您的帖子中所述),这会造成麻烦。 同时,vgo 会忽略新版本,一切都会继续正常工作。

我显然忽略了一些基本的东西。 但上次我在 slack 上要求澄清这一点时,我被推迟到假设的未来博客文章。 因此我感到沮丧,因为我真的不明白这个想法来自哪里,vgo 以某种方式在人们破坏其版本的语义方面比 dep 有更多的问题。

澄清一下,这与dep无关。 它分析了 MVS (vgo) 与其他基于 SAT 求解器的包管理解决方案(包括 dep)相比的行为方式。

Matt 的另一个例子如下:

您是模块app的开发者,您有两个依赖项:

  • grpc [>= 1.8 ]
  • helm ,具有以下依赖关系:

    • grpc [>= 1.0, < 1.4] (因为grpc在 1.4 中引入了重大更改)。

大多数人不会知道传递依赖要求( helm -> grpc [>= 1.0, < 1.4]),因为这需要注意helm在使用grpc >= 1.4。 我认为这不是大多数人会关心或花时间和精力调查它的事情。

如果您使用的是 vgo(特别是依赖于 MVS),您将获得:

  • helm
  • grpc [>= 1.8 ]

这应该是一个无效的组合(因为helm的要求不满足)并且大多数部门经理会给出一条错误消息,告诉您您有冲突(假设helm已在某些格式)。

这是对 MVS 和 vgo 的一个重要批评点。 它不想让依赖关系在特定版本出现问题时声明它们以避免需要完整的 SAT 解决方案。 您可以参考MVS 文章中的TheoryExcluding Modules部分以获得更多解释。

MVS 不想承认这种可能性(或至少想限制将其指示到当前模块的能力),假设 SemVer 始终受到尊重,因此不需要,但实际上并非总是如此。 我会参考您关于向后兼容性的文章,以说明为什么坚持向后兼容性很难并且在被迫采用 vgo 方式时是不切实际的。

MVS 解决方案是要求模块开发人员指定要排除哪些版本,而忽略依赖项的作者可以共享的关于这些不兼容性的知识。 因此,将责任转移给开发人员,以了解执行大多数部门经理都会经历的复杂逻辑,以确定哪些版本的依赖关系相互兼容(即 SAT 解决方案)。

除非人们神奇地开始遵守 SemVer,否则我会想象在使用 vgo 转换为古怪的练习时安装依赖项,在导入模块后,您必须检查 README 文件以复制不兼容的版本列表以包含它们在您的go.mod文件中。 请记住, exclude语句当前仅采用一个版本。

@Merovius

@mattfarina的博客文章中,他们描述了 Helm 有意升级到 grpc v1.4.0,即相当于更新go.mod 。 问题不是 dep 或 vgo 如何避免这个问题,而是它们如何使库作者能够从中恢复。 在 dep 的情况下,他们可以发布一个广告grpc<v1.4.0约束的补丁。 对于对 grpc 没有其他依赖的用户,这将起作用。 如果用户已经更新到最新版本的 helm 并且拥有对grpc>=v1.4.0的另一个依赖项,则此约束将导致他们的构建失败; 不理想,但比微妙地破坏运行时行为要好。

使用vgo ,Helm 维护者是否有任何等效的选项可用? 如果用户出于任何原因将 grpc 升级到 v1.4.0,MVS 将始终选择[email protected] ,Helm 将被破坏,并且 Helm 维护人员对此无能为力(无需调整更改的行为,这通常是耗时)。 我不能代表@mattfarina发言,但这就是我阅读提出的担忧的方式,也是我分享的一个。 使用dep和类似系统,中间库可以防御(或更经常地,恢复)具有上限约束的兼容性违规。

@ibrasho @mattfarina

似乎 Russ 在 GopherconSG 演讲中的一个例子涵盖了一个非常相似(可能相同?)的场景。

在上面的帖子中,您说 helm 依赖于“grpc [>= 1.0, < 1.4]”。 但这并不完全准确。 我认为您的意思是某些特定版本的 helm 取决于“grpc [>= 1.0, < 1.4]”。 假设 helm 的版本是 2.1.3; 大概这个版本的 helm 是在 grpc 1.4 之后发布的(否则它不会知道将自己标记为与 grpc 1.4 不兼容)。 Russ 在那次谈话中的观点是 helm 的早期版本(比如 2.1.2)可能不会(还)将自己标记为与 grpc 1.4 不兼容。

换句话说,一个令人满意的分配(根据 dep)应该是 helm = 2.1.2, grpc = 1.8。 Dep 可以正确选择此版本分配而不会出错,因为它满足给定版本的所有约束。

@balasanjay

Helm 的版本并没有从根本上改变这个例子。 [email protected]可能会声明对[email protected]的依赖,而 helm 的用户可能会直接或通过其他一些包对[email protected]有依赖。 在这种情况下,MVS 失败并安装[email protected][email protected] 此外,此示例假设用户在发现问题之前有意更新到[email protected] (因此是 [email protected]),甚至可能是[email protected][email protected]等。

MVS 故意禁止从中间依赖项中排除以实现其理论目标:

不能添加负面影响(X → ¬ Y,等价于 ¬ X ∨ ¬ Y:如果安装了 X,则不得安装 Y)...

对于本例,我们需要表示,如果安装了 X= helm>= 2.1.3 ,则一定不能安装 Y= grpc>=1.4.0 ,这是我们无法设计的。

实际上,当兼容性规则被破坏时,只有顶层包(通过修复损坏)和底层用户(通过手动添加排除)有任何追索权。 像 Helm 这样的中间体必须与任何依赖项的所有未来版本一起工作,对我来说,这大大降低了此类库的价值主张。

也许这会鼓励在 grpc 和 aws-sdk-go 等大量使用的库中采取更好的行为,因为所有各方都会感受到任何破坏的全部冲击,并且将更难以解决。 然而,这并不总是那么容易。 如果“破坏”不是真正的破坏,而是对某些用户产生意想不到的后果但对其他人有帮助的行为的合法变化怎么办? 这种类型的事情并不少见,上游作者在恢复这种情况下的更改时会犹豫不决。

我不认为我几乎像拉斯一样清楚。 让我再试一遍。

最初,deps 的状态如下所示。
头盔 2.1.2:grpc >= 1.0

然后,发布了 grpc 1.4。 Helm 的开发人员意识到存在不兼容性,因此推出了新版本的 Helm:
Helm 2.1.3:grpc >= 1.0, < 1.4

我启动了一个新的应用程序,它依赖于 Helm 和 grpc 的一些新功能(为了争论)。 我列出了以下部门:
头盔 >= 2.0.0
grpc >= 1.4

上面的说法是 dep 会注意到内在的冲突,并报告错误。

但 Russ 指出这不是真的,因为存在没有冲突的版本分配。 具体来说,dep 可以选择使用 helm 2.1.2 和 grpc 1.4。 根据 dep 的说法,这是一个有效的任务,因为 helm 2.1. 3是与 grpc 1.4 不兼容的,而 2.1. 2与所有 grpc >= 1.0 兼容。

换句话说,dep 可以_有效地_选择“修复”冲突,方法是降级到尚未记录冲突的版本。 如果您有不可变的版本,那么这似乎是不可避免的。 因此,dep 似乎也会错误处理https://codeengineered.com/blog/2018/golang-vgo-broken-dep-tree/。

@balasanjay当然,但最终 Helm 推出了 2.2.0 并提供了您想要的简洁新功能,然后您尝试升级,然后问题就出现了。

(我持谨慎乐观的态度,如果vgo 有一个设计良好的用户体验,让人们意识到他们的 MVS 结果存在这种形式的“冲突”,这将是可行的。“精心设计”的意思是它鼓励人们处理这个问题,而不是一直有很多警告。)

我当然同意在某些情况下 dep 会检测到冲突,但只是想清楚它_不是_该博客文章中提出的问题(或至少,不是当前提出的版本要求)。

FWIW,我认为如果安全措施在一个相对简单的冲突示例上中断,质疑其有效性是合理的。

作为记录,我追踪了 Russ 的示例(它与这个非常相似,令人印象深刻): https ://youtu.be/F8nrpe0XWRg?t=31m28s

回复:最小版本选择,我无法弄清楚如何解决以下情况:

想象一个流行的维护模式库,例如 v1.2.3 已经发布了一年多没有任何变化。 许多其他库都依赖它。
一位开发人员出现并在 v1.2.3 中实现了速度优化。 它将库的核心功能提高了 10 倍,无需更改 API! 这是作为 v1.3.0 发布的。 次年在此软件包中不再发现任何错误。

受抚养人最终如何获得此更新? 它们可以与 v1.2.3 一起使用,但 v1.3.0 显然更好。

@daurnimator

受抚养人最终如何获得此更新?

每个应用程序都需要将 1.3.0 设置为要使用的最低版本。

好吧,今天是多么愚蠢的一天! 😄

该提案已经公开讨论了两个多月

这个问题已经存在多年了,所有这一切的时间线 rsc 都被人为地缩短了,考虑到所有的事情。

该提案有两个反对意见,我们认为我们应该谈谈:

  1. 该提案未能为所有可能出现的涉及不兼容性的情况提供技术解决方案。

如果这是对我向拉斯概述的立场的暗示,那是一种误导性的漫画。 我花了这么长时间整理我对这个话题的想法的一个原因是,反复使用误导性的论点和陈述……坦率地说,是不稳定的。

从字面上看,没有人认为可以涵盖所有可能的场景。 问题一直是,MVS 只解决了一个 _illusory_ 问题(避免 SAT),在实践中创造了_new_ 问题,而这在实践中更重要,并且不能有效地作为我们可以合理操作的中间层。

这些在他们的观察中是准确的,但按预期工作。 代码的作者和用户将不得不改变他们在使用和发布库方面的一些做法,就像开发人员已经适应了 Go 的其他细节一样,例如运行 gofmt。

通过将此处提出的更改程度与运行 gofmt 进行比较来淡化此处提出的更改程度无济于事——这可能是我们作为 Go 开发人员执行的最无脑的活动。

正如 Russ 在他最近在新加坡 Gophercon 的演讲中指出的那样,不兼容的唯一永久解决方案是共同努力纠正不兼容并维护 Go 包生态系统。 像 vgo 或 dep 这样的工具中的临时解决方法只需要足够长的时间来给开发人员时间来解决真正的问题,而 vgo 可以很好地完成这项工作。

让我们明确一点 - 重要的是_社区_。 我们。 生产软件生态系统的人。 我们通过创建工具来帮助我们将有限的代币用于有用的合作,而不是通过创建脆弱的工具来惩罚我们的参与,从而建立一个更好的生态系统。

@Merovius

我被推迟到假设的未来博客文章。 因此我感到沮丧,因为我真的不明白这个想法来自哪里,vgo 以某种方式在人们破坏其版本的语义方面比 dep 有更多的问题。

把所有这些论点放在一起需要时间,而且这不是我的日常工作。 我的目标是今晚完成,但我仍在最后的编辑阶段。 😢明天早上...? 没有什么是一成不变的!

问题是,而且一直是,MVS 只能解决一个虚幻的问题(避免 SAT),......

恕我直言,SAT 解决方案是一个非解决方案,因此对我来说,MVS 解决了一个非常现实的问题。

恕我直言,SAT 解决方案是一个非解决方案,因此对我来说,MVS 解决了一个非常现实的问题。

如果可能的话,想了解其背后的原因吗?

这个问题已经存在多年了,所有这一切的时间线 rsc 都被人为地缩短了,考虑到所有的事情。

我在这里看到两个明显相互矛盾的想法。 调查一再表明,依赖管理迟早需要解决方案。 然而,六个月的时间表不足以审查和批准提案。

我知道我们不应该选择第一个实施的解决方案。 但时间是因素之一。 对 vgo 的反建议尚未明确定义,很可能需要数年时间才能实现。 我会选择 Go 1.11 中的 vgo,而不是 Go 1.14 中基于 SAT 的超棒解决方案。

这也是假设 vgo 实现的东西是一成不变的。 据我们所知,vgo 在 1.12 和 1.13 窗口期间可能会发生很大变化。

如果可能的话,想了解其背后的原因吗?

这与选择 Go 的正则表达式而不是 PCRE 的原因相同,即。 不允许程序依赖于具有二次/指数最坏情况复杂度的算法。

我们应该记住vgo只会替换go get仅此而已。 我们应该将vgogo get进行比较,而不是将vgo与不存在的东西进行比较。 “越差越好”! 现在让我们关注实现细节。

FWIW,IMO 完全有可能在没有完整的 SAT 求解器的情况下引入上限 - 只是不可能使生成的解决方案是最优的或保证会找到现有的解决方案。 如果需要,您可以

  1. 可以指定上限
  2. 通过 MVS 求解时忽略它们
  3. 对照所有边界检查找到的解决方案,如果它不适合,则发出嘶嘶声(这是关键区别:传统方法会尝试找到不同的解决方案)

这意味着您将获得 MVS(一种没有病态运行时间的简单算法)的优势,保留标记不兼容性并静态检测它们的可能性,但您放弃了始终找到解决方案(如果存在)的保证。 尽管可以争辩说,您找到的解决方案实际上并不是一个解决方案,因为不兼容性仍然存在,只是算法不知道。

所以至少对我来说,似乎完全有可能通过某种方式来获得 MVS 的上限。 我仍然不相信它实际上是需要的,但如果事实证明是这样,它总是可以在以后添加。 使“MVS 从根本上不合适”的断言有问题。 但我仍然可能误解了这个问题。 并且可能有一个失败的场景更好地说明它?

@sdboyer不用担心。 正如我所说,我完全理解这不是你的日常工作,我很感激你花时间参与。 这仍然是一个“如果我们不知道这些论点,我们就不能谈论它们”的实际问题。 我可以想象你和我一样对整个情况感到沮丧:)

这与选择 Go 的正则表达式而不是 PCRE 的原因相同,即。 不允许程序依赖于具有二次/指数最坏情况复杂度的算法。

@cznic我认为依赖管理不仅是一个技术问题,它还与社会约束和考虑因素交织在一起。 所以我不确定将它与正则表达式(一个纯粹的技术实现问题)进行比较,并且支持基本上基于时间复杂度的算法是一种公平的方法。

我看到有些人喜欢 MVS,因为它更简单、更容易推理,如果它解决了手头问题的其他方面,这是一个可以理解的考虑。

我想知道人们是否有其他理由更喜欢它而不是基于 SAT 的算法。

我想知道人们是否有其他理由更喜欢它而不是基于 SAT 的算法。

@ibrasho MVS 不需要锁定文件。

我想知道人们是否有其他理由更喜欢它而不是基于 SAT 的算法。

就个人而言,我的理由是

  1. 它将锁定文件和清单统一到一个文件中,该文件在很大程度上可以进行计算机编辑。 这意味着在常见情况下仍然可以重现(或“高保真”)构建时噪音更少。 AFAICT 这是 MVS 独有的东西。 由于我对其他包管理器的主要挫折之一是不得不编辑清单文件的辛苦,这对我来说是巨大的。
  2. 逐步修复将更容易/成为可能。 由于我坚信这种解决分布式开发问题的方法,这对我来说也很重要。
  3. 对于大多数图书馆来说,它可能有助于生态系统共同保持接近 HEAD。 这主要是推测,在某种程度上取决于可用的工具。 但是,如果“将所有内容都拉到最新版本”足够简单,它可以增加在生产中相互测试新版本库的节奏。
  4. 它在保持或超越其他方法 AFAICT 的理想品质的同时实现了所有这些。 仅在需要时才进行升级。 即使某些依赖项的构建在新版本中被破坏,二进制包仍将继续构建,从而使作者有时间进行修复。

尤其是最后一部分有点讽刺。 因为在@sdboyer的上一篇文章中,这种确切的质量被认为是极其重要的——但在这一点上,我仍然相信 vgo 在这方面比传统方法更好。

@Merovius
“它将锁定文件和清单统一到一个文件中”

  • 这并不完全正确,因为清单实际上分散在您的文件中。 导入语句有效地充当清单。

@docmerlin当我说“清单”时,我的意思是一个文件,其中列出了您所依赖的所有模块及其适当的版本(特别是,它包含比导入语句更多的信息,否则您将不需要它)。 在 vgo 的情况下是go.mod ,在 dep 的情况下是Gopkg.toml 。 您也可以将其称为依赖项求解器的配置文件。 如果您认为其他术语更合适,请在阅读我的评论时随意将其替换为清单。

请注意,每个文件都有一个列出所有依赖项的文件和一个显式导入列表(可能是依赖项的严格子集)是很正常的。 特别是,我知道的所有 Go 依赖管理器都这样做。 其他方法还使用一个锁定文件,该文件描述了用于确保可重复构建的特定、精确的版本。 我所指的 MVS 的显着特点是不需要这个文件,因为它可以从 manifest/dependency-solver-config/go.mod 文件中唯一地派生出来。

(已删除+从我的个人帐户转贴)

@cznic关注 MVC 的复杂性类与孤立的基于 SAT 的方法没有意义(正如我认为@sdboyer在某处写的那样——我们开始谈论它,因为它是我们能做的少数事情之一姓名/身份)。

@bradfitz在#25483 中提出的解决 vgo 一些问题的建议(我认为这最初是一个疯狂但可能是非常实用的解决方案)涉及在发布之前从你的 API 的任意用户运行测试。 这通常是一个 NP 完全问题,但实际上可能是一个很好的解决方案(就像 gps2 一样)。

因此,一方面我们有基于 SAT 的包管理算法,另一方面我们在 NL 中有一个算法,它迫使我们稍后进行 NP 完全工作(或超时,这是任何实用的 SAT 求解器都会做的事情)对于对抗性锁定文件)。

孤立地关注 MVC 的复杂性类别与基于 SAT 的方法是没有意义的……

我不确定“焦点”一词的来源。 只是如果有两种算法可用,一种最坏的情况是二次或更差,另一种是线性更好,我选择后者并避免前者。 我称之为原则,而不是重点。

... #25483 中的建议以解决 vgo 的一些问题 ...

似乎问题 #25483 与 vgo 无关。 错字?

只是如果有两种算法可用,一个最坏的情况是二次或更差,另一个是线性或更好,我选择后者并避免前者。

@cznic当然,通常如果两种算法给你相同的结果,你想要一个复杂度较低的算法(尽管这并不总是那么简单和干燥 - Python 对小输入使用插入排序,因为尽管复杂度界限更差,但它有更好的常量+运行时间)。

在这种情况下(MVS 与基于 SAT 的算法),结果在目的上有所不同,并具有广泛的后果。 我建议,因此你不能只比较算法的复杂性,你需要考虑它们更广泛的影响。

似乎问题 #25483 与 vgo 无关

该问题的第一行是 Brad 在本问题中的评论的链接: https ://github.com/golang/go/issues/24301#issuecomment -390788506。 虽然这个工具在 vgo 之外很有用,但似乎它在很大程度上被认为是为了减轻 MVS 的一些缺点(在我看来/理解)。

哎呀,这么多评论。 我会尝试添加一些细节和历史来提供帮助。

几年前,有许多不同的依赖管理解决方案(例如,godep、glide 等)。 为了找出前进的道路,发生了一些事情:

  1. 一群投资和知识渊博的人聚集在一个委员会中。 请注意,Google 的一名 Go 团队成员是该小组的成员。
  2. 第二组曾编写过依赖管理器或有关于它们的信息的人支持第一组。
  3. 对社区进行了一项关于现有工具(Go 内部和外部)的需求和想法的调查。 请注意,某些结果仅供委员会查看。
  4. 对使用 Go 进行生产的公司进行了采访,以获取有关其需求的详细信息。 这件事的细节是不公开的,所以人们可以自由发言。

调查和访谈数据全部反馈给委员会。 在查看数据并对其进行辩论后,他们决定我们需要一个具有某些功能的解决方案并创建了一个规范。 在那之后,dep 上的开发开始满足这些需求。

去年在 Gophercon 有一个贡献者峰会,其中包括投资于依赖管理的人谈论它。 在这些谈话快结束的时候,Russ 走到桌边说,如果我自己动手做点什么,我会做得更好。 他这样做并想出了vgo。 它与社区分开进行。

vgo 是否满足人们在调查和采访中表达的需求? 根据我从拉斯那里收集到的信息,他没有阅读结果,但他批评了这个过程。 可能值得有人做一个映射。

哦,在峰会上,Russ 确实分享了一个原因,当时他不喜欢 SAT 求解器。 他想要一个代码行数更少的解决方案,因为他不希望 Google 的 Go 团队因为维护那么多代码而陷入困境。 我记得那是因为我在那之后对 dep、glide 和其他工具进行了一些 LOC 比较,以便更好地了解差异。

Go 是 Google 拥有并运行的项目。 如果问题是资源之一,Go 是否应该建立在一个基金会之下,并且其他组织的人会参与其中? 这是添加资源以维护工具链的另一种方式。

不,#25483 与 vgo 无关。 我只是将它列为假设的go release辅助命令可以做的另一种事情。 但它随时都有用。

我更重要的一点是,Go 从来没有让发布 Go 包变得超级容易。 在以前的生活中,我写了http://search.cpan.org/dist/ShipIt/用于自动发布 Perl CPAN 包,当您使用工具处理这些事情而不是手动完成时,它会产生巨大的差异。

我只提到了一个假设的go release ,因为有人我 Go 可能会做哪些不同的事情来帮助人类不犯错误。

我在 vgo 上看到的一个实际问题似乎很容易解决……最大版本问题。 如果一个库与 dep 的 v1.7 中断,除了排除之外没有其他方法可以指定......这只会在 v1.8 出现之前有所帮助,这可能仍然以完全相同的方式被破坏。

似乎将 max version 添加到 vgo 只是帮助 vgo 确定两个库何时不兼容的一种方式是微不足道的。 如果一个图书馆说它需要至少 v1.7 而一个图书馆说它需要

没有这个,vgo 将只使用 1.7,并且其中一个库将中断。 如果你幸运的话,会有一个编译器错误......更有可能只是一个微妙的行为错误,直到在路上才会被注意到。 阴险的是,它可能是一些传递依赖,你甚至没有直接调用。

@natefinch最大版本限制将 MVS 推离其非常聪明的避免 SMT 求解器领域。

我认为@natefinch意味着最大版本作为最终检查/过滤器。 一旦 MVS 完成它的工作,如果不满足这些最大版本限制中的任何一个,vgo 就会出错。 这仍然没有让我们进入 SAT 求解器领域。

确切地。 没有办法解决。 只是“vgo 说 1.7 是正确的使用方法,但模块 X 声明它不适用于该版本”。 这已经是今天 vgo 可能发生的事情,如果你有一些东西说require foo 1.7和其他东西说exclude foo 1.7 ,如果没有更高的 foo 版本。

然后你应该怎么做?

你用你的人脑想出一个解决方案,因为没有机械的解决方案。 您正在尝试使用两个明确声明它们需要不兼容版本的库的库。 A 将以 1.6 打破,B 将以 1.7 打破。 结束。

您需要说服依赖作者修复错误(如果它是错误),说服库作者之一发布与新依赖版本兼容的新版本,或者分叉一大堆东西并自己修复.

没有好的答案。 但是世界上也没有任何工具可以为您解决这个问题。

@sdboyer

问题是,而且一直是,MVS 只能解决一个虚幻的问题(避免 SAT),

我无法谈论你与 Russ 的私人谈话,但“避免 SAT”对我来说似乎是一个稻草人的论点。 “避免 SAT”与“使用 SAT”一样,只是一个目标:两者都对用户没有任何具体的影响。

在我看来,MVS 解决的具体问题是:

  • 默认情况下使构建可重现,
  • 构建尽可能接近测试版本,并且
  • 避免虚假拒绝。

我们通过创建工具来帮助我们将有限的代币用于有用的合作,而不是通过创建脆弱的工具来惩罚我们的参与,从而建立一个更好的生态系统。

我同意。 但我想仔细看看你的假设:哪些工具会“惩罚”参与,为什么?

您在帖子中给出的示例之一是,“我们的项目现在依赖于[email protected] ,但它不适用于[email protected]或更新版本。 我们想成为好公民并适应,但我们现在没有带宽。”


您所做的第一个(隐含)假设是您(或您的用户)有时间和带宽针对其依赖项的所有版本详尽地测试您的项目。¹对于某些项目,该假设本身可能不成立:通过设置每个人在发现不兼容时标记上限的期望,您最终会“惩罚”没有带宽来测试回归和更新可能所有版本的边界的维护人员。

相比之下,在 MVS 下,维护者只需要声明一件事:“我们针对[email protected]进行了彻底的测试,并且成功了。” 如果您的用户不做任何事情来破坏它,他们会根据[email protected]构建,并且一切都会像以前一样继续工作。

在 MVS 下,_breakage 仅在显式升级时发生。_ 这是一个显着的好处:如果您的用户升级了一些其他依赖项,以便它引入[email protected]并中断他们对您的包的使用,那么他们可以简单地退出该升级,直到您有时间修复它,_或直到X的维护者有时间纠正不兼容性。_ 并且如果期望正确,后者可能比前者更有可能:切出如果[email protected]恢复兼容性, [email protected]可能会变成不必要的忙碌工作。


您所做的第二个假设是,影响模块_任何部分_的每个不兼容性都会影响模块的_所有用户_。 如果你的包中破坏[email protected]的部分是一个很少使用的函数,为什么要阻止你的用户在他们甚至不调用它的程序中升级X呢?

您可能会争辩说,如果模块的某些部分不依赖于其他部分,它们应该是单独的模块,但这再次迫使包维护者做额外的工作:如果我只有定期测试和升级的带宽,我可能只有维护一个模块的带宽,而不是具有细粒度依赖关系的整个细粒度模块的复杂性。


¹ 通​​常,检测与依赖项的不兼容性需要您针对这些依赖项的_所有版本的叉积_进行测试:您对[email protected]的特定使用可能很好,但它与您依赖的其他一些包结合使用会中断在。 例如,也许您和您的依赖项在全局配置中需要不兼容的选项。

他们可以简单地退出该升级

假设这是一个编译器错误或类似的高度可见的东西。 它更有可能是一个不立即显现的微妙行为错误。 你更新,运行你的测试,一切看起来都很好。 也许 10 秒的超时现在是 30 秒,并且在负载下会抛出堆栈其余部分的超时。 直到它在生产中运行时您才会注意到(几年前我们对 mgo 进行了更改,类似的事情发生在我们身上)。

如果你的包中在[email protected]下被破坏的部分是一个很少使用的函数,为什么要阻止你的用户在他们甚至不调用它的程序中升级 X?

因为如果它是传递依赖项,他们可能不知道他们是否正在使用该函数,并且谁说当您更改代码时某些未使用的代码路径不会突然被激活? 所以这个损坏的函数现在被调用了。 信任库维护者要好得多,他们是了解自己库的专家,如果他们说它不适用于 1.7,请不要使用 1.7 构建。

假设这是一个编译器错误或类似的高度可见的东西。 它更有可能是一个不立即显现的微妙行为错误。

几乎每个软件包的几乎每个版本都潜伏着细微的错误。 我们通常不会仅仅因为我们发现了一个这样的错误就将发布标记为完全不可用; 相反,我们会在以后的版本中修复它。

是什么让这类特定的错误变得特别?

谁说当您更改代码时,某些未使用的代码路径不会突然被激活?

这正是 MVS 的论点:如果您更改代码,那么您会检测到该修订版的损坏,并且您也可以更新该修订版的版本约束。

这确实意味着,作为用户,如果您一次升级一个依赖项而不是批量升级依赖项,则更容易将故障一分为二,但一般来说更改都是如此:更改越小,您可以越精确将它们一分为二,无论它们是对您的代码或其依赖项的更改。

是什么让这类特定的错误变得特别?

这不仅仅是一个错误。 它比那更大。 库作者是他们代码方面最重要的专家,他告诉全世界“哟,X 的 1.7 版以非常糟糕的方式破坏了我的东西,甚至不使用它构建”。

显然,除非它是编译器错误,否则它是一个判断调用。 如果您的 99 个函数出现恐慌,但没有一个......这只是一个错误吗? 还是完全不兼容? 如果它只是一个恐慌的功能怎么办?

在某些时候,人类必须做出这个决定。 我宁愿处理在构建时发生的先发制人的声明不兼容,也不愿担心将其投入生产的未声明的主要问题。

这不仅仅是一个错误。 它比那更大。 库作者是他们代码方面最重要的专家,他告诉全世界“哟,X 的 1.7 版以非常糟糕的方式破坏了我的东西,甚至不使用它构建”。

库作者还有另一种表达方式:编写一个在与不兼容版本的 X 一起使用时失败的测试。这将具有在 X 发布固定版本时不需要更改库的优点,以及捕获任何未来的回归。

编写一个在与不兼容的 X 版本一起使用时失败的测试。

是的,我是这么想的。 但是随后您要求每个顶级二进制文件运行所有传递依赖项的所有测试,这些测试可能需要您没有的基础设施。 并非所有测试套件都是 100% 隔离的。

go test ./...不在供应商目录中运行测试是有原因的。

总而言之(可能很糟糕),反对 vgo 的主要技术论据似乎是图书馆需要一种方法来声明对其自身依赖项的全局尊重约束。

为什么不让双方同意批准一项竞争性提案,该提案探讨 GPS2 如何适应双方喜欢的 vgo 部分。 那么,在此期间合并vgo实验,在vgo合并主线之前重温GPS2提案?

作为过去与许多依赖工具作斗争的人,我对 vgo 感到兴奋。 FWIW,作为“社区”成员,我觉得vgo解决方案远远代表了它。 但是,我仍然愿意考虑支持添加全局版本约束的论点,并期待该论点进一步发展。

只是把它扔在那里:

假设这是一个编译器错误或类似的高度可见的东西。 它更有可能是一个不立即显现的微妙行为错误。 你更新,运行你的测试,一切看起来都很好。 也许 10 秒的超时现在是 30 秒,并且在负载下会抛出堆栈其余部分的超时。

这是一般软件中的问题。 NPM(我个人最熟悉的另一个版本控制工具)使用 SAT 求解器与一个强烈接受 semver 的社区相结合,这个问题仍然存在。

就传递依赖关系的讨论而言,现实情况是没有魔杖:在某些时候,开发人员必须意识到他们使用的依赖关系,并花时间正确测试他们最终负责的代码。 出于法律和其他原因,我的雇主不能是唯一一个我们需要证明我们使用的每个图书馆的人,包括传递图书馆。

老实说,在我看来,很多对 vgo 的抱怨似乎都是“它并不完美,因此它不起作用”。 让我们不要因为缺乏完美的解决方案而实施一个好的解决方案。

对我来说,核心团队提供一个在大多数情况下都很好的工具似乎非常符合 Go 的整体理念,而将其留给社区提供更高级和/或特定的工具。

@malexdev您的演员可能存在以下论点:

让我们不要因为缺乏完美的解决方案而实施一个好的解决方案。

dep 今天是一个很好的解决方案。 由社区共同创建的一个。 如前所述,vgo 做了几个假设:

  1. 没有人会与 semver 断绝关系,即使是偶然的
  2. 我们将需要重新设计现有生态系统中的现有代码库

vgo 更多地基于“完美世界”中的“完美解决方案”,而 dep 今天的工作方式遵循其他编程语言生态系统中已经工作的方式,同时具有容错性。

你可以在我刚刚写的包管理问题空间的历史中看到这一点。

总而言之(可能很糟糕),反对 vgo 的主要技术论据似乎是图书馆需要一种方法来声明对其自身依赖项的全局尊重约束。

我同意这个总结。

为什么不让双方同意批准一项竞争性提案,该提案探讨 GPS2 如何适应双方喜欢的 vgo 部分。

在这一点上,我仍然不相信 GPS2 是必要的,甚至是一个好主意。 您在上面列出的功能可以在 MVS 之上改装为 vgo(如thisthis )。 就个人而言,我希望@sdboyer的下一篇博客文章将包含反对 MVS 本身的好论据,但现在,我真的没有看到任何不同算法的理由——尤其是一个会花费 vgo 的显着 UX 优势的算法。

不过,公平地说,我也看不出有任何理由反对这种做法。

dep 今天是一个很好的解决方案。

我不确定我是否同意。 我自己没有使用过它,但是在那里有几个人抱怨似乎可以直接追溯到其 SAT 解决方法的问题。 而且我知道我对 deps 的整体设计非常不满意(撇开依赖求解器本身的问题,以及一般的工作流程和 UX)。

  1. 没有人会与 semver 断绝关系,即使是偶然的

我很抱歉,但我多次询问此声明的理由,但没有得到任何结果。 为什么 vgo 会以任何方式、形状或形式而不是 dep 来假设这一点? 我从根本上不同意这种说法。 这是一个解释算法的假设。 就像你解释 dep 的算法一样,你会解释语义版本中的假设。 在最坏的情况下,如果您不遵守这些语义,它们会显示出相同的损坏。


我认为总的来说,区分我们正在谈论的不同部分是有意义的。 一些投诉是关于 SIV、一些 MVS 和一些一般的 vgo-shell。 例如,MVS 确实不能处理版本上限,但这并不意味着 vgo 不能处理版本上限。 SIV 需要更改大量代码(通过重写导入语句),但这并不一定意味着 vgo 需要这样做。 虽然要明确一点,但我也不认为这有什么大不了的,只要我们可以迁移。 我们可以使用哪个 AFAICT。

我刚刚观看了@rscGopherConSG 关于版本控制的开幕主题演讲。 他解决了依赖项引入破坏性更改的场景,并比较了 vgo 和 dep 将如何处理它,这似乎是这里的主要关注点。 (这是一块很棒的手表。)

如果我正确理解了他的观点,如果使用最大版本限制来避免错误版本,dep 也可能会破坏构建。 我很想看到这里那些担心 vgo 在这方面达不到 dep 的人解决这一点。

@willfaught对我们来说,当使用最大版本限制来避免错误版本时,dep 破坏构建被认为是成功的! 这就是我们想要发生的事情。 Russ 正确地指出,这个问题不会自动解决。 Helm>2.0.0的约束不会自动将用户升级到“ [email protected] ”,但如果用户明确依赖“Helm”,它会成功(降级 grpc 或触发构建失败,如果这是不可能的) ==2.1.4"。 就个人而言,当遇到库问题时,我通常会尝试的第一件事是强制更新到最新版本。 使用 Dep,这将通知我 GRPC 1.4.0 引入的故障。 使用vgo ,Helm 向我传达这一点的唯一方法是通过文档。

重复这一点,因为它仍然不被理解: depvgo都不能阻止这个问题的发生或提供一个万无一失的解决方案。 相反, dep允许 Helm传达存在问题的信息,而vgo则不允许。

总而言之(可能很糟糕),反对 vgo 的主要技术论据似乎是图书馆需要一种方法来声明对其自身依赖项的全局尊重约束。

我会为这个总结添加一些颜色,因为需要这些约束来处理违反兼容性规则时的不愉快路径。 vgo建立导入兼容性规则,然后假设始终遵循该规则,继续开发解决方案。 在这个理想化的世界中,对上限的需求是有限的,甚至根本不存在。 在现实世界中,这不会发生:开发人员会有意或无意地发布破坏兼容性的更新。

我认为@Merovius正在寻找一个可行的解决方案。 MVS 将按照指定进行,然后在解析完成后,vgo 将检查每个已解析的包以查找排除项。 如果找到,则将这些报告给最终用户,最终用户可以选择更改其依赖项以满足这些约束,或者选择覆盖和忽略这些约束。 我也站在另一边,有时你比维护者更了解; 它适用于您的应用程序,这就是您所关心的。

这恢复了中间库向最终用户传达不兼容性的路径。 再次重复 helm 示例:

用户@ 1.1 :头盔@2.1.2
[email protected] : GRPC @1.3.5

==> 用户有意升级到[email protected]

用户:Helm @2.1.3, [email protected] (“Helm 更新了,我终于可以使用 grpc 1.4 了!”)
头盔:GRPC @1.4.0

==> 检测到错误! 用户发现 Helm 存在一些问题,因此请检查新版本。

用户: [email protected] , [email protected]
Helm:GRPC @1.3.5,弱 GRPC <1.4.0

用户看到 GRPC 1.4.0 被[email protected] 安装拒绝的警告。 由于 Helm 对​​我来说坏了,这就是我要修复的问题,我删除了对 GRPC 1.4.0 的依赖(遗憾地失去了一些有用的功能)并重新运行 MVS。 这一次,MVS 将 GRPC 解析为 1.3.5,将 Helm 解析为 2.1.4,重新检查所有弱约束,发现它们成立,我就完成了。

我不指望任何工具能神奇地解决这些问题,但我确实希望有一些作为中间件库的资源。 据我所知,如果我想让我的用户免受这些依赖项的兼容性问题, vgo中的唯一选择是分叉并重命名我所有的上游依赖项(或等效地,将它们复制到我的项目中)。 我认为没有人想要那样。

@willfaught对我们来说,当使用最大版本限制来避免错误版本时,dep 破坏构建被认为是成功的! 这就是我们想要发生的事情。

谈话中的观点是,在这种情况下,vgo 并不比 dep 差。 在他的示例中,构建并没有中断,因为 dep 找不到解决方案; 从 dep确实找到解决方案的意义上讲,它已经被破坏了,并且该解决方案包含错误的版本,从而导致了我们想要避免的同样糟糕的情况。

你真的应该看视频,它介绍了一个很好的例子,但这是我理解/记住的要点:

  • 包版本(包括它们的依赖要求)是不可变的
  • 要为包的现有依赖项添加最大版本限制,您必须发布包的新版本。
  • dep 可能会选择您的包的先前版本以满足所有要求,在这种情况下,您的新的最大版本限制将不存在。 毕竟,这允许使用坏版本。

我基本同意添加最大版本排除的提议,但我确实有这个担心:假设我在我的库中放入“使用 gRPC >1.4, <1.8”然后在 gRPC 1.9 中,作者决定,“你知道吗,Helm 是是的,我们在 1.8 中进行了重大更改,我们正在恢复到 1.9.0 中的先前行为。” 现在尝试导入 Helm+gRPC 的人将无法使用 1.9,直到 Helm 发布“使用 gRPC >1.4,除了 1.8.0、1.8.1、1.8.2,但 1.9+ 很酷”的版本。

换句话说,也许exclude grpc 1.8就足够了,因为在 gRPC 1.9 发布之前我们不会知道它是否不兼容,此时 Helm 可以将其添加到排除列表中。

我对这个空间基本上一无所知。 但从这里的讨论来看,听起来最大的分歧归结为如何发现错误的案例。 也就是说,MVS (vgo) 和 SAT (dep) 都或多或少地处理正常情况,也许不完全相同,但足够好。

SAT 提供了 MVS 不具备的能力:使用 SAT,库包 P1 的作者可以声明“P1 需要 P2,并且适用于 P2Version > 1.1 和 P2Version < 1.4”。 MVS 只能声明“P1 需要 P2,并且适用于 P2Version > 1.1”,而不能表达“P2Version < 1.4”的限制。 在正常情况下,这无关紧要。 仅当某些操作尝试将 P2 升级到版本 1.4 时才有意义。 在这种情况下,SAT 会报错,而 MVS 不会。 在使用 MVS 时,如果不兼容不是编译错误,则可能在事后很久之后导致失败。

毫无疑问,SAT 支持者看到了 MVS 的其他主要问题,但到目前为止,这是我所理解的。

我认为值得注意的是,如果限制表达式本身是版本化的——如果它们是 P1 特定版本的一部分——那么在正常情况下,在 P2 版本 1.4 发布之前,P1 版本 2.2 会很高兴地说“P2Version > 1.1"。 只有当 P2 版本 1.4 发布时,P1 作者才会注意到不兼容,并以“P2Version > 1.1 和 P2Version < 1.4”发布 P1 版本 2.3。 因此,如果您使用的是 P1 2.2 版,SAT 和 MVS 都不会报告将 P2 升级到 1.4 版的任何问题,尽管它会以某种可能微妙的方式失败。

换句话说,虽然 P1 的版本列出 P2 的最低兼容版本是完全有意义的,但如果该版本确实与 P2 的最新版本正常工作,那么一个版本列出最大兼容版本是没有意义的. 最大兼容版本要么是保守的,因此随着 P2 的更新和更好版本的出现而越来越错误,或者如果 P2 在未来以某种不兼容的方式发生变化,将无法指定该要求,因为在发布时它没有不存在。

因此,如果我们想要一个系统定义最低版本要求以外的任何内容,那么这些要求不能是特定版本的一部分,而是必须是与包关联的某种元数据的一部分,元数据可以在任何时候都无需更新软件包本身。 这意味着“更新此包”操作必须与“检查我当前的包版本是否兼容”操作分开。

我会进一步声称——这肯定比上面更脆弱——如果“检查我当前的包版本是否兼容”失败,那么相信任何解决问题的工具通常都是不明智的。 如果兼容性问题不能通过简单的操作“将所有相关包升级到当前版本”来解决,那就需要考虑了。 一个工具可以引导这种想法,但通常它不能取代它。 特别是一个工具自动开始降级包似乎是非常不明智的。

所以如果我们考虑

  1. 一种定义描述包不兼容性的包元数据的方法
  2. 基于此,一个报告您当前的软件包是否兼容的工具

那么也许MVS和SAT之间的一些主要区别变得不那么重要了。

谢谢你说得这么好,伊恩。 跟进,一旦我们建立了版本和 vgo,我们绝对希望有一个新的 godoc.org(可能是一个不同的名称)来记录有关包的附加信息,go 命令可以查阅的信息。 并且其中一些信息将是成对的不兼容,go 命令可以在任何特定构建中报告为警告或错误(即报告损坏,而不是试图通过解决它来隐藏它)。 但是,在核心工具链中拥有版本是第一步,再加上最小的版本要求和语义导入版本控制,这就是本期所接受的。


我们致力于尽可能顺利地实现这一目标。 这将需要额外的工具、更多的教育推广和 PR 来解决现有软件包中的问题。 接受这个提议的所有事情都被阻止了,因为在不接受整体方法的情况下继续前进似乎很冒昧。 但该提议被接受了,既然不确定性已经结束,工作将开始更加积极地落地。

我对版本兼容性的外部信息有同样的想法......因为版本兼容性必须在整个范围内保持不变,所以它不需要在源代码控制中(事实上,如上所述,在源代码控制中是一个明显的缺点)。 如果对此有提议的解决方案会很好,因为它似乎肯定是提议的 MVS 的一个主要问题。

看到讨论朝着这个方向有机地发展真是太棒了。 这是我关注的一个中心点,当人们已经大部分时间解决基本问题时,它使解释基本问题变得容易得多。

@ianlancetaylor ,我认为您对需要能够对已发布版本的约束信息进行更改的观察很满意。 正如@rsc 所指出的,这样的服务是我们在会议中讨论过/我建议的。 当然,我们可以通过 godoc.org 或其他方式来实现。 但我实际上并不认为它需要单独的服务,如果没有它会更好。 我在周五发表的文章中快速引用了这一点(就在那个锚点上)。 如果不出意外,在服务中,有一些问题需要回答,即谁的不兼容性声明应该出现在警告中,这意味着处理身份,以及我们如何将声明范围限定为 depgraph 中的特定情况。 将声明保存在元数据文件中意味着我们不必担心这些。 但稍后会详细介绍。

这里真正重要的是这一点,尽管可能不是您想要的方式:

也许MVS和SAT之间的一些主要区别变得不那么重要了。

执行此搜索的元工具的建议 - 是的,这是一个 SAT 搜索 - 作为人们正在识别的问题的解决方案是有说服力的。 如果 MVS 像描述的那样继续进行,这几乎正是我们必须将 dep 变成的内容。 首先要注意的是,如果我们非常担心这些不兼容性,以至于我们谈论的是搜索工具,那么我们实际上要说的是,MVS 只是更大算法中的一个步骤,而grokkability 的好处就在窗外。

但更糟糕的是,因为再多的元工具都无法解决由于将最小版本和当前版本压缩在一起而导致的信息丢失问题。 这样做的最大结果是级联回滚,这意味着实际上尝试修复此列表中的任何不兼容性很可能最终会退回与您的问题无关的依赖关系图的其他部分。 而且,开发人员将无法遵循对他人无害的更新策略。 . (哦,还有幻象规则,但这通常只是 MVS 的副作用)。

这就是为什么我断言 MVS 是一个不适合在其上构建像这样的高阶工具的中间层 - “不适合目的”。 很明显,人们相信这些不兼容会发生,所以 MVS 只是解决一个难题并使其变得更难。

相反,如果我们将“不兼容服务”的问题统一到元数据文件中,那么我相信仅使用一组简单的成对声明就可以达到相同的效果。 (这是概念的草稿,但似乎越来越紧密)

这将需要 MVS 的某些部分发生变化,但 MVS 仍然可以运行在那里编码的信息之上。 如果不兼容性真的发疯了,那将很有用,并且您只想避免所有这些。 但主要算法将从看起来像 MVS 的基线开始,然后切换到更广泛的搜索(要清楚,MVS 本身仍应被视为搜索),而没有可能进入荒谬的旧版本。

(注意,我这周要放假,所以要到下周末才会回复)

@sdboyer

执行此搜索的元工具的建议 - 是的,那是 SAT 搜索

你可以说得更详细点吗? 您引用的这句话是在 Ian 建议一个工具来报告所选版本是否兼容之后 - 据我所知,这是这里建议的主要替代方案(这当然是我上面的意图)。 这个问题绝对不是搜索,它是微不足道的,不需要解决 SAT(它只是评估给定值集的布尔公式,而不是试图找到满足它的值)。

是的,简单地报告公式中有一些已知不兼容的值不需要求解 SAT。 在此基础上采取任何行动,例如使用一种工具来帮助查找其中没有此类值的结果。

我引用这句话并不是因为我认为它表明人们已经接受了搜索,而是因为如果我们认为报告这些情况很重要,那是因为我们相信我们很可能会遇到这种情况。

问题是,一旦确定了解决这些案例的合理性和重要性,看起来人们就会错误地跳出“我们可以在 MVS 之上做所有搜索的事情,就可以了”。 我们可以,但是由于 MVS 在设计上切断了有用的可能路径,因此处理此类尝试变得更加棘手。

在美国东部时间 2018 年 5 月 28 日下午 4:02:13,Axel Wagner [email protected]写道:

@sdboyer

执行此搜索的元工具的建议 - 是的,那是
SAT搜索

你可以说得更详细点吗? 你引用的那句话就在伊恩之后
建议一个工具来报告所选版本是否兼容- 据我所知,这是主要的
这里建议的替代方案(这当然是我上面的意图)。
这个问题绝对不是搜索,它是微不足道的
不需要解决 SAT(它只是评估一个布尔公式
对于给定的一组值,而不是试图找到满足它的值)。

--
你收到这个是因为你被提到了。
直接回复此邮件或在 GitHub 上查看:
https://github.com/golang/go/issues/24301#issuecomment -392595150

我引用这句话并不是因为我认为它表明人们已经接受了搜索,而是因为如果我们认为报告这些情况很重要,那是因为我们相信我们很可能会遇到这种情况。

需要明确的是:以这种方式修改上限的建议纯粹是对提出的问题的反应,并表明它可以做到(批判性地质疑 MVS 从根本上不适合目的的说法)。 把这种让步和妥协的意愿作为我们认为你一直都是对的证据似乎有点不公平。

对我来说,这种说法(MVS 不合适,而且基本上是朝着错误方向迈出的不可逆转的一步)是我个人所面临的挑战,也是我正在阅读你的论点的镜头。 其中一个论点是,如果我们可以声明不兼容并让版本选择算法在遇到它们时失败,那么它就是一个特性。 另一个公平的论点是,如果它们发生,让算法能够为我们解决它们会很好(这确实需要 SAT 求解器)。

然而,虽然我认为这些是有效和公平的担忧,但我不相信他们通过了向我证明 MVS 从根本上不合适的门槛。 我仍然相信以 MVS 为起点会带来好的和重要的特性。 而且,如果这些担忧在实践中导致严重的痛苦,我们仍然有很多方法可以迭代 - 从添加上限(无论是作为go.mod的一部分还是作为单独的服务)与纯粹的失败直到并包括在某些时候添加完整的 SAT 求解器和锁定文件。 即我同意你的观点,这些事情会发生,但是 a) 我(也许天真地)乐观地认为它们不会像你预期的那样造成痛苦,b) 即使我们从 MVS 开始,它们也是可以解决的问题。

我突然想到,确定兼容性的源代码控制之外的某些东西会改变 MVS 系统的确定性。 如果您在一个库中将 foo >= 1.5.0 作为约束,而另一个库的 foo >= 1.6.0。 然后把这两个放在一个二进制文件中,然后他们选择 1.6.0。 在 MVS 中,这就是可重复构建所需的全部内容。 它总是选择 1.6

但是,如果您将外部兼容性添加到混合中,那么您可以更新第一个库以说它与 1.6 不兼容,然后算法会选择 1.7,即使代码没有更改......这意味着您需要再次锁定文件。

作为参考,我不认为锁定文件是一件坏事。 很高兴有一个明确列出您需要构建的内容。 这应该让它很快。 不需要神奇的逻辑。

@natefinch如果应用程序的 go.mod 文件更新为需要 v1.7.0,因为外部兼容性工具指示 v1.6.0 不兼容,则您不需要锁定文件。 因为指定 v1.7.0 存在于 go.mod 文件中,所以作者还可以添加注释说明为什么使用 v1.7.0 以及该信息对读者有用。

@leighmcculloch ,如果应用程序中的任何文件被更新,那么它是一个不同的构建,并且完全超出了“没有锁定文件的可重现构建”问题的范围。

提出带外兼容性信息以反映知识的发展方式:在发布时没有已知不兼容性,但随后变得明显,并发布了有关已发布版本的额外信息。 恕我直言,根据定义,这种方法会导致依赖关系的提取方式发生变化,否则为什么要提取不兼容信息呢?

@redbaron @natefinch

不兼容信息的重点是图书馆的作者将不兼容信息传达给他们的用户。 该信息是在构建时使用还是在发布时使用是一个不同的问题。

在 vgo 的情况下,当前的想法是只显示警告(或潜在的 croak)。 但值得注意的是不要让它影响所用版本的选择(因为这需要解决 SAT)。 所以实际上没关系,您可以在其中一个或两个上使用它,它会很好地履行其职责,同时保留可重复性¹。

在 dep 中,这些信息只在发布时使用,然后记录到一个锁定文件中,该文件在构建时使用。 因此,似乎我们无论如何都在考虑发布时使用“足够好”,至少在涉及 vgo 与 dep 的问题时。

不过,我仍然认为我们现在不必真正回答这些问题。


[1] 就我个人而言,我认为在发布时使用它并且只有在构建时使用-v会更好,因为用户不必决定警告是否可操作。

@rsc写道:

跟进,一旦我们建立了版本和 vgo,我们绝对希望有一个新的 godoc.org(可能是一个不同的名称)来记录有关包的附加信息,go 命令可以查阅的信息。 并且其中一些信息将是成对的不兼容,go 命令可以在任何特定构建中报告为警告或错误(即报告损坏,而不是试图通过解决它来隐藏它)。

我想知道是否有必要记录成对的不兼容性。 我目前看到的方式是,模块A@vN和模块B@ vM 之间的任何不兼容实际上是因为 B 从 L < M 的某个版本 vL 进行了不兼容的更改。

如果模块 B没有做出不兼容的更改,那么模块 A 只是有一个错误。 如果是这样,那么问题在于 B 本身,而不是 A 和 B 的配对。

因此 ISTM 认为任何模块元数据的公共存储库都只能记录任何模块与其自身先前版本的不兼容性,这可能会使问题更容易处理。 这些不兼容性报告与错误报告非常相似,尽管它们无法解决,因为一旦发布版本,就无法更改。

当您升级模块版本时,go 工具可以考虑元数据并拒绝考虑与任何当前选择的版本不兼容的版本。 我认为这避免了解决SAT的需要。 它还可以确定给定模块有太多不兼容报告并拒绝将其添加为依赖项。

一组形式的元组(模块旧版本新版本描述)可能就足够了。

go 工具可以考虑元数据并拒绝考虑与任何当前选择的版本不兼容的版本

当然,当您添加多个依赖项时,这不起作用,它们之间最终需要使用相互不兼容的版本,因为新版本不是现有模块的一部分,但可能有合理的启发式可用。 这不是至关重要的 AFAICS,因为应该很少添加依赖项。

我担心go release正在成为这个讨论的“足够聪明的编译器” 。 用户对 Go 1.11/12 中的go release有什么具体期望? 我认为这对围绕 MVS/SIV 的合理期望有所不同。

感谢你们中的许多人为 Go 带来的能量,尤其是这个提议。

提案过程的第一个目标是“[m]确保提案得到正确、公平、及时、有记录的评估和明确的答案。” 对该提案进行了详细讨论,我们发布了讨论摘要。 经过六周和多次讨论后,提案审查委员会——因为我写了提案而作为仲裁者介入——接受了该提案。

单个 GitHub 问题很难进行广泛讨论,因为 GitHub 没有针对不同对话链的线程,甚至不再显示所有评论。 像这样的讨论完全有效的唯一方法是积极策划讨论摘要。 到提案被接受时,即使是摘要也变得笨拙。

现在提案被接受,这个问题不再适合讨论,我们也不再更新摘要。 相反,请针对您遇到的问题提出新的、有针对性的问题或具体的更改建议,以便我们可以针对每个特定主题进行集中讨论。 请在这些新问题前加上“x/vgo:”。 如果你在新一期的正文中提到了#24301,那么这里会交叉引用以供其他人查找。

最后一点是,接受提案意味着接受这个想法,而不是原型实现错误等等。 还有一些细节需要解决,还有一些错误需要修复,我们将继续共同努力。

再次感谢你的帮助。

还有更多工作要做(参见模块标签),但本期提出的初始模块实现已提交到主树,因此我将关闭此期。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

OneOfOne picture OneOfOne  ·  3评论

gopherbot picture gopherbot  ·  3评论

mingrammer picture mingrammer  ·  3评论

rakyll picture rakyll  ·  3评论

natefinch picture natefinch  ·  3评论