Go 1.5 中的包版本管理

huangapple go评论83阅读模式
英文:

Package version management in Go 1.5

问题

我正在用Go语言进行实践,虽然我理解并欣赏Go所建立的“简洁性”原则,但我想了解放弃在其依赖获取工具go getimport语句中内置包版本管理方法的原因。

如果我理解正确,go getimportHEAD获取包,无法引用分支或标签。虽然有像gopkg.in这样的工具可以绕过这个限制,但官方工具链有以下限制:

  1. 强制开发人员为其产品的主要(破坏性)版本创建单独的仓库。
  2. 不允许消费者在发现新版本中存在错误时降级到较低的次要或微小版本。

说实话,事情并不那么简单,因为包版本管理需要处理冲突的传递依赖,例如X依赖于AB,而它们又依赖于不同版本的C

作为一个有Java背景的人,我认为这个限制带来了一些风险和问题,其中包括:

  1. 产品/包的演进和公共API的破坏是不可避免的,因此版本管理在工具链中必须是一等公民。

  2. _每个版本一个Git仓库_的策略效率非常低下:

    • 包的整体Git历史丢失或分散在多个仓库中(版本之间的合并、回溯等)。
    • 仍然可能出现与传递依赖的冲突,并且由于语言和工具链没有强加任何语义来允许首先进行检测,所以这些冲突将不会被发现。
  3. 企业采用可能受到阻碍,开发团队可能会回避该语言,因为:

    • 每次都使用HEAD意味着他们无法控制或冻结其第三方依赖,可能导致最终产品的不可预测性。
    • 可能缺乏人力资源来始终与上游的HEAD保持产品的更新和测试(并非全世界的公司都是Google :))。

虽然我理解后者的风险可以通过持续集成来减轻,但这并不能解决问题的根本原因。

我缺少哪些信息?在企业中如何处理Go的包更新问题,尤其是在人力有限的情况下?

英文:

I'm getting my hands dirty with Go, and while I understand and appreciate the principle of simplicity that Go was built upon, I'd like to grasp the rationale behind forgoing a built-in package versioning method in their dependency-fetching tool go get and the import statement.

If I understand correctly, go get and import fetch the package from HEAD and they are unable to refer to a branch or a tag. While there are tools like gopkg.in that circumvent this limitation, the official toolchain:

  1. Forces developers to create separate repos for major (breaking) versions of their products.
  2. It doesn't allow consumers to downgrade between minor or micro versions in case bugs are found in newer ones.

Truth be told, things are not so easy because package versioning would require a strategy to deal with conflicting transitive dependencies, e.g. X depends on A and B, each of which depend on different versions of C.

Coming from a Java background, it does appear that this limitation poses some risks and problems, amongst others:

  1. Product/package evolution and breakage of public APIs of 3rd party deps is unavoidable, therefore versioning must be a first-class citizen in the toolchain IMHO.

  2. The Git-repo-per-version policy is highly inefficient:

  • The overall Git history of the package is lost or scattered across repos (merges between versions, backports, etc.)
  • Conflicts with transitive dependencies may still occur, and will go about undetected because the language nor the toolchain impose any semantics to allow detection in the first place.
  1. Enterprise adoption may be hindered and development teams may shy away from the language, given that:
  • Always dragging in HEAD means that they can't control or freeze their 3rd party deps, leading to a potentially unpredictable end product.
  • May lack the manpower to keep their product constantly updated and tested with upstream's HEAD (not every company in the world is Google :)).

While I do understand that the latter risk can be – and must be – mitigated with Continuous Integration, it does not solve the underlying root of the problem.

What information am I missing? How do you deal with package upstream changes when deploying Go in an enterprise with limited manpower?

答案1

得分: 7

这是要翻译的内容:

vendoring 是 Go 1.5 的一个实验性功能,它可以通过在运行 go 命令时在环境中设置 GO15VENDOREXPERIMENT=1 来启用,并且在 Go 1.6 中将成为一个“完整”的功能。另请参阅 Vendor Directories

关于 Go 1.5 Vendor 实验的原始讨论可以在这里找到。

vendoring 的核心思想是创建一个名为 vendor 的文件夹,并将代码所依赖的包的确切版本放入其中。vendor 文件夹中的代码只能被位于 vendor 的父目录的目录树中的代码导入,你可以使用类似于将 vendor 视为 workspace/src 文件夹的导入路径来从 vendor 导入包(也就是说,导入路径省略了前缀,直到包括 vendor 元素为止)。

示例:

/home/user/goworkspace/
    src/
        mymath/
            mymath.go
            vendor/
                github.com/somebob/math
                    math.go

在这个示例中,github.com/somebob/mathmymath 包(来自 mymath.go)使用的外部包。如果要从 mymath.go 中使用它,可以这样导入:

import "github.com/somebob/math"

(而不是 import mymath/vendor/github.com/somebob/math,这样是错误的。)

英文:

It is being addressed by vendoring which is part of Go 1.5 as an experimental feature, it can be enabled if the go command is run with GO15VENDOREXPERIMENT=1 in its environment, and will be a "full" feature in Go 1.6. Also see Vendor Directories.

The original discussion that led to the Go 1.5 Vedor Experiment can be found here.

The essence of vendoring is that you create a folder named vendor, and you put the exact version of the packages that your code relies on. Code inside the vendor folder is only importable by the code in the directory tree rooted at the parent of vendor, and you can import packages from vendor with an import path as if vendor would be the workspace/src folder (that is, with an import path that omits the prefix up to and including the vendor element).

Example:

/home/user/goworkspace/
    src/
        mymath/
            mymath.go
            vendor/
                github.com/somebob/math
                    math.go

In this example github.com/somebob/math is an external package used by mymath package (from mymath.go). It can be used from mymath.go if it is imported like:

import "github.com/somebob/math"

(And not as import mymath/vendor/github.com/somebob/math which would be bad.)

答案2

得分: 1

虽然Go语言没有标准的包管理器,但有足够的选项可以使构建过程可复现(即使在人力有限的企业中)。

  1. Vendoring(供应商管理),由@icza在另一个答案中描述。这几乎相当于在Java中检入有版本控制的jar文件。在maven流行之前,这是ant构建工具的常见做法。实际上,供应商管理更好,因为你不会丢失源代码。

  2. 这是第一种选项的轻微变体。不需要检入供应商源代码,而是在构建过程中通过检出预定义版本的依赖项来填充供应商文件夹。有一些工具(如glide)可以自动化这个过程。

  3. 最后,你可以在内部仓库中维护所有第三方库的预定义版本,并将其添加到GOPATH中。这种方法在https://blog.gopheracademy.com/advent-2015/go-in-a-monorepo/中有详细描述。

请注意,不兼容的传递依赖关系不仅限于Go语言。Java(和大多数其他语言)也存在这个问题,尽管Java通过使用类加载器的机制在一定程度上解决了这个问题,但这使得程序更加复杂。请注意,Go语言会在编译时报告所有不兼容性,而在Java中,一些不兼容性只有在运行时才会触发(因为链接较晚)。

Java工具链没有版本的概念,这是由外部工具maven提供的。我相信随着Go语言变得更加成熟和流行,会出现类似的标准依赖管理工具。

英文:

While Go doesn't come with the standard package manager there are enough options to make builds reproducible (even in an enterprise with limited manpower).

  1. Vendoring, which is described in another answer by @icza. This is almost complete equivalent of checking in versioned jar files in Java. This was very common approach with ant build tool before maven became popular. Actually vendoring is much better because you cannot lose source code.

  2. This is slight variation of the first option. Instead of checking in vendored source code you can populate vendor folder during build by checking out predefined versions of the dependencies. There tools (e.g. glide) that automate this process.

  3. Finally you can maintain predefined versions of all 3-rd party libraries in the internal repository and add it to GOPATH. This approach is described in detail in https://blog.gopheracademy.com/advent-2015/go-in-a-monorepo/

Note that incompatible transitive dependencies are not specific to Go. They exist in Java (and most other languages) too, though Java has a mechanism to partially solve this problem by making programs more complex - class loaders. Note that Go will report all incompatibilities at compile time while in Java some of the incompatibilities are triggered only at run time (because of late linking).

Java toolchain doesn't have concept of versions. It is provided by the external tool - maven. I believe by the time Go becomes more mature and popular a similar standard dependency management tool will emerge.

huangapple
  • 本文由 发表于 2016年1月14日 03:47:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/34775722.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定