https://cnblogs.com/songhaibin/articles/15512953.html


1. 三种包管理方式

使用go path问题

  1. 代码开发必须在go path src目录下,不然,就有问题。
  2. 依赖手动管理
  3. 依赖包没有版本可言

从这个看, go path不算包管理工具


govendor

  1. 解决了包依赖,一个配置文件就管理
  2. 依赖包全都下载到项目vendor下,每个项目都把有一份。拉取项目时,开始怀疑人生。

go mod介绍

go modules 是 golang 1.11 新加的特性。现在1.12 已经发布了,是时候用起来了。Modules官方定义为:

模块是相关Go包的集合。modules是源代码交换和版本控制的单元。 go命令直接支持使用modules,包括记录和解析对其他模块的依赖性。modules替换旧的基于GOPATH的方法来指定在给定构建中使用哪些源文件。





2. go mod基本说明

下面设置go mod和go proxy

1
2
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct // 使用七牛云的

GO111MODULE

GO111MODULE 有三个值:off, on和auto(默认值)。

GO111MODULE=off,go命令行将不会支持module功能,寻找依赖包的方式将会沿用旧版本那种通过vendor目录或者GOPATH模式来查找。
GO111MODULE=on,go命令行会使用modules,而一点也不会去GOPATH目录下查找。
GO111MODULE=auto,默认值,go命令行将会根据当前目录来决定是否启用module功能。这种情况下可以分为两种情形:

1
2
当前目录在GOPATH/src之外且该目录包含go.mod文件
当前文件在包含go.mod文件的目录下面。

当modules功能启用时,依赖包的存放位置变更为$GOPATH/pkg,允许同一个package多个版本并存,且多个项目可以共享缓存的 module





3. go mod命令

命令 说明
download 下载依赖包到本地缓存
edit 从工具或脚本编辑 go.mod 文件
graph 打印模块依赖图
init 在当前目录初始化新模块
tidy 拉取缺少的模块,移除不用的模块
vendor 将依赖复制到 vendor 目录下
verify 验证依赖是否正确
why 解释为什么需要某些包或模块

初始化项目

在项目根目录下执行go mod init xxx,go.mod文件一旦创建后,它的内容将会被go toolchain全面掌控。go toolchain会在各类命令执行时,比如go get、go build、go mod等修改和维护go.mod文件。

go.mod 提供了module, require、replace和exclude 四个命令

  • module 语句指定包的名字(路径)
  • require 语句指定的依赖项模块
  • replace 语句可以替换依赖项模块
  • exclude 语句可以忽略依赖项模块

添加依赖

在使用Go模块(Go Modules)管理依赖时,Go工具链遵循一定的原则来决定应该安装哪个版本的包(package)。下面是这些原则的详细解释:

  1. 安装包的原则

当你使用go get命令来安装一个包时,Go会首先检查该包的所有公开发布的版本(通常是通过git标签来标记的版本)。Go的偏好是:

  • 首先拉取最新的发布版本(release tag):这意味着如果一个库有一个或多个发布的版本,Go将会选择最新的版本。这些版本通常会遵循语义版本控制(SemVer),比如v1.2.3

  • 如果没有发布的版本,将拉取最新的commit:如果一个库没有任何发布的版本标签,Go将会使用该库的最新commit。这种情况下,依赖的版本可能不稳定,因为它直接反映了库在主分支(通常是mastermain)上的最新开发状态。

这种机制旨在确保项目能够利用依赖的稳定和经过测试的版本,同时也提供了一种方法来使用最新或未发布的更改。

  1. go.sum 文件

当你使用Go模块工作时,Go工具会自动创建和维护一个名为go.sum的文件。这个文件的作用包括:

  • 记录依赖树(dependency tree)go.sum文件包含了项目所依赖的每个模块的精确版本和这些版本的哈希值。这不仅仅是直接依赖,还包括这些依赖所依赖的其他模块(即间接依赖)。

  • 确保依赖的完整性和安全性:通过记录每个依赖版本的哈希值,go.sum文件帮助确保这些依赖在将来获取时仍然是一致的,未被篡改。这意味着无论何时何地构建项目,所使用的依赖都应该是完全相同的,从而提高了构建的可重复性和安全性。


go get升级

  • 运行 go get -u 将会升级到最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号)
  • 运行 go get -u=patch 将会升级到最新的修订版本
  • 运行 go get package@version 将会升级到指定的版本号version
  • 运行go get如果有版本的更改,那么go.mod文件也会更改

使用replace替换无法直接获取的package

由于某些已知的原因,并不是所有的package都能成功下载,比如:golang.org下的包。

modules 可以通过在 go.mod 文件中使用 replace 指令替换成github上对应的库,比如:

1
2
3
replace (
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a => github.com/golang/crypto v0.0.0-20190313024323-a1f597ede03a
)

为什么使用 replace?

  1. 处理无法访问的包:有些包,比如golang.org/x/下的包,在某些地区可能无法直接访问或下载。虽然这些包通常也在GitHub上有镜像,但Go工具链默认可能直接尝试从原始的golang.org域名下载,导致失败。使用replace指令可以将这些包的来源重定向到GitHub上的镜像地址,确保下载和使用的可行性。
  2. 开发和调试:如果你正在开发或修改某个依赖库,并希望在项目中测试它,你可以使用replace将依赖指向你本地的文件路径,而不是远程库。这样可以即时看到本地更改的效果,而无需推送更改到远程仓库。
  3. 依赖问题修复:有时候,一个库的特定版本可能包含错误或问题,而这些问题在另一个版本或fork中已经被修复。通过replace,你可以强制项目使用修复后的版本。




4. go mod发布和使用

Creating a Module

如果你设置好go mod了,那你就可以在任何目录下随便创建

1
2
$mkdir gomodone
$cd gomodone

在这个目录下创建一个文件say.go

1
2
3
4
5
6
7
8
package gomodone

import "fmt"

// say Hi to someone
func SayHi(name string) string {
return fmt.Sprintf("Hi, %s", name)
}

初始化一个 go.mod文件

1
2
$ go mod init github.com/jacksonyoudi/gomodone
go: creating new go.mod: module github.com/jacksonyoudi/gomodone

查看 go.mod内容如下:

1
2
github.com/jacksonyoudi/gomodone
go 1.14

下面我们要将这个module发布到github上,然后在另外一个程序使用

1
2
3
4
5
6
$git init
$vim .gitiiignore
$git commit -am "init"
// github创建对应的repo
$git remote add origin git@github.com:jacksonyoudi/gomodone.git
$git push -u origin master

执行完,上面我们就相当于发布完了。

如果有人需要使用,就可以使用

1
go get github.com/jacksonyoudi/gomodone

这个时候没有加tag,所以,没有版本的控制。默认是v0.0.0后面接上时间和commitid。如下:

1
gomodone@v0.0.0-20200517004046-ee882713fd1e

官方不建议这样做,没有进行版本控制管理。


module versioning

使用tag,进行版本控制

making a release:

1
2
git tag v1.0.0
git push --tags

操作完,我们的module就发布了一个v1.0.0的版本了。

推荐在这个状态下,再切出一个分支,用于后续v1.0.0的修复推送,不要直接在master分支修复

1
2
$git checkout -b v1
$git push -u origin v1




5. Vendoring

Vendoring 概述

Vendoring允许你将所有的外部依赖复制到你的项目内部(通常是vendor目录)。这样做的主要好处是确保构建的可重复性,无论外部源的状态如何变化,你的项目都可以使用已经存储的、不变的依赖版本进行构建。

使用vendor目录的好处

  1. 离线构建:在vendor目录中保留所有依赖的副本,可以在没有网络连接的情况下构建项目。
  2. 控制和稳定性:锁定依赖的具体版本,防止未经意的升级引入潜在的问题。
  3. 避免依赖消失:如果一个依赖库从公共存储库中消失,拥有本地副本意味着你的项目仍然可以构建和运行。
  4. 合规和审核:对于需要严格审计依赖来源和版本的环境,vendor目录提供了一个简单的解决方案。

如何使用vendor目录

  1. 生成vendor目录

虽然go mod vendor是正确的命令,但你提供的命令有误。正确的命令是:

1
go mod vendor

这个命令会根据项目根目录下的go.mod文件,将所有依赖复制到vendor目录中。执行后,所有的模块依赖都会按照go.mod文件中记录的具体版本,存放在项目的vendor目录中。

  1. 构建时使用vendor目录

当你希望Go工具链在构建项目时使用vendor目录中的依赖,而不是下载最新的或缓存的依赖,可以使用如下命令:

1
go build -mod=vendor

这个命令告诉Go编译器在构建时查找vendor目录中的依赖。这是确保项目使用已验证和已审计依赖的一种方式,特别是在生产环境中部署时。

  1. 类似的
1
go run -mod=vendor main.go