大家好。预期Golang Developer课程的开始,我们为您准备了另一种有趣的翻译。
模块是处理Go中依赖项的一种方式。这些模块最初是作为实验提供的,应该作为一种新的标准引入现场,用于管理版本1.13以来的软件包。我发现这个主题对于来自其他语言的初学者来说已经足够不寻常了,所以我决定在这里收集一些想法和技巧,以帮助像我这样的人了解Go中的软件包管理想法。我们将与一般的介绍开始,然后转移到不太明显的方面,包括使用供应商的文件夹,在发展中,工具的依赖等使用模块与码头工人如果你已经熟悉围棋模块和知道维基,就像手背一样,这篇文章可能对您没有多大帮助。但是,对于其余部分,它可以节省数小时的反复试验。因此,如果您在旅途中,请加入并享受旅程。
快速开始
如果项目中已经集成了版本控制,则只需运行go mod init
或手动指定模块的路径。这类似于您的软件包的名称,URL和导入路径:go mod init github.com/you/hello
这将创建一个文件go.mod
,该文件还根据项目的正确版本定义项目要求和位置(作为类推,就像package.json
,并package-lock.json
组合成一个文件):module github.com/you/hello
go 1.12
运行go get
以向项目添加新的依赖项:请注意,尽管无法使用go get指定版本范围,但此处定义的不是特定版本,而是最低版本。稍后我们将看到,有一种方法可以根据semver优雅地更新依赖关系。# use Git tags
go get github.com/go-chi/chi@v4.0.1
# or Git branch name
go get github.com/go-chi/chi@master
# or Git commit hash
go get github.com/go-chi/chi@08c92af
现在我们的文件go.mod
如下:module github.com/you/hello
go 1.12
require github.com/go-chi/chi v4.0.2+incompatible
后缀将+incompatible
添加到尚未为Go模块配置或违反其版本控制规则的所有软件包。由于我们没有在项目中的任何地方导入此软件包,因此将其标记为// indirect
。我们可以使用以下命令整理一下:go mod tidy
根据存储库的当前状态,它将删除未使用的模块或删除注释// indirect
。如果本身没有依赖关系go.mod
(例如,尚未为模块配置),则所有依赖关系都将被写入父文件go.mod
(作为选择,您的文件go.mod)
以及注释// indirect
以指示它们不是直接导入的)在您的模块中,全球范围内的目的go mod tidy
还在于添加其他操作系统组合,体系结构和程序集标签所需的任何依赖项。请确保在每个发行版之前运行它;另外,请注意,在添加依赖项之后,会创建文件go.sum
。您可能会认为这是一个锁定文件。但是实际上,它go.mod
已经为100%可复制的构建提供了足够的信息。go.sum
创建该文件用于验证目的:它包含模块各个版本的内容的预期加密校验和。部分原因是它go.sum
不是锁定文件,因此即使您停止使用该模块,它也会保存模块版本的书面校验和。这样,您便可以在以后恢复使用校验和的情况下检查校验和,从而提高了安全性。
Mkcert刚刚迁移到模块(与供应商一起使用/为了向后兼容),一切都顺利进行
https://github.com/FiloSottile/mkcert/commit/26ac5f35395fb9cba3805faf1a5a04d260271291
$ GO111MODULE=on go1.11rc1 mod init
$ GO111MODULE=on go1.11rc1 mod vendor
$ git add go.mod go.sum vendor
$ git rm Gopkg.lock Gopkg.toml Makefile

常见问题:我应该go.sum
在git中提交吗?
答:绝对可以。使用它,您的源所有者不需要信任其他GitHub存储库和自定义导入路径的所有者。通往我们的道路已经更好了,但是目前,它与锁定文件中的哈希值具有相同的模型。
go build
和
命令go test
将自动加载所有丢失的依赖项,尽管您可以在go mod download
预填充对CI有用的本地缓存的帮助下显式地执行此操作。默认情况下,所有项目中的所有软件包都被加载到directory中$GOPATH/pkg/mod
。我们将在后面详细讨论。升级软件包版本
您可以使用go get -u
两种go get -u=patch
分别以更新依赖于最新的次要版本或补丁。但是您不能升级到这样的主要版本。Go模块中包含的代码在技术上必须遵守以下规则:- 匹配semver(示例标记VCS v1.2.3)。
- 如果模块的版本为v2或更高版本,则
/vN
在文件中使用的模块路径的末尾go.mod
和程序包导入路径中均应包括模块的主版本:
import "github.com/you/hello/v2"
显然,这样做是为了使不同版本的软件包可以在一个程序集中导入(请参阅菱形依赖问题)。简而言之,Go希望您在介绍主要版本时要非常小心。更换导入的模块
您可以使用指令为您自己的fork甚至文件的本地路径指定必要的模块replace
:go mod edit -replace github.com/go-chi/chi=./packages/chi
结果:module github.com/you/hello
go 1.12
require github.com/go-chi/chi v4.0.2+incompatible
replace github.com/go-chi/chi => ./packages/chi
您可以手动删除该行或运行:go mod edit -dropreplace github.com/go-chi/chi
项目依赖管理
历史上,所有Go代码都存储在一个巨大的单一存储库中,因为Google就是这样组织其代码库的,这会影响语言设计。Go模块与这种方法背道而驰。您不再需要将所有项目都保留在中$GOPATH
。但是,从技术上讲,您下载的所有依赖项仍然放置在中$GOPATH/pkg/mod
。如果使用Docker容器进行本地开发,则可能会出现问题,因为依赖项存储在项目外部。默认情况下,它们在您的IDE中根本不可见。
对于其他语言,这通常不是问题,但这是我在使用Go代码库时首先遇到的问题。幸运的是,有几种(未记录)的方法可以解决此问题。选项1.在项目目录中安装GOPATH。
乍看起来,这似乎违反直觉,但是如果从容器运行Go,则可以覆盖GOPATH,使其指向项目目录,以便可以从主机访问软件包:version: '3.7'
services:
app:
command: tail -f /dev/null
image: golang:1.12.6-stretch
environment:
# - /code/.go/pkg/mod
- GOPATH=/code/.go
ports:
- 8000:8000
volumes:
- ./:/code:cached
working_dir: /code
流行的IDE应该能够在项目(工作区)级别上安装GOPATH:
这种方法的唯一缺点是与主机上的Go运行时缺乏交互。您必须在容器内执行所有Go命令。选项2:出售您的依存关系
另一种方法是将项目的依赖项复制到文件夹vendor
:go mod vendor
应当立即注意:我们不允许 Go直接将材料上传到供应商文件夹:这对于模块而言是不可能的。我们只复制已经下载的软件包。另外,如上例所示,如果取消依赖关系,然后清除$GOPATH/pkg/mod
然后尝试向项目中添加几个新的依赖关系,您将看到以下内容:- Go将为所有软件包重建下载缓存
$GOPATH/pkg/mod/cache
。 - 所有加载的模块将复制到
$GOPATH/pkg/mod
。 - 最后,Go会将这些模块复制到
vendor
文件夹中,删除示例,测试以及您不直接依赖的其他文件。
此外,此新创建的vendor文件夹中缺少很多东西:
一个典型的Docker Compose文件如下所示(请注意卷绑定):version: '3.7'
services:
app:
command: tail -f /dev/null
image: golang:1.12.6-stretch
ports:
- 8000:8000
volumes:
# go,
- modules:/go/pkg/mod/cache
- ./:/code:cached
working_dir: /code
volumes:
modules:
driver: local
请注意,我不会在版本控制系统中将此供应商文件夹添加为漫画,也不会在生产中使用它。这是严格的本地开发脚本,通常可以使用其他一些语言找到。但是,当我阅读一些Go维护人员的评论以及与部分自动售货(CHE?)相关的一些报价时,我会觉得该功能最初不是针对该用户情况的。关于reddit的评论员之一帮助我阐明了这一点:通常人们出于某种原因出售他们的依赖关系,例如,希望拥有紧密的组件而无法访问网络,以及在github故障或存储库消失的情况下提供现成的依赖关系副本的可用性,以及使用标准VCS工具更容易审核依赖关系变化的可能性等。 。是的,它看起来并不像一个事实,即我可能感兴趣的东西。根据Go命令,您可以通过设置环境变量轻松启用自动售货GOFLAGS=-mod=vendor
。我不建议这样做。使用标记只会中断,go get
而不会给您的日常工作流程带来任何其他好处:
实际上,您唯一需要启用自动售货的地方就是您的IDE:
经过一番尝试和错误之后,我想出了以下步骤,以这种方式添加供应商依赖性。步骤1.要求
您可以要求依赖项go get
:go get github.com/rs/zerolog@v1.14.3
步骤2.汇入
然后将其导入代码中的某个位置:import (
_ "github.com/rs/zerolog"
)
步骤3.贩卖
最后,重新打开您的依赖项:go mod vendor
有一个悬而未决的建议,允许go mod供应商接受某些模块模板,这些模板可以(或可以不)解决与此工作流程相关的一些问题。go mod vendor
已经自动要求错过导入,因此步骤1在此工作流程中是可选的(如果您不想指定版本限制)。但是,如果没有步骤2,它将无法提取下载的软件包。这种方法在主机系统上效果更好,但是在编辑依赖项时却相当混乱。
我个人认为重新定义GOPATH是一种更干净的方法,因为它不会牺牲功能go get
。尽管如此,我还是想展示这两种策略,因为其他语言(例如PHP,Ruby,Javascript等)的人可能会对vendor文件夹更加熟悉。从本文中描述的欺诈行为可以看出,对于Go来说并不是一个特别好的选择。工具依赖
我们可能需要安装一些基于Go的工具,这些工具不是导入的,而是用作项目开发环境的一部分。此类工具的一个简单示例是CompileDaemon,它可以监视代码中的更改并重新启动应用程序。官方推荐的方法是添加tools.go
具有以下内容的文件(名称无关紧要):
package tools
import (
_ "github.com/githubnemo/CompileDaemon"
)
- 该限制会
// +build tools
阻止您的常规程序集实际导入工具。 - 导入表达式使go命令可以将工具的版本信息准确地写入
go.mod
模块文件。
这就是全部。希望您不会像我刚开始使用Go模块时那样困惑。您可以访问Go Modules Wiki了解更多详细信息。
上课程。