Go 博客

Go 1.16 的新模块变更

Jay Conrod
2021 年 2 月 18 日

希望您喜欢 Go 1.16!此版本带来了许多新功能,尤其是针对模块。 发布说明 简要描述了这些更改,但让我们深入探讨其中一些。

模块默认启用

go 命令现在默认以模块感知模式构建包,即使没有 go.mod 文件也是如此。这是在所有项目中推广使用模块的重要一步。

通过设置 GO111MODULE 环境变量为 off,仍然可以在 GOPATH 模式下构建包。您也可以将 GO111MODULE 设置为 auto,仅在当前目录或任何父目录中存在 go.mod 文件时启用模块感知模式。这以前是默认设置。请注意,您可以使用 go env -w 永久设置 GO111MODULE 和其他变量。

go env -w GO111MODULE=auto

我们计划在 Go 1.17 中放弃对 GOPATH 模式的支持。换句话说,Go 1.17 将忽略 GO111MODULE。如果您有不适合在模块感知模式下构建的项目,现在是时候迁移了。如果您有阻止您迁移的问题,请考虑提交一个 issue 或一个 experience report

go.mod 和 go.sum 无自动更改

以前,当 go 命令发现 go.modgo.sum 存在问题时,例如缺少 require 指令或缺少校验和,它会尝试自动修复问题。我们收到了大量反馈,认为这种行为令人惊讶,特别是对于像 go list 这样通常没有副作用的命令。自动修复并不总是理想的:如果导入的包没有由任何必需的模块提供,go 命令会添加一个新的依赖项,可能触发常见依赖项的升级。即使是拼写错误的导入路径也会导致(失败的)网络查找。

在 Go 1.16 中,模块感知命令在发现 go.modgo.sum 中的问题后会报告错误,而不是尝试自动修复问题。在大多数情况下,错误消息会推荐一个用于修复问题的命令。

$ go build
example.go:3:8: no required module provides package golang.org/x/net/html; to add it:
    go get golang.org/x/net/html
$ go get golang.org/x/net/html
$ go build

与之前一样,go 命令可以使用 vendor 目录(如果存在)(有关详细信息,请参阅 Vendoring)。像 go getgo mod tidy 这样的命令仍然会修改 go.modgo.sum,因为它们的主要目的是管理依赖项。

安装特定版本的可执行文件

go install 命令现在可以通过指定 @version 后缀来安装特定版本的可执行文件。

go install golang.org/x/tools/gopls@v0.6.5

使用此语法时,go install 将安装该精确模块版本的命令,忽略当前目录及其父目录中的任何 go.mod 文件。(如果不带 @version 后缀,go install 将继续按原样运行,使用当前模块的 go.mod 中列出的版本要求和替换来构建程序。)

我们过去曾推荐使用 go get -u program 来安装可执行文件,但这会与 go get 用于在 go.mod 中添加或更改模块版本要求的含义产生过多混淆。为了避免意外修改 go.mod,人们开始建议使用更复杂的命令,例如

cd $HOME; GO111MODULE=on go get program@latest

现在我们可以使用 go install program@latest 来代替。有关详细信息,请参阅 go install

为了消除有关使用哪个版本的歧义,当使用此安装语法时,程序的 go.mod 文件中允许存在的指令有一些限制。特别是,replaceexclude 指令目前不允许。长远来看,一旦新的 go install program@version 在足够多的用例中运行良好,我们计划让 go get 停止安装命令二进制文件。有关详细信息,请参阅 issue 43684

模块撤回

您是否曾不小心在模块版本准备好之前发布过它?或者在版本发布后立即发现了一个需要快速修复的问题?已发布版本的错误很难纠正。为了保持模块构建的确定性,已发布版本在发布后无法修改。即使您删除或更改了版本标签,proxy.golang.org 和其他代理可能已经缓存了原始版本。

模块作者现在可以使用 go.mod 中的 retract 指令来 *撤回* 模块版本。已撤回的版本仍然存在并且可以下载(因此依赖于它的构建不会中断),但 go 命令在解析如 @latest 这样的版本时不会自动选择它。go getgo list -m -u 将会打印关于现有使用的警告。

例如,假设一个流行的库 example.com/lib 的作者发布了 v1.0.5,然后发现了一个新的安全问题。他们可以在其 go.mod 文件中添加如下指令:

// Remote-triggered crash in package foo. See CVE-2021-01234.
retract v1.0.5

接下来,作者可以标记并推送版本 v1.0.6,这是新的最高版本。此后,已经依赖 v1.0.5 的用户将在检查更新或升级依赖包时收到撤回通知。通知消息可能包含 retract 指令上方的注释文本。

$ go list -m -u all
example.com/lib v1.0.0 (retracted)
$ go get .
go: warning: example.com/lib@v1.0.5: retracted by module author:
    Remote-triggered crash in package foo. See CVE-2021-01234.
go: to switch to the latest unretracted version, run:
    go get example.com/lib@latest

对于交互式、基于浏览器的指南,请查看 play-with-go.dev 上的 撤回模块版本。有关语法详情,请参阅 retract 指令文档

使用 GOVCS 控制版本控制工具

go 命令可以从镜像(如 proxy.golang.org)下载模块源代码,或者直接从版本控制仓库使用 githgsvnbzrfossil 下载。直接访问版本控制很重要,特别是对于不在代理上的私有模块,但它也可能是一个安全问题:版本控制工具中的一个 bug 可能被恶意服务器利用来运行未经授权的代码。

Go 1.16 引入了一个新的配置变量 GOVCS,它允许用户指定哪些模块可以使用特定的版本控制工具。GOVCS 接受一个逗号分隔的 pattern:vcslist 规则列表。pattern 是一个 path.Match 模式,匹配模块路径的一个或多个前导元素。特殊模式 publicprivate 分别匹配公共和私有模块(private 定义为 GOPRIVATE 中模式匹配的模块;public 是其他所有模块)。vcslist 是一个管道分隔的允许的版本控制命令列表,或者关键字 alloff

例如

GOVCS=github.com:git,evil.com:off,*:git|hg

使用此设置,可以通过 git 下载路径在 github.com 上的模块;不能通过任何版本控制命令下载路径在 evil.com 上的模块,并且所有其他路径(* 匹配所有)可以使用 githg 下载。

如果未设置 GOVCS,或者模块不匹配任何模式,go 命令将使用以下默认设置:公共模块允许使用 githg,私有模块允许使用所有工具。允许仅使用 Git 和 Mercurial 的理由是,这两个系统对作为不受信任服务器的客户端运行时的问题给予了最多的关注。相比之下,Bazaar、Fossil 和 Subversion 主要在受信任、已认证的环境中使用,并且没有像攻击面那样受到严格审查。也就是说,默认设置是:

GOVCS=public:git|hg,private:all

有关详细信息,请参阅 使用 GOVCS 控制版本控制工具

下一步是什么?

希望您觉得这些功能很有用。我们正在努力开发 Go 1.17 的下一组模块功能,特别是 延迟模块加载,这将使模块加载过程更快、更稳定。一如既往,如果您遇到新的 bug,请在 issue tracker 上告知我们。祝您编码愉快!

下一篇文章:Contexts and structs
上一篇文章:Go 1.16 已发布
博客索引