使用Go模块管理软件包:实用指南

大家好。预期Golang Developer课程的开始,我们为您准备了另一种有趣的翻译。



模块是处理Go中依赖项的一种方式。这些模块最初是作为实验提供的,应该作为一种新的标准引入现场,用于管理版本1.13以来的软件包。

我发现这个主题对于来自其他语言的初学者来说已经足够不寻常了,所以我决定在这里收集一些想法和技巧,以帮助像我这样的人了解Go中的软件包管理想法。我们将与一般的介绍开始,然后转移到不太明显的方面,包括使用供应商的文件夹,在发展中,工具的依赖等使用模块与码头工人

如果你已经熟悉围棋模块和知道维基,就像手背一样,这篇文章可能对您没有多大帮助。但是,对于其余部分,它可以节省数小时的反复试验。

因此,如果您在旅途中,请加入并享受旅程。



快速开始


如果项目中已经集成了版本控制,则只需运行

go mod init

或手动指定模块的路径。这类似于您的软件包的名称,URL和导入路径:

go mod init github.com/you/hello

这将创建一个文件go.mod,该文件还根据项目的正确版本定义项目要求和位置(作为类推,就像package.json,并package-lock.json组合成一个文件):

module github.com/you/hello
go 1.12

运行go get以向项目添加新的依赖项:

请注意,尽管无法使用go get指定版本范围,但此处定义的不是特定版本,而是最低版本。稍后我们将看到,有一种方法可以根据semver优雅地更新依赖关系。

# use Git tags
go get github.com/go-chi/chi@v4.0.1
# or Git branch name
go get github.com/go-chi/chi@master
# or Git commit hash
go get github.com/go-chi/chi@08c92af

现在我们的文件go.mod如下:

module github.com/you/hello
go 1.12
require github.com/go-chi/chi v4.0.2+incompatible // indirect

后缀将+incompatible添加到尚未为Go模块配置或违反其版本控制规则的所有软件包。

由于我们没有在项目中的任何地方导入此软件包,因此将其标记为// indirect我们可以使用以下命令整理一下:

go mod tidy

根据存储库的当前状态,它将删除未使用的模块或删除注释// indirect

如果本身没有依赖关系go.mod(例如,尚未为模块配置),则所有依赖关系都将被写入父文件go.mod(作为选择,您的文件go.mod)以及注释// indirect以指示它们不是直接导入的)在您的模块中,

全球范围内的目的go mod tidy还在于添加其他操作系统组合,体系结构和程序集标签所需的任何依赖项。请确保在每个发行版之前运行它;

另外,请注意,在添加依赖项之后,会创建文件go.sum您可能会认为这是一个锁定文件。但是实际上,它go.mod已经为100%可复制的构建提供了足够的信息。go.sum创建该文件用于验证目的:它包含模块各个版本的内容的预期加密校验和。

部分原因是它go.sum不是锁定文件,因此即使您停止使用该模块,它也会保存模块版本的书面校验和。这样,您便可以在以后恢复使用校验和的情况下检查校验和,从而提高了安全性。


Mkcert刚刚迁移到模块(与供应商一起使用/为了向后兼容),一切都顺利进行
https://github.com/FiloSottile/mkcert/commit/26ac5f35395fb9cba3805faf1a5a04d260271291

$ GO111MODULE=on go1.11rc1 mod init
$ GO111MODULE=on go1.11rc1 mod vendor
$ git add go.mod go.sum vendor
$ git rm Gopkg.lock Gopkg.toml Makefile



常见问题:我应该go.sum在git中提交吗?
答:绝对可以。使用它,您的源所有者不需要信任其他GitHub存储库和自定义导入路径的所有者。通往我们的道路已经更好了,但是目前,它与锁定文件中的哈希值具有相同的模型。

go build 命令go test将自动加载所有丢失的依赖项,尽管您可以在go mod download预填充对CI有用的本地缓存的帮助下显式地执行此操作

默认情况下,所有项目中的所有软件包都被加载到directory中$GOPATH/pkg/mod我们将在后面详细讨论。

升级软件包版本


您可以使用go get -u两种go get -u=patch分别以更新依赖于最新的次要版本或补丁。

但是您不能升级到这样的主要版本。Go模块中包含的代码在技术上必须遵守以下规则:

  • 匹配semver(示例标记VCS v1.2.3)。
  • 如果模块的版本为v2或更高版本,则/vN在文件中使用的模块路径的末尾go.mod和程序包导入路径中均应包括模块的主版本

import "github.com/you/hello/v2"

显然,这样做是为了使不同版本的软件包可以在一个程序集中导入(请参阅菱形依赖问题)。

简而言之,Go希望您在介绍主要版本时要非常小心。

更换导入的模块


您可以使用指令为您自己的fork甚至文件的本地路径指定必要的模块replace

go mod edit -replace github.com/go-chi/chi=./packages/chi

结果:

module github.com/you/hello
go 1.12
require github.com/go-chi/chi v4.0.2+incompatible
replace github.com/go-chi/chi => ./packages/chi

您可以手动删除该行或运行:

go mod edit -dropreplace github.com/go-chi/chi

项目依赖管理


历史上,所有Go代码都存储在一个巨大的单一存储库中,因为Google就是这样组织其代码库的,这会影响语言设计。

Go模块与这种方法背道而驰。您不再需要将所有项目都保留在中$GOPATH

但是,从技术上讲,您下载的所有依赖项仍然放置在中$GOPATH/pkg/mod。如果使用Docker容器进行本地开发,则可能会出现问题,因为依赖项存储在项目外部。默认情况下,它们在您的IDE中根本不可见。



对于其他语言,这通常不是问题,但这是我在使用Go代码库时首先遇到的问题。

幸运的是,有几种(未记录)的方法可以解决此问题。

选项1.在项目目录中安装GOPATH。


乍看起来,这似乎违反直觉,但是如果从容器运行Go,则可以覆盖GOPATH,使其指向项目目录,以便可以从主机访问软件包:

version: '3.7'

services:
  app:
    command: tail -f /dev/null
    image: golang:1.12.6-stretch
    environment:
      #        - /code/.go/pkg/mod
      - GOPATH=/code/.go
    ports:
      - 8000:8000
    volumes:
      - ./:/code:cached
    working_dir: /code

流行的IDE应该能够在项目(工作区)级别上安装GOPATH:



这种方法的唯一缺点是与主机上的Go运行时缺乏交互。您必须在容器内执行所有Go命令。

选项2:出售您的依存关系


另一种方法是将项目的依赖项复制到文件夹vendor

go mod vendor

应当立即注意:我们不允许 Go直接将材料上传到供应商文件夹:这对于模块而言是不可能的。我们只复制已经下载的软件包。

另外,如上例所示,如果取消依赖关系,然后清除$GOPATH/pkg/mod然后尝试向项目中添加几个新的依赖关系,您将看到以下内容:

  1. Go将为所有软件包重建下载缓存$GOPATH/pkg/mod/cache
  2. 所有加载的模块将复制到$GOPATH/pkg/mod
  3. 最后,Go会将这些模块复制到vendor文件夹中,删除示例,测试以及您不直接依赖的其他文件。

此外,此新创建的vendor文件夹中缺少很多东西:



一个典型的Docker Compose文件如下所示(请注意卷绑定):

version: '3.7'

services:
  app:
    command: tail -f /dev/null
    image: golang:1.12.6-stretch
    ports:
      - 8000:8000
    volumes:
     #    go,           
      - modules:/go/pkg/mod/cache
      - ./:/code:cached
    working_dir: /code 

volumes:
  modules:
    driver: local

请注意,我不会在版本控制系统中将此供应商文件夹添加为漫画,也不会在生产中使用它。这是严格的本地开发脚本,通常可以使用其他一些语言找到。

但是,当我阅读一些Go维护人员的评论以及与部分自动售货(CHE?)相关的一些报价时,我会觉得该功能最初不是针对该用户情况的。关于reddit

评论员之一帮助我阐明了这一点:

通常人们出于某种原因出售他们的依赖关系,例如,希望拥有紧密的组件而无法访问网络,以及在github故障或存储库消失的情况下提供现成的依赖关系副本的可用性,以及使用标准VCS工具更容易审核依赖关系变化的可能性等。 。

是的,它看起来并不像一个事实,即我可能感兴趣的东西。

根据Go命令,您可以通过设置环境变量轻松启用自动售货GOFLAGS=-mod=vendor。我不建议这样做。使用标记只会中断,go get而不会给您的日常工作流程带来任何其他好处:



实际上,您唯一需要启用自动售货的地方就是您的IDE:



经过一番尝试和错误之后,我想出了以下步骤,以这种方式添加供应商依赖性。

步骤1.要求


您可以要求依赖项go get

go get github.com/rs/zerolog@v1.14.3

步骤2.汇入


然后将其导入代码中的某个位置:

import (
   _ "github.com/rs/zerolog"
)

步骤3.贩卖


最后,重新打开您的依赖项:

go mod vendor

有一个悬而未决的建议,允许go mod供应商接受某些模块模板,这些模板可以(或可以不)解决与此工作流程相关的一些问题。

go mod vendor已经自动要求错过导入,因此步骤1在此工作流程中是可选的(如果您不想指定版本限制)。但是,如果没有步骤2,它将无法提取下载的软件包。

这种方法在主机系统上效果更好,但是在编辑依赖项时却相当混乱。



我个人认为重新定义GOPATH是一种更干净的方法,因为它不会牺牲功能go get尽管如此,我还是想展示这两种策略,因为其他语言(例如PHP,Ruby,Javascript等)的人可能会对vendor文件夹更加熟悉。从本文中描述的欺诈行为可以看出,对于Go来说并不是一个特别好的选择。

工具依赖


我们可能需要安装一些基于Go的工具,这些工具不是导入的,而是用作项目开发环境的一部分。此类工具的一个简单示例是CompileDaemon,它可以监视代码中的更改并重新启动应用程序。

官方推荐的方法是添加tools.go具有以下内容文件(名称无关紧要):

// +build tools
package tools
import (
_ "github.com/githubnemo/CompileDaemon"
)

  • 该限制会// +build tools阻止您的常规程序集实际导入工具。
  • 导入表达式使go命令可以将工具的版本信息准确地写入go.mod模块文件

这就是全部。希望您不会像我刚开始使用Go模块时那样困惑。您可以访问Go Modules Wiki了解更多详细信息。



上课程。



All Articles