介绍
Golang 1.16 已经正式发布了,其中 Modules 有一些变化:
- 默认开启 Modules。
- 不自动修改
go.mod
和go.sum
。 - 通过指定
@version
后缀安装特定版本可执行文件。 - 新增
retract
指令撤回 Module 版本。 - 使用新增配置变量 GOVCS 指定特定模块使用特定版本控制工具。
本文来深入探讨一下 golang 1.16 关于 Modules 的一些变化。
默认开启 Modules
golang 1.16 默认开启 Modules,即使不存在 go.mod
,Go 命令现在默认情况下也会在 module-aware
(模块感知)模式下构建包。
在 golang 1.16 中,通过设置关闭 GO111MODULE
环境变量,在 GOPATH
模式下构建包仍然是可能的。您还可以将 GO111MODULE
设置为 auto
,以便在当前目录或任何父目录中存在 go.mod
文件时启用 module-aware
(模块感知)模式。您还可以使用 go env -w
永久设置 GO111MODULE
和其他变量,:
go env -w GO111MODULE=auto
Go 官方计划在 Go 1.17
中放弃对 GOPATH
模式的支持。换句话说,Go 1.17
将忽略 GO111MODULE
。如果您的项目不在 module-aware
(模块感知)模式下构建,则现在是时候迁移至 module-aware
(模块感知)模式了。
不自动修改 go.mod
和 go.sum
在 golang 1.16 之前版本中,当 go 命令发现 go.mod
或 go.sum
存在问题时,如缺少 require
指令或缺少 sum
,它将尝试自动解决问题。Go 官方收到很多反馈,这种行为是令人惊讶的,特别是对于 go 命令,如 go list
,通常没有副作用。自动修复并不总是可取的:如果任何所需模块不提供导入的包,Go 命令将添加新的依赖项,可能触发常见依赖项的升级。即使输入路径拼写错误,也会导致(失败的)网络查找。
在 golang 1.16 中,module-aware
(模块感知)命令在 go.mod
或 go.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
golang 1.16 与 Go 之前版本一样,如果 vendor
目录存在,Go 命令可能会使用 vendor
目录。 go get
和go mod tidy
命令仍然修改 go.mod
和 go.sum
,因为他们的主要目的是管理依赖关系。
04
通过指定 @version
后缀安装特定版本可执行文件
go install
命令现在可以通过指定 @version
后缀安装特定版本的可执行文件,例如:
go install golang.org/x/tools/gopls@v0.6.5
如果使用 @version
后缀,go install
命令使用该确切 Module 版本,忽略当前目录和父目录中的任何 go.mod
文件中的 Module 版本。
如果没有 @version
后缀,go install
继续运行,因为它一直有,建立程序使用当前模块的 go.mod
文件中 requirements 列表和 replacements 列表列出的版本。
我们曾经建议 go get -u program
安装一个可执行文件, 但这种使用给 go get
安装或更改在 go.mod
文件中 requirements 的 module 版本时造成了太多的混乱。
为了避免意外修改 go.mod
,Go 用户开始建议更复杂的命令,如:
cd $HOME; GO111MODULE=on go get program@latest
现在,我们都可以使用 go install program@latest
代替。
为了消除使用哪个版本的模糊性,在使用此安装语法 go install program@latest
时,Go 程序的 go.mod 文件中可能存在几个限制的指令。特别是,至少目前不允许 replace
和 exclude
指令。从长远来看,一旦新的 go install program@version
在大多数使用情况下工作的很好的前提下,Go 官方计划在未来某个版本中让 go get
命令停止安装二进制文件。
05
新增 retract
指令撤回 Module 版本
您是否在模块版本准备好之前意外地发布了该版本?或者,您是否在发布需要快速修复的版本后发现了问题?已发布版本中的错误很难更正。为了保持模块生成的确定性,版本发布后无法修改。即使您删除或更改了版本标签,proxy.golang.org
和其他代理可能已经有原始缓存。
模块作者现在可以使用 go.mod
中的 retract
指令 retract 模块版本。retract 的版本仍然存在,可以下载(因此依赖于它的构建不会中断),但 go 命令在解决 @latest
等版本时不会自动选择它。go get
和 go 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
接下来,作者可以 tag 和 push 版本 v1.0.6,新的最高版本。在此之后,已依赖 v1.0.5 的用户在检查更新或升级依赖包时将收到撤回通知。通知消息可能包括收回指令上方注释的文本。例如:
$ 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
06
使用新增配置变量 GOVCS 指定特定模块使用特定版本控制工具
go 命令可以从镜像 proxy.golang.org
或直接从版本控制存储库下载模块源代码,使用 git、hg、svn、bzr 或 fossil。直接版本控制访问很重要,尤其是对于代理上不可用的私有模块,但它也可能是一个安全问题:版本控制工具中的错误可能被恶意服务器利用来运行恶意代码。
Go 1.16 引入了一个新的配置变量 GOVCS,它允许用户指定哪些模块允许使用特定的版本控制工具。GOVCS 接受一个逗号分隔的模式列表:vcslist 规则。
模式是一条 path.Match
。匹配模式匹配模块路径的一个或多个主要元素。公共和私有的特殊模式匹配公共和私有模块(私有定义为与 GOPRIVATE 中的模式匹配的模块;公共是其他一切模块)。vcslist 是允许版本控制命令或关键字 all 或 off 的管道分隔列表。例如:
GOVCS=github.com:git,evil.com:off,*:git|hg
使用此设置,可以使用 git 下载带有 github.com
路径的模块;无法使用任何版本控制命令下载 evil.com
上的路径,使用 git 或 hg 下载所有其他路径(*
匹配所有内容)的模块。
如果未设置环境变量 GOVCS,或者如果模块与任何模式不匹配,Go 命令将使用 GOVCS 的默认值:允许 git 和 hg 用于公共模块,并且允许所有工具用于私有模块。
设置只允许使用 Git 和 Mercurial 的理由是,这两个版本控制工具最关注作为不受信任服务器的客户端运行的问题。相比之下,Bazaar、Fossil 和 Subversion 主要用于受信任的、经过验证的环境中,而且没有像 attack surfaces 那样受到很好的审查。即默认设置为:
GOVCS=public:git|hg,private:all
07
Module 未来发展
我们希望您发现这些功能很有用。我们已经开始开发 Go 1.17 的模块功能,特别是懒惰的模块加载,这应该使模块加载过程更快,更稳定。
08
总结
本文主要介绍了 Golang 1.16 针对 Module 做的一些变化。通过 Go 官方的这些 Module 变化,切实解决了 Go 用户在使用 Go 时的实际问题。Go 官方也表示会在 Golang 1.17 计划彻底去除 GOPATH
模式,所以,如果您的项目目前还没有迁移到 Module 模式,是时候开始迁移了。