英文:
Golang Dependency Management Best Practice
问题
在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. Forking
这将使您完全控制您的Go代码所需的外部依赖项的版本。
例如,您可以将github.com/RichardKnop/somelibrary fork到您自己的GitHub帐户中,然后在您的代码中这样做:
import "github.com/ForkingUser/somelibrary"
然后,您将不得不fork所有外部依赖项,这似乎有点过度。但是,这将使您完全控制版本。您可以将您的fork保持在您知道与您的代码一起工作的版本,并且只有在检查到依赖项的新版本不会破坏任何内容后才更新forks。
您有什么想法?
英文:
In Golang, we can specify open source libraries on GitHub as dependencies. For example:
import "github.com/RichardKnop/somelibrary"
This will try to look for a branch based on your Go version and default to master if I understand correctly.
So there is no way to import a specific release of a dependency, e.g.:
import "github.com/RichardKnop/somelibrary#v1.4.8"
What is the best practise to manage dependencies in Go then?
I can see two approaches.
I. Version Modules
Is it to create new modules for major versions with breaking changes?
For example, my Go library could define modules v1 and v2 so then you could do:
import "github.com/RichardKnop/somelibrary/v1"
Or:
import "github.com/RichardKnop/somelibrary/v2"
Based on what you need. Any changes made to v1 or v2 would be required not to break any APIs or working functionality.
II. Forking
This would give you a complete control over a version of external dependency your Go code requires.
For example, you could fork github.com/RichardKnop/somelibrary into your own GitHub account and then in your code do:
import "github.com/ForkingUser/somelibrary"
Then you would have to fork all external dependencies which seems a bit overkill. However it would give you total control over versions. You could keep your forks at a version you know is working with your code and only update forks once you have checked that new releases of dependencies do not break anything.
Thoughts?
答案1
得分: 16
2018年2月:如果vgo被集成到工具链中,下面介绍的供应商方法(在2015/2016年)可能会消失。请参见下面的答案。
**2015年8月版:**Go 1.5版本带有内置的(但仍处于实验阶段的)供应商支持。设置环境变量GO15VENDOREXPERIMENT
将使go build
和相关命令在./vendor
目录和GOPATH
中查找包。有关更多详细信息,请参见VonC的答案和设计文档。
III. 供应商
据我所知,这是确保构建可重现和可预测的最常用方法。Go团队本身在他们的存储库中使用供应商。Go团队目前正在讨论统一的依赖关系清单文件格式。来自Go工具链开发者邮件列表:
>在Google的内部源代码树中,我们将所有依赖项供应商化(复制)到我们的源代码树中,并且每个外部库只有一个副本。我们只有一个等效的GOPATH,并且重写我们的导入路径以引用我们供应商化的副本。例如,Google内部的Go代码想要使用“golang.org/x/crypto/openpgp”,而是将其导入为类似“google/third_party/golang.org/x/crypto/openpgp”的东西。
>
>(...)
>
>我们的建议是,Go项目
>
>* 正式推荐将依赖项供应商化到“internal”目录,并进行导入重写(而不是GOPATH修改),作为固定依赖项的规范方式。
>
>* 定义依赖关系和供应商的通用配置文件格式
>
>* 在Go 1.5中不对cmd/go进行任何代码更改。外部工具如“godep”或“nut”将实现1)和2)。我们可以重新评估是否在Go 1.6+中包含此类工具。
英文:
Feb. 2018: the vendoring approach presented below (in 2015/2016) might end up disappearing if vgo is integrated to the toolchain.
See my answer below.
August 2015 edition: Go 1.5 comes with a built-in (but still experimental) vendoring support. Setting the environment variable GO15VENDOREXPERIMENT
will make go build
and friends look for packages in ./vendor
directory as well as GOPATH
. See VonC's answer and the design document for more details.
III. Vendoring
AFAIK, this is the most widely used way of ensuring that your builds are reproducible and predictable. The Go team itself uses vendoring in their repo. The Go team is now discussing the unified dependency manifest file format. From the Go toolchain developers mailing list:
>In Google’s internal source tree, we vendor (copy) all our dependencies into our source tree and have at most one copy of any given external library. We have the equivalent of only one GOPATH and rewrite our imports to refer to our vendored copy. For example, Go code inside Google wanting to use “golang.org/x/crypto/openpgp” would instead import it as something like “google/third_party/golang.org/x/crypto/openpgp”.
>
>(...)
>
>Our proposal is that the Go project,
>
>* officially recommends vendoring into an “internal” directory with import rewriting (not GOPATH modifications) as the canonical way to pin dependencies.
>
>* defines a common config file format for dependencies & vendoring
>
>* makes no code changes to cmd/go in Go 1.5. External tools such as “godep” or “nut” will implement 1) and 2). We can reevaluate including such a tool in Go 1.6+.
答案2
得分: 12
注意:2015年6月,Go 1.5首次支持供应商(vendoring)!请参见c/10923/:
> 当环境中存在GO15VENDOREXPERIMENT=1
时,此CL根据Go 1.5供应商提案更改了导入路径的解析方式:
> - 如果存在源目录d/vendor
,那么在编译以d
为根的子树中的源文件时,import "p"
将被解释为import "d/vendor/p"
(如果存在)。
- 当存在多个可能的解析时,选择最具体(最长)的路径。
- 必须始终使用短形式:导入路径不能显式包含“
/vendor/
”。 - 在供应商包中忽略导入注释。
更新于2016年1月:Go 1.6将使供应商成为默认选项。
正如文章“MOST GO TOOLS NOW WORK WITH GO15VENDOREXPERIMENT”中详细说明的那样:
> 1.6为大多数工具(如oracle)带来了对/vendor/
的支持;使用Beta版重新构建它们。
-
issue 12278已解决。
英文:
Note: June 2015, the first support for vendoring appears in Go 1.5!
See c/10923/:
> When GO15VENDOREXPERIMENT=1
is in the environment, this CL changes the resolution of import paths according to the Go 1.5 vendor proposal:
> - If there is a source directory d/vendor
, then, when compiling a source file within the subtree rooted at d
, import "p"
is interpreted as import "d/vendor/p"
if that exists.
- When there are multiple possible resolutions, the most specific (longest) path wins.
- The short form must always be used: no import path can contain “
/vendor/
” explicitly. - Import comments are ignored in vendored packages.
Update January 2016: Go 1.6 will make vendoring the default.
And as detailed in the article "MOST GO TOOLS NOW WORK WITH GO15VENDOREXPERIMENT":
> 1.6 brings support for /vendor/
to most tools (like the oracle) out of the box; use the Beta to rebuild them.
-
issue 12278 has been resolved.
-
<strike>there still is an issue with
goimports
, and there is a CL that can be cherry-picked</strike>.
答案3
得分: 11
2018年8月更新:现在已经使用Go 1.11和模块实现了以下内容(下面介绍的vgo)。
2018年2月更新,三年后。
核心Golang开发团队的Russ Cox开发了一种新的方法。
vgo项目。
go get -u golang.org/x/vgo
这个提案:
- 保留了
go get
的优点,- 增加了可复现构建,
- 采用了语义化版本控制,
- 消除了vendoring,
- 弃用了
GOPATH
,采用基于项目的工作流程,- 提供了从
dep
及其前身平滑迁移的路径。
它基于一个新的"MVS("Minimal Version Selection")算法:
你可以看到:
- **Go & Versioning**中的第一篇文章
- 这个讨论
- **Jon Calhoun**对该讨论的分析
- Sam Boyer在"Thoughts on vgo and dep"中的反驳:
他领导了**go dep
**的私人倡议 - Russ、Sam和**Jesse Frazelle之间的第一次辩论(YouTube)**!
2018年5月:Artifactory提出了一个vgo代理
最近发布的Artifactory 5.11增加了对与vgo兼容的Go注册表(和go代理)的支持,为开发Go提供了各种能力。
以下是其中的一些:
- Artifactory中的本地仓库允许您设置安全的、私有的Go注册表,并根据项目或开发团队对软件包进行细粒度的访问控制。
- Artifactory中的远程仓库是对远程Go资源(如GitHub项目)的缓存代理。通过Artifactory访问go代理可以消除对网络或GitHub的依赖,因为所有Go构建所需的依赖都被缓存在Artifactory中,因此在本地可用。这也消除了某人对版本控制中的依赖进行更改或删除,或更糟糕的是,对远程Git标签进行强制推送从而更改了应该是不可变版本的风险,这可能给依赖项目带来很多混乱和不稳定性。
- Artifactory中的虚拟仓库聚合了本地和远程Go注册表,使您可以从单个URL访问所有构建所需的Go资源,隐藏了使用本地和远程资源的复杂性。
英文:
Update August 2018: this (vgo presented below) is now implemented with Go 1.11 and modules.
Update Feb. 2018, 3 years later.
There is a new approach developed by Russ Cox, from the core Golang development team.
The vgo project.
go get -u golang.org/x/vgo
> This proposal:
>
> - keeps the best parts of go get
,
>- adds reproducible builds,
>- adopts semantic versioning,
>- eliminates vendoring,
>- deprecates GOPATH
in favor of a project-based workflow, and
>- provides a smooth migration path from dep
and its predecessors.
It is based on a new MVS ("Minimal Version Selection") algorithm:
You can see:
- the first articles in Go & Versioning
- the discussion in this thread
- the analysis of that thread by Jon Calhoun
- the counter-point by Sam Boyer in "Thoughts on vgo and dep":
He was leading the private initiative ofgo dep
- the first debate (YouTube) between Russ, Sam and Jesse Frazelle!
May 2018: Artifactory proposes a vgo proxy
> The recent release of Artifactory 5.11 added support for vgo-compatible Go registries (and go proxy) giving the community a variety of capabilities when developing with Go.
Here are just a few of them:
> - Local repositories in Artifactory let you set up secure, private Go registries with fine-grained access control to packages according to projects or development teams.
>- A remote repository in Artifactory is a caching proxy for remote Go resources such as a GitHub project. Accessing a go proxy through Artifactory removes your dependency on the network, or on GitHub since all dependencies needed for your Go builds are cached in Artifactory and are therefore locally available. This also removes the risk of someone mutating or removing a dependency from version control, or worse, force-pushing changes to a remote Git tag thus changing what should be an immutable version which can create a lot of confusion and instability for dependent projects.
>- A virtual repository aggregates both local and remote Go registries giving you access to all Go resources needed for your builds from a single URL, hiding the complexity of using a combination of both local and remote resources.
答案4
得分: 0
也可以在Maven中使用mvn-golang-wrapper来描述依赖关系,具体如下所示:
<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>
</executions>
</plugin>
英文:
also it is possible just describe dependencies in maven, if to use mvn-golang-wrapper, in the case it looks like in maven as below text
<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>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论