系列索引
为什么以及什么
项目,依赖项和Gopls
最小版本选择
镜像,校验和和Athens
Gopls改进
Vendor使用
介绍
我是vendor的粉丝已经不是秘密,如果你的应用程序项目中合理地使用它。该项目构建应用程序所需的每一行源代码,我相信vendor都会为你的应用程序项目提供最大的持久性保证。如果你想要可重复的构建而无需依赖外部服务(例如模块镜像)或连接到网络,vendor可以提供解决方案。
vendor还有其他好处:
- 如果从VCS中删除了依赖,或者代理服务器丢失了模块,vendor可以解决。
- 升级依赖时你可以看见差异对比,并且你可以维护历史记录。
- 你将能够跟踪和调试依赖,并在必要时进行测试更改。
- 运行
go mod tidy
,go 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
图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.14
为go 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