管理依赖项
当您的代码使用外部软件包时,这些软件包(以模块形式分发)将成为依赖项。随着时间的推移,您可能需要升级或替换它们。Go 提供了依赖项管理工具,可帮助您在合并外部依赖项时保持 Go 应用程序的安全性。
本主题介绍如何执行任务来管理您在代码中采用的依赖项。您可以使用 Go 工具执行其中大部分任务。本主题还介绍了如何执行其他一些您可能觉得有用的依赖项相关任务。
另请参阅
- 如果您不熟悉将依赖项作为模块进行处理,请参阅 入门教程 以获取简要介绍。
- 使用
go
命令管理依赖项有助于确保您的要求保持一致,并且 go.mod 文件的内容有效。有关命令的参考,请参阅 命令 go。您还可以通过键入go help
command-name(如go help mod tidy
)从命令行获取帮助。 - 您用于进行依赖项更改的 Go 命令会编辑您的 go.mod 文件。有关该文件内容的更多信息,请参阅 go.mod 文件参考。
- 让您的编辑器或 IDE 了解 Go 模块可以使管理它们的工作变得更容易。有关支持 Go 的编辑器的更多信息,请参阅 编辑器插件和 IDE。
- 本主题不介绍如何开发、发布和版本化供他人使用的模块。有关更多信息,请参阅开发和发布模块。
使用和管理依赖项的工作流
你可以使用 Go 工具获取和使用有用的包。在 pkg.go.dev 上,你可以搜索可能觉得有用的包,然后使用 go
命令将这些包导入到自己的代码中以调用其函数。
以下列出了最常见的依赖项管理步骤。有关每个步骤的更多信息,请参阅本主题中的部分。
- 在 pkg.go.dev 上查找有用的包。
- 在代码中导入所需的包。
- 将代码添加到模块中以进行依赖项跟踪(如果尚未在模块中)。请参阅启用依赖项跟踪
- 添加外部包作为依赖项,以便对其进行管理。
- 根据需要随时升级或降级依赖项版本。
将依赖项作为模块进行管理
在 Go 中,你可以将依赖项作为包含已导入包的模块进行管理。此过程受以下内容支持
- 用于发布模块和检索其代码的分散式系统。开发人员让其他开发人员可以使用其模块,从自己的存储库中使用其模块,并使用版本号发布其模块。
- 你可以从中查找模块的包搜索引擎和文档浏览器 (pkg.go.dev)。请参阅查找和导入有用的包。
- 模块版本编号约定,可帮助你了解模块的稳定性和向后兼容性保证。请参阅模块版本编号。
- Go 工具,可让你更轻松地管理依赖项,包括获取模块的源代码、升级等。有关更多信息,请参阅本主题的部分。
查找和导入有用的包
你可以搜索 pkg.go.dev 以查找具有可能觉得有用的函数的包。
当你找到要在代码中使用的包时,找到页面顶部的包路径,然后单击“复制路径”按钮将路径复制到剪贴板。在自己的代码中,将路径粘贴到 import 语句中,如下例所示
import "rsc.io/quote"
在代码导入包后,启用依赖项跟踪并获取包的代码以进行编译。有关更多信息,请参阅在代码中启用依赖项跟踪和添加依赖项。
在代码中启用依赖项跟踪
要跟踪和管理您添加的依赖项,您首先要将您的代码放在其自己的模块中。这会在您的源代码树的根目录中创建一个 go.mod 文件。您添加的依赖项将列在该文件中。
要将您的代码添加到其自己的模块中,请使用 go mod init
命令。例如,从命令行切换到您的代码的根目录,然后运行命令,如下例所示
$ go mod init example/mymodule
go mod init
命令的参数是您的模块的模块路径。如果可能,模块路径应该是您的源代码的存储库位置。
如果您最初不知道模块最终的存储库位置,请使用一个安全的替代方案。这可能是您拥有的域的名称或您控制的另一个名称(例如您的公司名称),以及从模块名称或源目录派生的路径。更多信息,请参阅 命名模块。
当您使用 Go 工具管理依赖项时,这些工具会更新 go.mod 文件,以便它维护您的依赖项的当前列表。
当您添加依赖项时,Go 工具还会创建一个 go.sum 文件,其中包含您依赖的模块的校验和。Go 使用它来验证下载的模块文件的完整性,特别是对于在您的项目上工作的其他开发人员。
将 go.mod 和 go.sum 文件与您的代码一起包含在您的存储库中。
有关更多信息,请参阅 go.mod 参考。
命名模块
当您运行 go mod init
为跟踪依赖项创建一个模块时,您指定一个模块路径,该路径作为模块的名称。模块路径成为模块中包的导入路径前缀。务必指定一个模块路径,该路径不会与其他模块的模块路径冲突。
至少,模块路径只需要指示其来源,例如公司或作者或所有者名称。但该路径也可能更详细地描述模块是什么或做什么。
模块路径通常采用以下形式
<prefix>/<descriptive-text>
-
前缀 通常是一个部分描述模块的字符串,例如描述其来源的字符串。这可能是
-
Go 工具可以在其中找到模块源代码的存储库的位置(如果您要发布模块,则需要)。
例如,它可能是
github.com/<project-name>/
。如果您认为可能会发布模块供他人使用,请使用此最佳实践。有关发布的更多信息,请参阅开发和发布模块。
-
您控制的名称。
如果您不使用存储库名称,请务必选择一个您确信不会被其他人使用的前缀。一个不错的选择是贵公司的名称。避免使用常见术语,例如
widgets
、utilities
或app
。
-
-
对于描述性文本,一个不错的选择是项目名称。请记住,包名称承担了描述功能的大部分权重。模块路径为这些包名称创建了一个命名空间。
保留的模块路径前缀
Go 保证以下字符串不会用于包名称。
-
test
– 您可以将test
用作模块路径前缀,用于其代码旨在本地测试另一个模块中的函数的模块。对作为测试的一部分创建的模块使用
test
路径前缀。例如,您的测试本身可能会运行go mod init test
,然后以某种特定方式设置该模块,以便使用 Go 源代码分析工具进行测试。 -
example
– 在一些 Go 文档中用作模块路径前缀,例如在您创建模块只是为了跟踪依赖项的教程中。请注意,Go 文档还使用
example.com
来说明示例可能是已发布模块的情况。
添加依赖项
一旦您从已发布模块中导入包,您就可以使用go get
命令将该模块添加为依赖项进行管理。
该命令执行以下操作
-
如果需要,它会为命令行中命名的包所需的模块向您的 go.mod 文件添加
require
指令。require
指令跟踪您的模块所依赖模块的最低版本。有关更多信息,请参阅go.mod 参考。 -
如果需要,它会下载模块源代码,以便您可以编译依赖它们的包。它可以从模块代理(如 proxy.golang.org)或直接从版本控制存储库下载模块。源代码在本地缓存。
您可以设置 Go 工具下载模块的位置。有关更多信息,请参阅指定模块代理服务器。
以下描述了一些示例。
-
要添加模块中包的所有依赖项,请运行类似于以下命令的命令(“.”表示当前目录中的包)
$ go get .
-
要添加特定依赖项,请将它的模块路径指定为该命令的参数。
$ go get example.com/theirmodule
该命令还会对下载的每个模块进行身份验证。这可确保模块自发布以来未发生更改。如果模块自发布以来发生了更改(例如,开发者更改了提交内容),Go 工具将显示安全错误。此身份验证检查可保护您免受可能被篡改的模块的侵害。
获取特定依赖项版本
您可以通过在 go get
命令中指定其版本来获取依赖项模块的特定版本。该命令会更新 go.mod 文件中的 require
指令(但您也可以手动更新)。
您可能希望执行此操作,原因如下
- 您希望获取模块的特定预发布版本以进行尝试。
- 您已发现您当前需要的版本对您不起作用,因此您希望获取您知道可以依赖的版本。
- 您希望升级或降级您已需要的模块。
以下是使用 go get
命令 的示例
-
要获取特定编号版本,请在模块路径后附加 @ 符号,后跟所需版本
$ go get example.com/[email protected]
-
要获取最新版本,请在模块路径后附加
@latest
$ go get example.com/theirmodule@latest
以下 go.mod 文件 require
指令示例(有关更多信息,请参见 go.mod 参考)说明了如何需要特定版本号
require example.com/theirmodule v1.3.4
发现可用更新
您可以检查您当前模块中已使用的依赖项是否有较新版本。使用 go list
命令显示模块依赖项的列表,以及该模块可用的最新版本。发现可用升级后,您可以使用代码对其进行尝试,以决定是否升级到新版本。
有关 go list
命令的更多信息,请参见 go list -m
。
以下是一些示例。
-
列出当前模块的所有依赖项模块,以及每个模块的最新可用版本
$ go list -m -u all
-
显示特定模块的最新可用版本
$ go list -m -u example.com/theirmodule
升级或降级依赖项
您可以使用 Go 工具发现可用版本,然后添加不同版本作为依赖项来升级或降级依赖项模块。
同步代码的依赖项
你可以确保管理所有代码导入包的依赖项,同时还可以移除不再导入的包的依赖项。
当对代码和依赖项进行更改时,这可能很有用,可能会创建一组受管依赖项和下载的模块,这些模块不再与代码中导入的包特别需要的集合匹配。
要保持受管依赖项集井然有序,请使用 go mod tidy
命令。此命令使用代码中导入的包集,编辑 go.mod 文件以添加必要但缺失的模块。它还会移除不提供任何相关包的未使用模块。
该命令没有参数,只有一个标志 -v,用于打印有关已移除模块的信息。
$ go mod tidy
针对未发布的模块代码进行开发和测试
你可以指定代码应使用可能未发布的依赖项模块。这些模块的代码可能在其各自的存储库中,在这些存储库的分支中,或在使用它们的当前模块的驱动器上。
你可能希望在以下情况下执行此操作
- 你希望对外部模块的代码进行自己的更改,例如在对其进行分支和/或克隆之后。例如,你可能希望为模块准备一个修复程序,然后将其作为拉取请求发送给模块的开发者。
- 你正在构建一个新模块,但尚未发布它,因此在
go get
命令可以访问的存储库中不可用。
在本地目录中需要模块代码
你可以指定所需模块的代码与需要它的代码位于同一本地驱动器上。当你在以下情况下,你可能会发现这很有用
- 开发你自己的独立模块,并希望从当前模块进行测试。
- 修复外部模块中的问题或向其中添加功能,并希望从当前模块进行测试。(请注意,你还可以从你自己的存储库分支中需要外部模块。有关更多信息,请参阅 从你自己的存储库分支中需要外部模块代码。)
要告诉 Go 命令使用模块代码的本地副本,请在 go.mod 文件中使用 replace
指令来替换 require
指令中给出的模块路径。有关指令的更多信息,请参阅 go.mod 参考。
在以下 go.mod 文件示例中,当前模块需要外部模块 example.com/theirmodule
,并使用不存在的版本号 (v0.0.0-unpublished
) 来确保替换正常工作。然后,replace
指令将原始模块路径替换为 ../theirmodule
,该目录与当前模块目录处于同一级别。
module example.com/mymodule
go 1.16
require example.com/theirmodule v0.0.0-unpublished
replace example.com/theirmodule v0.0.0-unpublished => ../theirmodule
在设置 require
/replace
对时,请使用 go mod edit
和 go get
命令来确保文件中描述的要求保持一致
$ go mod edit -replace=example.com/[email protected]=../theirmodule
$ go get example.com/[email protected]
注意:当您使用 replace 指令时,Go 工具不会对外部模块进行身份验证,如 添加依赖项 中所述。
有关版本号的更多信息,请参阅 模块版本编号。
从您自己的仓库分支中要求外部模块代码
当您已分叉外部模块的仓库(例如,修复模块代码中的问题或添加功能)时,您可以让 Go 工具使用您的分支作为模块的源。这对于测试您自己代码中的更改非常有用。(请注意,您还可以在本地驱动器上要求模块代码,该驱动器与要求它的模块位于同一目录中。有关更多信息,请参阅 在本地目录中要求模块代码。)
您可以通过在 go.mod 文件中使用 replace
指令来实现此目的,该指令将外部模块的原始模块路径替换为仓库中分支的路径。这会指示 Go 工具在编译时使用替换路径(分支的位置),例如,同时允许您将 import
语句保留为原始模块路径。
有关 replace
指令的更多信息,请参阅 go.mod 文件参考。
在以下 go.mod 文件示例中,当前模块需要外部模块 example.com/theirmodule
。然后,replace
指令将原始模块路径替换为 example.com/myfork/theirmodule
,这是模块自己仓库的一个分支。
module example.com/mymodule
go 1.16
require example.com/theirmodule v1.2.3
replace example.com/theirmodule v1.2.3 => example.com/myfork/theirmodule v1.2.3-fixed
设置 require
/replace
对时,使用 Go 工具命令来确保文件描述的需求保持一致。使用 go list
命令获取当前模块使用的版本。然后使用 go mod edit
命令将所需模块替换为 fork
$ go list -m example.com/theirmodule
example.com/theirmodule v1.2.3
$ go mod edit -replace=example.com/[email protected]=example.com/myfork/[email protected]
注意:当您使用 replace
指令时,Go 工具不会对外部模块进行身份验证,如 添加依赖项 中所述。
有关版本号的更多信息,请参阅 模块版本编号。
使用存储库标识符获取特定提交
您可以使用 go get
命令从其存储库中的特定提交中添加模块的未发布代码。
为此,您可以使用 go get
命令,并使用 @
符号指定所需的代码。当您使用 go get
时,该命令会将 require
指令添加到您的 go.mod 文件中,该指令需要外部模块,并使用基于提交详细信息的伪版本号。
以下示例提供了一些说明。这些示例基于其源代码位于 git 存储库中的模块。
-
若要获取特定提交中的模块,请追加 @commithash 表单
$ go get example.com/theirmodule@4cf76c2
-
若要获取特定分支中的模块,请追加 @branchname 表单
$ go get example.com/theirmodule@bugfixes
删除依赖项
当您的代码不再使用模块中的任何包时,您可以停止跟踪该模块作为依赖项。
若要停止跟踪所有未使用的模块,请运行 go mod tidy
命令。此命令还可能会添加构建模块中包所需的缺少依赖项。
$ go mod tidy
若要删除特定依赖项,请使用 go get
命令,指定模块的模块路径并追加 @none
,如下例所示
$ go get example.com/theirmodule@none
go get
命令还会降级或删除依赖于已删除模块的其他依赖项。
指定模块代理服务器
当您使用 Go 工具处理模块时,这些工具默认从 proxy.golang.org(一个公共的 Google 运行的模块镜像)或直接从模块的存储库下载模块。您可以指定 Go 工具应改为使用另一个代理服务器来下载和验证模块。
如果您(或您的团队)已设置或选择了您想要使用的其他模块代理服务器,则您可能希望这样做。例如,一些人设置模块代理服务器是为了更好地控制如何使用依赖项。
若要为 Go 工具使用指定另一个模块代理服务器,请将 GOPROXY
环境变量设置为一个或多个服务器的 URL。Go 工具将按您指定的顺序尝试每个 URL。默认情况下,GOPROXY
首先指定一个公共的 Google 运行的模块代理,然后直接从模块的存储库下载(如其模块路径中指定的)。
GOPROXY="https://proxy.golang.org,direct"
有关 GOPROXY
环境变量的更多信息,包括支持其他行为的值,请参阅 go
命令参考。
您可以将变量设置为其他模块代理服务器的 URL,并使用逗号或管道分隔 URL。
-
当您使用逗号时,只有当当前 URL 返回 HTTP 404 或 410 时,Go 工具才会尝试列表中的下一个 URL。
GOPROXY="https://proxy.example.com,https://proxy2.example.com"
-
使用管道时,无论 HTTP 错误代码如何,Go 工具都将尝试列表中的下一个 URL。
GOPROXY="https://proxy.example.com|https://proxy2.example.com"
Go 模块经常在版本控制服务器和模块代理上开发和分发,而这些服务器和代理在公共互联网上不可用。你可以设置 GOPRIVATE
环境变量,以配置 go
命令从私有源下载和构建模块。然后,go 命令可以从私有源下载和构建模块。
GOPRIVATE
或 GONOPROXY
环境变量可以设置为匹配私有模块前缀的 glob 模式列表,不应从任何代理请求这些前缀。例如
GOPRIVATE=*.corp.example.com,*.research.example.com