Golang 依赖管理最佳实践

40
在Golang中,我们可以将GitHub上的开源库指定为依赖项。例如:
import "github.com/RichardKnop/somelibrary"

如果我理解正确的话,这将基于您的 Go 版本尝试查找一个分支,如果找不到,则默认为主分支。

因此,没有办法导入特定版本的依赖关系,例如:

import "github.com/RichardKnop/somelibrary#v1.4.8"

那么在Go中管理依赖关系的最佳实践是什么?

我可以看到两种方法。

I. 版本模块

是创建具有重大更改的主要版本的新模块吗?

例如,我的Go库可以定义模块v1和v2,然后您可以执行以下操作:

import "github.com/RichardKnop/somelibrary/v1"
或者:
import "github.com/RichardKnop/somelibrary/v2"

根据您的需求,对v1或v2所做的任何更改都需要确保不会破坏任何API或工作功能。

II. 分叉

这将使您完全控制您的Go代码所需的外部依赖版本。

例如,您可以将github.com/RichardKnop/somelibrary分叉到您自己的GitHub帐户中,然后在您的代码中执行:

import "github.com/ForkingUser/somelibrary"

那么你将不得不分叉所有外部依赖项,这似乎有点过度。但是这将使您完全控制版本。您可以将您的分支保持在一个您知道与您的代码配合工作的版本,并且只有在检查到依赖项的新版本不会破坏任何内容后才更新分支。

你怎么想?


你的v1/v2方法最终可能会成为大家追随的方法。请参见我的新回答,了解vgo。 - VonC
4个回答

16

2018年2月:如果vgo被集成到工具链中,下面介绍的vendor方法可能会消失。请参见我的下面的回答


2015年8月版:Go 1.5附带了内置的(但仍处于实验阶段的)vendoring支持。设置环境变量GO15VENDOREXPERIMENT将使go build和相关命令在查找包时同时搜索./vendor目录以及GOPATH。有关详细信息,请参见VonC的答案设计文档


III. Vendoring

据我所知,这是确保构建可重现且可预测性最广泛使用的方法。 Go团队本身在其存储库中使用vendoring。 Go团队正在讨论统一的依赖项清单文件格式。 来自Go工具链开发人员邮件列表

在Google的内部源代码树中,我们将所有依赖项vendor(复制)到我们的源代码树中,并且最多只有一个给定外部库的副本。 我们具有仅有一个GOPATH的等效物,并重新编写我们的导入以引用我们vendored的副本。 例如,想要使用“golang.org/x/crypto/openpgp”的Google内部Go代码可能会将其导入为类似于“google/third_party/golang.org/x/crypto/openpgp”的东西。

(...)

我们的建议是Go项目,

  • 官方建议使用导入重写(而不是修改GOPATH)将依赖项固定到“internal”目录中,作为固定依赖项的规范方式。

  • 定义了用于依赖项和供应商的通用配置文件格式。

  • 在Go 1.5中没有对cmd/go进行任何代码更改。外部工具,如“godep”或“nut”,将实现1)和2)。我们可以重新评估在Go 1.6+中是否包含此类工具。


谢谢。这似乎是一个不错的方法 :) 感谢提供邮件列表的链接。了解1.5和1.6的计划非常有用。 - Richard Knop

12
注意:在2015年6月,Go 1.5首次支持供应商!
请参见c/10923/
当环境中存在“GO15VENDOREXPERIMENT=1”时,此CL根据Go 1.5供应商提案更改了导入路径的解析方式: - 如果有源目录“d/vendor”,则在编译以“d”为根的子树中的源文件时,“import“p””将被解释为“import“d/vendor/p””,如果存在该路径。 - 当存在多个可能的分辨率时,最具体(最长)的路径获胜。 - 必须始终使用短格式:没有导入路径可以明确包含“/vendor/”。 - 在供应商包中忽略导入注释。

2016年1月更新:Go 1.6将使供应商(vendoring)成为默认选项。

并且如“大多数Go工具现在都适用于Go15vendorexperiment”所述:

1.6版本将支持大多数工具(如oracle)中的/vendor/;使用Beta版进行重建。


11

2018年8月更新:下面介绍的vgo已经使用Go 1.11和模块实现。

2018年2月更新,三年后。

有一个新方法是由Golang核心开发团队Russ Cox开发的。

vgo项目。

go get -u golang.org/x/vgo

这份提案:
  • 保留了go get最好的部分,
  • 增加了可重复构建功能,
  • 采用了语义化版本控制,
  • 消除了vendoring机制
  • 弃用了GOPATH,采用项目为基础的工作流程,并且
  • 为从dep及其前身平滑迁移提供了路径。

vgo semver

本技术基于一种新的MVS ("Minimal Version Selection")算法:

https://research.swtch.com/version-select-2.png

您可以看到:


2018年5月:Artifactory 提出了vgo代理

Artifactory 5.11的最新版本支持vgo兼容的Go注册表(和go代理),为Go开发人员提供了多种能力。
下面是其中的一些:

  • Artifactory中的本地仓库可让您设置安全的、私有的Go注册表,并根据项目或开发团队对软件包进行细粒度的访问控制。
  • Artifactory中的远程仓库是远程Go资源的缓存代理,例如GitHub项目。通过Artifactory访问go代理可消除对网络或GitHub的依赖,因为所有Go构建所需的依赖项都已缓存在Artifactory中,因此可以在本地使用。这也消除了某人在版本控制中突变或删除依赖项的风险,或更糟糕的是,强制将更改推送到远程Git标记,从而更改应该是不可变版本的内容,这可能会为依赖项目创建许多混乱和不稳定性。
  • 虚拟仓库汇集了本地和远程的Go注册表,使您可以从一个URL访问所有需要用于构建的Go资源,隐藏了使用本地和远程资源组合的复杂性。

非常期待能够使用基于项目的工作流来开发Go(我想你是指项目可以在gopath之外独立存在而不会出现问题)。特别是关于这方面是否有更多的信息? - David Alsh
@DavidAlsh 除了https://research.swtch.com/vgo-cmd中的“Go Builds and the Isolation Rule”部分外,没有太多内容。与此同时,Artifactory提出了一个与vgo兼容的代理:https://jfrog.com/blog/goproxy-artifactory-go-registries/。 - VonC

0

如果使用mvn-golang-wrapper,在Maven中可以仅描述依赖项,此时它看起来像下面的文本

<plugin>
 <groupId>com.igormaznitsa</groupId>
 <artifactId>mvn-golang-wrapper</artifactId>
 <version>1.1.0</version>
 <executions>
  <execution>
    <id>golang-get</id>
     <goals>
       <goal>get</goal>
     </goals>
     <configuration>
       <packages>
         <package>github.com/gizak/termui</package>
       </packages>
       <buildFlags>
         <flag>-u</flag>
       </buildFlags>
       <goVersion>1.6</goVersion>
     </configuration>
   </execution>
</plugin>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接