【译】模块(Modules)第六部分:Vendor使用

系列索引

为什么以及什么
项目,依赖项和Gopls
最小版本选择
镜像,校验和和Athens
Gopls改进
Vendor使用

介绍

我是vendor的粉丝已经不是秘密,如果你的应用程序项目中合理地使用它。该项目构建应用程序所需的每一行源代码,我相信vendor都会为你的应用程序项目提供最大的持久性保证。如果你想要可重复的构建而无需依赖外部服务(例如模块镜像)或连接到网络,vendor可以提供解决方案。

vendor还有其他好处:

  • 如果从VCS中删除了依赖,或者代理服务器丢失了模块,vendor可以解决。
  • 升级依赖时你可以看见差异对比,并且你可以维护历史记录。
  • 你将能够跟踪和调试依赖,并在必要时进行测试更改。
  • 运行go mod tidygo mod vendor之后,你的更改将被替换。

在本文中,我将提供Go支持vendor的历史以及随着时间的推移默认行为的变化。我还将分享Go的工具如何保持版本之间的向后兼容性。最后,我将分享(随着时间的推移)可能需要手动升级go.mod文件中列出的版本,以更改将来的Go版本的默认行为。

运行不同版本的Go

为了向你展示Go 1.13和Go 1.14之间默认行为的差异,我需要能够同时在我的计算机上运行两个版本的go tooling。发布本文时,我已经在计算机上安装了Go 1.14.2。但是,对于本文,我还需要运行Go 1.13环境。那么如何在不破坏当前开发环境的情况下做到这一点呢?

幸运的是,Go团队发布了版本下载,为你要使用的任何Go版本包括Go Tip提供了特定的二进制文件。

图1

image

图1显示了来自Go 1.13.10 下载页面的屏幕截图。它显示了构建二进制文件的说明,该二进制文件可用于使用Go 1.13.10构建和测试你的Go代码。

代码1

$ cd $HOME
$ go get golang.org/dl/go1.13.10

OUTPUT
go: downloading golang.org/dl v0.0.0-20200408221700-d6f4cf58dce2
go: found golang.org/dl/go1.13.10 in golang.org/dl v0.0.0-20200408221700-d6f4cf58dce2


$ go1.13.10 download

OUTPUT
Downloaded   0.0% (    14448 / 121613848 bytes) ...
Downloaded   9.5% ( 11499632 / 121613848 bytes) ...
Downloaded  30.8% ( 37436528 / 121613848 bytes) ...
Downloaded  49.2% ( 59849840 / 121613848 bytes) ...
Downloaded  69.3% ( 84262000 / 121613848 bytes) ...
Downloaded  90.3% (109804656 / 121613848 bytes) ...
Downloaded 100.0% (121613848 / 121613848 bytes)
Unpacking /Users/bill/sdk/go1.13.10/go1.13.10.darwin-amd64.tar.gz ...
Success. You may now run 'go1.13.10'


$ go1.13.10 version

OUTPUT
go version go1.13.10 darwin/amd64


$ go version

OUTPUT
go version go1.14.2 darwin/amd64

代码1显示了在Go 1.1.3.10版本运行go get命令并下载完成后的效果,现在我可以在我的机器上使用Go 1.13.10,而扰乱Go 1.14.2的安装。

如果要从计算机中删除Go的任何版本,可以在HOME/sdk中找到。

代码2

$ cd $GOPATH/bin
$ l

OUTPUT
-rwxr-xr-x   1 bill  staff   7.0M Apr 11 10:51 go1.13.10
-rwxr-xr-x   1 bill  staff   2.3M Jan  6 11:02 gotip


$ cd $HOME
$ l sdk/

OUTPUT
drwxr-xr-x  22 bill  staff   704B Apr 11 10:52 go1.13.10
drwxr-xr-x  24 bill  staff   768B Feb 26 01:59 gotip

Vendoring 快速教程

Go工具出色地将管理应用程序项目的依赖项的工作流程最小化。它需要两个命令:tidy和vendor。

代码3

$ go mod tidy

代码3显示的tidy,帮助保持模块文件中列出的依赖关系准确。一些编辑器(例如VS Code和GoLand)提供了在开发过程中更新模块文件的支持,但这并不意味着一旦你完成所有工作,模块文件会被准确地清理干净。建议tidy你在提交并将任何代码推向VCS之前运行此命令。

如果您还想提供这些依赖项,请在之后运行vendor命令tidy。

代码4

$ go mod vendor

代码4显示了vendor命令。此命令会在项目内部创建一个vendor文件夹,其中包含项目构建和测试代码所需的所有依赖项(直接和间接)的源代码。该命令最好在运行tidy后运行,以使vendor文件夹与模块文件保持同步。确保提交并将vendor文件夹推送到你的VCS。

GOPATH或模块模式

在Go 1.11中,被称为“模块模式”的新模式添加到了Go工具中。当Go工具在模块模式下运行时,会使用模块系统查找和构建代码。当Go工具在GOPATH模式下运行时,传统的GOPATH系统将继续用于查找和构建代码。我使用Go工具的最大难题之一就是不知道不同版本之间默认使用哪种模式,以及需要知道进行哪些配置更改和使用哪些标记,以使构建保持一致。

要了解过去4个版本的Go的历史和语义变化,最好对这些模式进行一下复习。

Go1.11

引入了一个新的环境变量GO111MODULE,其默认值为auto。此变量将根据代码所在的位置(GOPATH的内部或外部)确定Go工具将使用模块模式还是GOPATH模式。要强制使用一种模式,你可以将此变量设置为on或off。对于vendor文件夹,默认情况下,模块模式将忽略vendor文件夹,并针对模块缓存建立依赖关系。

Go1.12

GO111MODULE保留默认设置,并且Go工具继续根据代码所在的位置(GOPATH的内部或外部)确定模块模式或GOPATH模式。对于vendor文件夹,默认情况下,模块模式仍将忽略vendor文件夹,并针对模块缓存建立依赖关系。

Go1.13

GO111MODULE默认设置仍保留,但Go工具不再对工作目录是否在GOPATH中敏感。默认情况下,模块模式仍将忽略供vendor文件夹,并针对模块缓存建立依赖关系。

Go1.14

GO111MODULE保留的默认设置auto,Go工具仍然不再对工作目录是否在GOPATH中敏感。但是,如果存在vendor文件夹,则默认情况下将使用它来构建依赖关系,而不是模块缓存[1]。另外,go命令会确认项目的vendor/modules.txt文件与其go.mod文件保持一致。

版本之间的向后兼容性

在Go 1.14中,默认情况下在模块缓存上使用vendor文件夹更改。最初,我认为我可以只使用Go 1.14来针对现有项目进行构建,这样就足够了,但是我错了。在我使用Go 1.14进行首次构建之后,没有注意vendor文件夹,我了解到Go工具会读取go.mod文件以获取版本信息,并保持与所列版本的向后兼容性。我不知道,但是在Go 1.14的发行说明中清楚地表达了这一点。

https://golang.org/doc/go1.14#go-command

当主模块包含顶级vendor目录并且其go.mod文件指定Go 1.14或更高版本时,go命令现在默认-mod=vendor

为了使用新的默认行为进行vendor管理,我需要将go.mod文件中的版本信息从Go 1.13 升级到Go 1.14。

示范

为了向你展示Go 1.13和Go 1.14的行为,以及Go工具如何保持向后兼容性,我将使用service项目。我将向你展示更改go.mod中列出的版本,Go工具的默认行为如何表现。

首先,我将在GOPATH之外克隆service项目。

代码5

$ cd $HOME/code
$ git clone https://github.com/ardanlabs/service
$ cd service
$ code .

代码5显示了用于克隆项目并使用VS Code打开项目的命令。

代码6

$ ls -l vendor/

OUTPUT
total 8
drwxr-xr-x   3 bill  staff    96 Mar 26 16:01 contrib.go.opencensus.io
drwxr-xr-x  14 bill  staff   448 Mar 26 16:01 github.com
drwxr-xr-x  20 bill  staff   640 Mar 26 16:01 go.opencensus.io
drwxr-xr-x   3 bill  staff    96 Mar 26 16:01 golang.org
drwxr-xr-x   3 bill  staff    96 Mar 26 16:01 gopkg.in
-rw-r--r--   1 bill  staff  2860 Mar 26 16:01 modules.txt

代码6显示了项目的vendor文件夹清单。可以看到一些受欢迎的VCS站点的目录以及多个虚构域名。项目依赖于构建和测试的所有代码都位于vendor文件夹中。

接下来,我将手动将go.mod文件更改回版本1.13。这将向你展示我第一次在该项目中使用Go 1.14时遇到的行为。

代码7

module github.com/ardanlabs/service

go 1.13   // I just changed this from go 1.14 to go 1.13

代码7显示了我对该go.mod文件所做的更改(切换go 1.14go 1.13)。

注意:有一个go mod命令可用于更改go.mod文件中的版本:go mod edit -go=1.14

GO 1.13
在第一个构建中,我将使用Go 1.13.10来构建sales-api应用程序。请记住,go.mod文件将Go 1.13列为该项目的兼容版本。

代码8

$ cd service/cmd/sales-api
$ go1.13.10 clean -modcache
$ go1.13.10 build

OUTPUT
go: downloading contrib.go.opencensus.io/exporter/zipkin v0.1.1
. . .
go: finding github.com/leodido/go-urn v1.2.0

代码8显示了如何进入应用程序的文件夹,清除本地模块缓存,然后使用Go 1.13.10执行构建。注意Go工具如何将所有依赖项下载回我的模块缓存中以构建二进制文件。vendor文件夹被忽略。

为了使Go 1.13认可vendor文件夹,我需要在构建和测试时使用-mod=vendor标志。

代码9

$ go1.13.10 clean -modcache
$ go1.13.10 build -mod=vendor

OUTPUT

代码9显示了我现在如何在构建调用中使用-mod=vendor标志。这次,模块缓存未使用缺少的模块重新填充,并且认可了vendor文件夹中的代码。

Go1.14

这次,我将使用Go 1.14.2运行build命令,而不使用-mod=vendor标志。

代码10

$ go clean -modcache
$ go build

OUTPUT
go: downloading github.com/openzipkin/zipkin-go v0.2.2
. . .
go: downloading github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e

代码10显示了使用Go 1.14构建项目时发生的情况。由于该工具使用Go 1.13语义进行操作,因此不认可vendor文件夹。这是因为go.mod文件将Go 1.13列为该项目的兼容版本。当我第一次看到这个时,我感到震惊。这就是我开始调查的原因。

如果我将go.mod文件切换到版本1.14,则Go 1.14工具的默认模式将默认切换为认可vendor文件夹。

清单11

module github.com/ardanlabs/service

go 1.14   // I just changed this from go 1.13 to go 1.14

清单11显示了对该go.mod文件的更改回到1.14。我将再次清除模块缓存,并使用Go 1.14再次运行build命令。

代码12

$ go clean -modcache
$ go build

OUTPUT

代码12显示,这次go build使用Go 1.14 的调用中没有重新填充模块缓存。这意味着不需要-mod=vendor标记就可以认可vendor文件夹。由于模块文件列出了Go 1.14,因此默认行为已更改。

vendor和模块的未来变化

感谢John Reese,这里是一个链接的讨论,该讨论基于go.mod文件中列出的内容维护在不同版本的Go之间的向后兼容性。John在帮助验证了本文内容准确和流程正确。

https://github.com/golang/go/issues/30791

将来的发行版中将提供更多有关vendor的支持。讨论中的这样一种功能是关于验证vendor文件夹中的代码以查找代码已更改的情况。

https://github.com/golang/go/issues/27348

我必须感谢Chris Hines提醒我有关Go早期版本中的默认行为以及每个新发行版中的默认行为。Chris还提供了一些有趣的链接,这些链接共享了Go工具中用于模块的一些历史和其他有趣的东西。克里斯在确保职位准确和流程正确方面发挥了作用。
https://github.com/golang/go/issues/33848
https://github.com/golang/go/issues/36460

结论

这篇文章是令我惊讶的是,go.mod中列出的版本影响了Go工具的默认行为。为了获得我想要的Go 1.14中新的默认vendor行为,我必须手动将go.mod列出的版本从1.13 升级到1.14。

对于go.mod使用版本信息来保持版本之间的向后兼容性,我尚未想到任何具体的意见。Go工具从未与Go兼容性的承诺挂钩,因此这对我来说是意想不到的。也许这是伟大事物的开始,并且Go工具可以不断发展,而Go社区不必担心发布新版Go工具时其构建,测试和工作流程是否会中断。

如果你有任何意见,我很想在Twitter上听听的意见。

补充说明

[1]从Go 1.11开始,该-mod=vendor标志使go命令从vendor目录而不是从模块缓存中加载模块。(vendor目录包含单个软件包,而不是完整的模块。)在Go 1.14中,该-mod标志的默认值根据主模块的内容而变化:如果存在vendor目录且go.mod文件指定go 1.14或更高,则-mod默认为-mod=vendor。如果go.mod文件是只读的,则-mod默认为-mod=readonly。我们还添加了一个新值-mod=mod,意思是“从模块缓存中加载模块”(也就是说,如果没有其他条件满足,则默认情况下会获得相同的行为)。即使你正在使用默认行为的主模块-mod=vendor,你可以使用该-mod=mod标志显式返回模块缓存。-Bryan Mills

你可能感兴趣的:(【译】模块(Modules)第六部分:Vendor使用)