管理依赖项

当您的代码使用外部软件包时,这些软件包(以模块形式分发)将成为依赖项。随着时间的推移,您可能需要升级或替换它们。Go 提供了依赖项管理工具,可帮助您在合并外部依赖项时保持 Go 应用程序的安全性。

本主题介绍如何执行任务来管理您在代码中采用的依赖项。您可以使用 Go 工具执行其中大部分任务。本主题还介绍了如何执行其他一些您可能觉得有用的依赖项相关任务。

另请参阅

使用和管理依赖项的工作流

你可以使用 Go 工具获取和使用有用的包。在 pkg.go.dev 上,你可以搜索可能觉得有用的包,然后使用 go 命令将这些包导入到自己的代码中以调用其函数。

以下列出了最常见的依赖项管理步骤。有关每个步骤的更多信息,请参阅本主题中的部分。

  1. pkg.go.dev 上查找有用的包。
  2. 在代码中导入所需的包。
  3. 将代码添加到模块中以进行依赖项跟踪(如果尚未在模块中)。请参阅启用依赖项跟踪
  4. 添加外部包作为依赖项,以便对其进行管理。
  5. 根据需要随时升级或降级依赖项版本

将依赖项作为模块进行管理

在 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 保证以下字符串不会用于包名称。

添加依赖项

一旦您从已发布模块中导入包,您就可以使用go get命令将该模块添加为依赖项进行管理。

该命令执行以下操作

以下描述了一些示例。

该命令还会对下载的每个模块进行身份验证。这可确保模块自发布以来未发生更改。如果模块自发布以来发生了更改(例如,开发者更改了提交内容),Go 工具将显示安全错误。此身份验证检查可保护您免受可能被篡改的模块的侵害。

获取特定依赖项版本

您可以通过在 go get 命令中指定其版本来获取依赖项模块的特定版本。该命令会更新 go.mod 文件中的 require 指令(但您也可以手动更新)。

您可能希望执行此操作,原因如下

以下是使用 go get 命令 的示例

以下 go.mod 文件 require 指令示例(有关更多信息,请参见 go.mod 参考)说明了如何需要特定版本号

require example.com/theirmodule v1.3.4

发现可用更新

您可以检查您当前模块中已使用的依赖项是否有较新版本。使用 go list 命令显示模块依赖项的列表,以及该模块可用的最新版本。发现可用升级后,您可以使用代码对其进行尝试,以决定是否升级到新版本。

有关 go list 命令的更多信息,请参见 go list -m

以下是一些示例。

升级或降级依赖项

您可以使用 Go 工具发现可用版本,然后添加不同版本作为依赖项来升级或降级依赖项模块。

  1. 要发现新版本,请使用 go list 命令,如 发现可用更新 中所述。

  2. 要将特定版本添加为依赖项,请使用 go get 命令,如 获取特定依赖项版本 中所述。

同步代码的依赖项

你可以确保管理所有代码导入包的依赖项,同时还可以移除不再导入的包的依赖项。

当对代码和依赖项进行更改时,这可能很有用,可能会创建一组受管依赖项和下载的模块,这些模块不再与代码中导入的包特别需要的集合匹配。

要保持受管依赖项集井然有序,请使用 go mod tidy 命令。此命令使用代码中导入的包集,编辑 go.mod 文件以添加必要但缺失的模块。它还会移除不提供任何相关包的未使用模块。

该命令没有参数,只有一个标志 -v,用于打印有关已移除模块的信息。

$ go mod tidy

针对未发布的模块代码进行开发和测试

你可以指定代码应使用可能未发布的依赖项模块。这些模块的代码可能在其各自的存储库中,在这些存储库的分支中,或在使用它们的当前模块的驱动器上。

你可能希望在以下情况下执行此操作

在本地目录中需要模块代码

你可以指定所需模块的代码与需要它的代码位于同一本地驱动器上。当你在以下情况下,你可能会发现这很有用

要告诉 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 editgo 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 存储库中的模块。

删除依赖项

当您的代码不再使用模块中的任何包时,您可以停止跟踪该模块作为依赖项。

若要停止跟踪所有未使用的模块,请运行 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。

Go 模块经常在版本控制服务器和模块代理上开发和分发,而这些服务器和代理在公共互联网上不可用。你可以设置 GOPRIVATE 环境变量,以配置 go 命令从私有源下载和构建模块。然后,go 命令可以从私有源下载和构建模块。

GOPRIVATEGONOPROXY 环境变量可以设置为匹配私有模块前缀的 glob 模式列表,不应从任何代理请求这些前缀。例如

GOPRIVATE=*.corp.example.com,*.research.example.com