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。如果您有在模块感知模式下无法构建的项目,现在是迁移的时候了。如果存在阻止您迁移的问题,请考虑提交 问题经验报告

不会自动更改 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 目录(如果存在)(有关详细信息,请参阅 供应商)。go getgo mod tidy 这样的命令仍然会修改 go.modgo.sum,因为它们的主要目的是管理依赖项。

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

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

go install golang.org/x/tools/[email protected]

使用此语法时,go install 会从该确切的模块版本安装命令,忽略当前目录和父目录中的任何 go.mod 文件。(没有 @version 后缀,go install 会像往常一样继续运行,使用当前模块的 go.mod 中列出的版本要求和替换来构建程序。)

我们以前建议使用 go get -u program 来安装可执行文件,但这会导致对 go get 的含义产生太多混淆,因为 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 停止安装命令二进制文件。有关详细信息,请参阅 问题 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 的用户将在检查更新或升级依赖包时收到撤回通知。通知消息可能包含撤回指令上方的注释中的文本。

$ go list -m -u all
example.com/lib v1.0.0 (retracted)
$ go get .
go: warning: example.com/[email protected]: 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 直接从版本控制存储库下载。直接版本控制访问非常重要,尤其是对于在代理上不可用的私有模块而言,但它也可能存在安全问题:版本控制工具中的错误可能被恶意服务器利用来运行非预期代码。

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

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

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

GOVCS=public:git|hg,private:all

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

下一步是什么?

我们希望您发现这些功能有用。我们已经在为 Go 1.17 开发下一组模块功能,尤其是 延迟模块加载,它应该使模块加载过程更快、更稳定。与往常一样,如果您遇到新的错误,请在 问题跟踪器 上告知我们。祝您编程愉快!

下一篇文章:上下文和结构体
上一篇文章:Go 1.16 发布
博客索引