Go 博客

Go 1.16 中的新模块变化

Jay Conrod
18 February 2021

我们希望您喜欢 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 或一份 体验报告

go.mod 和 go.sum 不再自动更改

以前,当 go 命令发现 go.modgo.sum 中存在问题(例如缺失的 require 指令或缺失的 checksum)时,它会尝试自动修复问题。我们收到了很多反馈,认为这种行为令人意外,特别是对于像 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

与以前一样,如果存在 vendor 目录,go 命令可能会使用它(详情请参阅 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 getgo.mod 中添加或更改模块版本要求的含义混淆太多。为了避免意外修改 go.mod,人们开始建议使用更复杂的命令,例如

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

现在我们都可以改为使用 go install program@latest。有关详情,请参阅 go install

现在我们都可以改用 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 会打印关于现有使用的警告。

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

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

$ 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 指令文档

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

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

使用 GOVCS 控制版本控制工具

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

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

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

例如

GOVCS=public:git|hg,private:all

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

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

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

下一篇文章:上下文和结构体
详情请参阅 使用 GOVCS 控制版本控制工具
接下来是什么?