Go 博客

2019 年 Go Modules

Russ Cox
2018 年 12 月 19 日

多么棒的一年!

2018 年对于 Go 生态系统来说是极好的一年,其中包管理是我们关注的主要焦点之一。在二月,我们发起了一场社区范围的讨论,探讨如何将包管理直接集成到 Go 工具链中,并在八月,我们在 Go 1.11 中交付了该功能——Go modules 的第一个粗略实现。迁移到 Go modules 将是自 Go 1.0 以来对 Go 生态系统影响最深远的变革。将整个生态系统——代码、用户、工具等——从 GOPATH 迁移到 modules 将需要在许多不同领域付出努力。模块系统反过来将帮助我们为 Go 生态系统提供更好的认证和更快的构建速度。

这篇博文是 Go 团队在 2019 年关于 modules 的计划预览。

发布

Go 1.11 于 2018 年 8 月发布,引入了对 modules 的初步支持。目前,modules 支持与传统的基于 GOPATH 的机制并行维护。当 `go` 命令在 GOPATH/src 外部的目录树中运行时,并在根目录中由 `go.mod` 文件标记时,它默认处于 module 模式。可以通过设置过渡性环境变量 `$GO111MODULE` 为 `on` 或 `off` 来覆盖此设置;默认行为是 `auto` 模式。我们已经看到了 Go 社区在 modules 方面的显著采用,以及许多有用的建议和错误报告,以帮助我们改进 modules。

Go 1.12 定于 2019 年 2 月发布,将完善 modules 支持,但仍默认保持 `auto` 模式。除了许多错误修复和其他小改进之外,Go 1.12 中最重要的变化可能是,像 `go run x.go` 或 `go get rsc.io/2fa@v1.1.0` 这样的命令现在可以在 `GO111MODULE=on` 模式下运行,而无需显式的 `go.mod` 文件。

我们的目标是让定于 2019 年 8 月发布的 Go 1.13 默认启用 module 模式(即,将默认值从 `auto` 更改为 `on`)并弃用 GOPATH 模式。为了实现这一目标,我们一直在努力改进工具支持以及对开源 module 生态系统的支持。

工具 & IDE 集成

在我们拥有 GOPATH 的八年中,已经创建了大量假设 Go 源代码存储在 GOPATH 中的工具。迁移到 modules 需要更新所有做出此类假设的代码。我们设计了一个新的包,golang.org/x/tools/go/packages,它抽象了查找和加载给定目标 Go 源代码信息的操作。这个新包会自动适应 GOPATH 和 modules 模式,并且还可以扩展到特定于工具的代码布局,例如 Bazel 使用的布局。我们一直在与 Go 社区的工具作者合作,帮助他们在工具中采用 golang.org/x/tools/go/packages。

作为这项工作的一部分,我们还致力于将 gocode、godef 和 go-outline 等各种源代码查询工具统一到一个工具中,该工具可以从命令行使用,并且还支持现代 IDE 使用的语言服务器协议

向 modules 的过渡以及包加载的变化也促使 Go 程序分析发生了重大变化。作为重写 `go vet` 以支持 modules 的一部分,我们引入了一个用于增量分析 Go 程序的通用框架,在该框架中,分析器一次针对一个包调用。在此框架中,一个包的分析可以将事实写出,供导入第一个包的分析的其他包使用。例如,`go vet` 对log 包的分析确定并记录了 `log.Printf` 是 `fmt.Printf` 的包装器的这一事实。然后 `go vet` 可以在调用 `log.Printf` 的其他包中检查 printf 风格的格式字符串。这个框架应该能够支持许多新的、复杂的程序分析工具,以帮助开发人员更早地发现 bug 并更好地理解代码。

Module 索引

`go get` 原始设计中最重要的一部分是它是去中心化的:我们当时认为——并且今天仍然认为——任何人都可以将他们的代码发布到任何服务器上,这与 Perl 的 CPAN、Java 的 Maven 或 Node 的 NPM 等中心化注册表不同。将域名放在 `go get` 导入空间的开头重用了现有的去中心化系统,并避免了重新解决谁可以使用哪些名称的问题。它还允许公司在私有服务器上导入代码,同时导入公共服务器上的代码。在转向 Go modules 时,保持这种去中心化至关重要。

Go 依赖的去中心化带来了许多好处,但也带来了一些显著的缺点。第一个是很难找到所有公开可用的 Go 包。每个想要提供包信息的网站都必须自己进行抓取,否则就必须等到用户查询某个特定包时才能获取它。

我们正在开发一项新服务,即 Go Module Index,它将提供一个公共日志,记录进入 Go 生态系统的包。像 godoc.org 和 goreportcard.com 这样的网站将能够监视此日志的新条目,而不是各自独立实现查找新包的代码。我们还希望该服务能够通过简单的查询来查找包,以便 `goimports` 可以为尚未下载到本地系统的包添加导入。

Module 认证

如今,`go get` 依赖于连接级认证(HTTPS 或 SSH)来检查它是否正在与正确的服务器通信以下载代码。代码本身没有额外的检查,这使得在 HTTPS 或 SSH 机制以某种方式被破坏时存在中间人攻击的可能性。去中心化意味着构建代码是从许多不同的服务器获取的,这意味着构建依赖于许多系统来提供正确代码。

Go modules 设计通过在每个模块中存储 `go.sum` 文件来改进代码认证;该文件列出了每个模块依赖项的预期文件树的加密哈希。使用 modules 时,`go` 命令使用 `go.sum` 来验证依赖项是否与预期的版本逐字相同,然后再将它们用于构建。但 `go.sum` 文件仅列出该模块使用的特定依赖项的哈希。如果你使用 `go get -u` 添加新依赖项或更新依赖项,`go.sum` 中没有相应的条目,因此对下载的字节没有直接认证。

对于公开可用的 modules,我们打算运行一个名为notary的服务,该服务会跟随 module index 日志,下载新 modules,并以“module M 在版本 V 具有文件树哈希 H”的形式对语句进行加密签名。notary 服务将在一个可查询的、证书透明度风格的防篡改日志中发布所有这些公证的哈希,以便任何人都可以验证 notary 是否行为正确。此日志将作为公共的、全局的 `go.sum` 文件,`go get` 可以使用它在添加或更新依赖项时对 modules 进行认证。

我们的目标是在 Go 1.13 中开始,让 `go` 命令检查 `go.sum` 中尚未存在的公开可用的 modules 的公证哈希。

Module 镜像

由于去中心化的 `go get` 从多个源服务器获取代码,因此获取代码的速度和可靠性仅取决于最慢、最不可靠的服务器。在 modules 出现之前,唯一的防御措施是将依赖项打包到自己的存储库中。虽然打包将继续得到支持,但我们更希望找到一个适用于所有 modules(而不仅仅是你已经使用的那些)的解决方案,并且不需要将依赖项复制到使用它的每个存储库中。

Go module 设计引入了 module proxy 的概念,它是一个 `go` 命令向其请求 modules 的服务器,而不是源服务器。一种重要的代理类型是module mirror,它通过从源服务器获取 modules 来响应请求,然后缓存它们以供将来的请求使用。一个运行良好的 mirror 即使在一些源服务器宕机时也应该快速可靠。我们计划在 2019 年为公开可用的 modules 推出一个 mirror 服务。GoCenter 和 Athens 等其他项目也在计划推出 mirror 服务。(我们预计公司将有多种选项来运行自己的内部 mirror,但这篇博文重点关注公共 mirror。)

Mirrors 的一个潜在问题是它们正是中间人服务器,这使它们成为攻击的自然目标。Go 开发人员需要一些保证,即 mirrors 提供与源服务器相同的字节。上一节所述的 notary 流程恰恰解决了这个问题,并且它将同时适用于使用 mirrors 和源服务器的下载。mirrors 本身不必被信任。

我们的目标是让 Google 运行的 module mirror 从 Go 1.13 开始在 `go` 命令中默认可用。使用备用 mirror,或根本不使用 mirror,都可以轻松配置。

Module 发现

最后,我们之前提到 module index 将使构建 godoc.org 等网站更加容易。我们在 2019 年的工作将包括对 godoc.org 的重大改造,使其对需要发现可用 modules 并决定是否依赖给定 module 的开发人员更有用。

大局

此图展示了 module 源代码如何通过本文中的设计进行传输。

以前,所有 Go 源代码的消费者——`go` 命令和 godoc.org 等任何网站——直接从每个代码主机获取代码。现在,它们可以从快速可靠的 mirror 获取缓存的代码,同时仍然能够验证下载的字节是否正确。而 index 服务使 mirrors、godoc.org 和任何其他类似网站能够轻松跟上每天添加到 Go 生态系统的所有精彩新代码。

我们对 Go modules 在 2019 年的未来感到兴奋,希望你也是。新年快乐!

下一篇文章:Go 1.12 已发布
上一篇文章:Go 2,我们来了!
博客索引