Go模块:版本控制和依赖性管理

版本控制和依赖性管理一直是企业采用go的障碍,因此,Go v1.11引入了对Go模块的初步支持,希望在整个go生态系统中统一依赖性管理。

模块引入了一些不同的概念,然后引入了其他编程语言的传统依赖性管理工具。

根据Modules Wiki:

模块是相关的Go软件包的集合,这些Go软件包一起作为一个单元进行了版本控制。 通常,单个版本控制存储库恰好对应于一个模块,但是,另一个版本控制存储库可以容纳多个模块。

模块为形式编号使用v(major).(minor).(patch)形式的语义版本控制。

模块记录精确的依赖要求并创建可复制的构建。 go.mod在包含go源文件的树的根目录中定义了一个模块。 一个示例go.mod文件如下所示:

 模块github.com/my/thingrequire( 
github.com/some/dependency v1.2.3
github.com/another/dependency/v4 v4.0.0

go模块的引入为弃用GOPATH因为项目不再需要驻留在GOPATH 。 Go模块可以在GOPATH之外创建。

MVS希望使用与给定项目兼容的最古老的已知“良好”版本库,其中已知的“良好”是人类指定的版本。 如果go.mod文件指定需要版本v1.2,则即使v1.3可用,Go也会使用版本v1.2;否则,Go将使用版本v1.2。 假定没有其他依赖项对此库有需求。

如果我们项目的go.mod文件指定它需要一个库的v1.2,而其其他依赖项之一指定它需要同一个库的v1.3,则Go将使用v1.3,因为它是最低版本(最老),满足所有规定的版本需求。

这与其他编程语言使用的大多数其他依赖工具形成了鲜明的对比。 大多数其他语言的依赖项工具将使用一组复杂的符号,使开发人员可以让该工具决定是否可以使用新版本(如果有较新版本的话),并且语义版本化表明没有任何问题。

以前,导入路径是导入路径,开发人员使用的是master分支上驻留的库的版本,或者使用诸如dep之类的工具来指定要使用的VCS存储库的哪个分支/标记/哈希。

语义导入版本或SIV,指定主要版本在导入路径中具有其版本号。 因此,对于第二版, github.com/russross/blackfriday将变为github.com/russross/blackfriday/v2对于github.com/russross/blackfriday它将变为github.com/russross/blackfriday/v2

不用担心,无需更新较旧的导入路径,因为允许2.0版之前的版本使用不带版本的导入路径,而版本0是golang的向后兼容规则的例外,因为在版本1之前可以向后兼容的版本。 最好是刚刚开始的库。

现在,让我们跳入测试go模块。

让我们创建我们的包。 我们将其称为“ testmod”。

注意:此目录应位于 $GOPATH 之外, 因为默认情况下,其中的模块支持处于禁用状态。 要在 $GOPATH 使用它, 我们需要通过设置环境变量 export GO111MODULE=on; 来手动激活模块模式 export GO111MODULE=on;

  $ mkdir testmod 
$ go mod init github.com/amansardana/testmod
$ vim testmod.go

我们的“ testmod”的第一个版本包含非常基本的功能Print,它打印出“ Hello,World!”

  package testmodimport“ fmt” //打印打印字符串“ Hello,World!” 
func Print(){
fmt.Println(“ H​​ello,World!”)
}

最初的go.mod文件如下所示:

 模块github.com/amansardana/testmod 

现在我们将代码推送到github存储库,以便可以通过go get获取来使用它。

  $ git init 
$ git添加。
$ git commit -m“第一次提交”
$ git remote add origin git@github.com:amansardana / testmod.git
$ git push -u原始主机

现在我们的包已经准备好了,我们可以将它发布给全世界了。 我们通过使用版本标签来实现。 让我们发布1.0.0版本:

  $ git标签v1.0.0 
$ git push-标签

现在我们可以使用我们的模块了。 让我们创建一个使用我们模块的简单程序。

 包mainimport( 
“ github.com/amansardana/testmod”
)func main(){
testmod.Print()
}

让我们使用go模块获取依赖项并构建程序

  $ go mod init mod 
$去建立

如我们所见, go build命令自动执行并获取程序导入的软件包。 如果我们检查go.mod文件,它将如下所示:

 模块modrequire github.com/amansardana/testmod v1.0.1 

现在,我们还有一个名为go.sum文件,其中包含软件包的校验和,以确保我们具有正确的版本和文件。

  github.com/amansardana/testmod v1.0.0 h1:QLHAJZN6FDI5mVgeOQ59LH7ayei ++ OMQXwDtH9SIaq8 = 
github.com/amansardana/testmod v1.0.0 / go.mod h1:TWBE1bzyS / RR8 + 3LGTDrbIF2mpv / + 5OblpC / izinYlU =

现在,让我们更新我们的testmod模块,以打印“ Hola,Techreveri!”,并将其作为v1.0.1版的补丁发布。

  package testmodimport“ fmt” //打印打印字符串“ Hola,Techreveri!” 
func Print(){
fmt.Println(“ H​​ola,Techreveri!”)
} $ git commit -am“你好,世界->你好,Techreveri”
$ git标签v1.0.1
$ git push-标签

由于我们正在发布补丁程序,因此我们不需要更新模块的更新导入路径。

在我们的测试程序mod中更新依赖项。 我们可以使用以下任一go get命令获取更新:

  $去得到-u 
$ go get -u =补丁
$去获取github.com/amansardana/testmod@v1.0.1

运行后,说go get -u=patch我们的go.mod更改为:

 模块模组 
需要github.com/amansardana/testmod v1.0.1

现在让我们以向后不兼容的方式更改我们的模块,并将其发布为v2。

 软件包testmodimport“ fmt” //打印以问候语和名称作为参数传递的问候语 
func打印(称呼,姓名字符串){
fmt.Printf(“ \ n%s,%s!\ n”,称呼,名称)
}

由于我们要发布主要版本,因此我们还需要根据语义导入版本控制来更新模块的导入路径。 我们必须更新go.mod文件,以将v2包含在模块的导入路径中。

 模块github.com/amansardana/testmod/v2$ git commit -m“增加到v2” 
$ git标签v2.0.0
$ git push-标签

即使我们发布了库的新不兼容版本,现有软件也不会中断 ,因为它将继续使用现有版本1.0.1。 go get -u 将不会获得版本2.0.0。

现在,如果我们想更新代码以使用库testmod的v2。 然后,我们必须手动执行此操作。

 包mainimport( 
“ github.com/amansardana/testmod”
testmodV2“ github.com/amansardana/testmod/v2”
)func main(){
testmod.Print()
testmodV2.Print(“ Hola”,“ Techreveri”)
}

让我们整理一下。

  $去整理整洁 

tidy是mod工具链中包含的mod工具。 确保go.mod与模块中的源代码匹配。 它添加了构建当前模块的程序包和依赖项所必需的所有缺少的模块,并删除了不提供任何相关程序包的未使用模块。 它还会将所有缺少的条目添加到go.sum中,并删除所有不必要的条目。

现在,我们的go.mod包含模块的两个版本作为依赖项。 这消除了依赖性管理的一个常见问题:依赖性依赖于同一库的不同版本。

Go模块默认情况下会忽略vendor/目录。 这个想法是最终消除供应商。 但是,如果我们仍然希望将供应商依赖性添加到我们的版本控制中,我们仍然可以这样做:

  $ go mod供应商 

这将在项目的根目录下创建一个vendor/目录,其中包含所有依赖项的源代码。

不过,默认情况下, go build将忽略此目录的内容。 如果要从vendor/目录构建依赖关系,则需要询问它。

  $ go build -mod = vendor 

模块是一种管理Go社区渴望的依赖版本的方法。 它是Go语言和工具的核心功能,是整个社区使用彼此的库并填补自Go公开以来所希望的功能漏洞的单一方式。

模块通过启用更多配置选项为您提供更多控制权。 它在项目的整个生命周期内都提供了可靠性和一致性。 它减轻了许多开发人员在长期工作的项目的痛苦。

它还消除了使用$GOPATH的需要,后者是新的Go开发人员的障碍,他们无法理解为什么必须将内容放入特定目录。

PS :阅读有关techreveri的原始文章。 此处使用的代码示例可以在github上找到。