目录
前言
即看即用
Go 包依赖管理工具govendor
vendor 特性
vendor 使用建议
govendor 简介
govendor 使用
安装
初始化
常用命令
govendor 子命令
govendor 状态参数
Go modules
常见错误
参考
go mod与 govendor 区别与使用
vendor 目录方式
解决的问题
未解决的问题
什么是 GOROOT 和 GOPATH
要解决 vendor 目录 未解决的问题,使用 govendor
govendor
常用命令
使用建议
缺点
go mod
go mod
go mod 与 go vendor 区别
Golang 管理go类库版本
Go类库版本规则
Go类库发版示例
小版本升级
大版本升级
使用本地go类库
结束语
前言
go mod 和 govendor 都是 Go 包管理器,类似 Java 工程的 maven
go 包依赖管理史( 按时间出现顺序):
- 2012年3月 Go 1 发布,此时没有版本的概念
- 2013年 Golang 团队在 FAQ 中提议开发者保证相同 import path 的兼容性,后来成为一纸空文
- 2013年10月 Godep
- 2014年7月 glide
- 2014年 有人提出 external packages 的概念,在项目的目录下增加一个 vendor 目录来存放外部的包
- 2015年8月 Go 1.5 实验性质加入 vendor 机制
- 2015年 有人提出了采用语义化版本的草案
- 2016年2月 Go 1.6 vendor 机制 默认开启
- 2016年5月 Go 团队的 Peter Bourgon 建立委员会,讨论依赖管理工具,也就是后面的 dep
- 2016年8月 Go 1.7: vendor 目录永远启用
- 2017年1月 Go 团队发布 Dep,作为准官方试验
- 2018年8月 Go 1.11发布 Modules 作为官方试验
- 2019年2月 Go 1.12发布 Modules 默认为 auto
- 2019年9月 Go 1.13 版本默认开启 Go Mod 模式
……
故事:失宠的 Vendor 目录
Vendor目录是Golang从1.5版本开始引入的,为项目开发提供了一种离线保存第三方依赖包的方法。但是到了Golang 1.11之后,由于引入了Module功能,在运行go build时,优先引用的是Module依赖包的逻辑,所以Vendor目录就被“无视”了,进而可能发生编译错误, moudle 说还是很想他,于是 提供了 go mod vendor 命令用来生成 vendor 目录。这样能避免一些编译问题,依赖可以先从 vendor 目录进行扫描。
$ go mod help vendor usage: go mod vendor [-v] Vendor resets the main module's vendor directory to include all packages needed to build and test all the main module's packages. It does not include test code for vendored packages.
这句话的意思是:把 go mod init 后下载的相关依 赖包(Gopath 的 pkg) 目录,拷贝到 vendor 目录。
争议:https://zhuanlan.zhihu.com/p/59191567
go mod vendor 只是给的兼容方案。
vendor 目录就不该入代码仓库的。
国内网络问题,goproxy 可完美解决,gomod/athens
至少能用了,代理私有库也没问题。
生产环境 1.11 就开始全面 go mod 的路过。
目前我们公司的开发人员建议使用go mod 代替 govendor
即看即用
govendor 使用
要求:
- 项目必须在
$GOPATH/src
目录下 - 如果 Go 版本为 1.5时,则须手动设置环境变量
set GO15VENDOREXPERIMENT=1,1.6以上版本不需要
安装
go get -u github.com/kardianos/govendor
为了方便快捷使用 govendor
,建议将 $GOPATH/bin
添加到 PATH 中。Linux/macOS 如下设置:
export PATH="$GOPATH/bin:$PATH"
初始化
在项目根目录下执行以下命令进行 vendor
初始化:
govendor init
1、govendor list可以快速查看你项目中的外部依赖包。
2、govendor add添加依赖包到vendor目录下,在使用 govendor add命令时,后面需要跟上下面介绍的一些状态,也可以直接跟上缺失包的地址,如下文常见错误中的做法。
3、govendor update从你的GOPAHT中更新你工程的依赖包
4、govendor remove从你工程下的vendor文件中移除对应的包
5、govendor fetch添加或者更新vendor文件夹中的包
govendor使用状态来指定包
1 +local (l) 表示工程中的包
2 +external (e) 从GOPATH中引用的包,但不包含在你的当前工程中
3 +vendor (v) vendor文件夹中的包
4 +std (s) Go标准库中的包
5 +excluded (x) 从vendor文件中排除的外部依赖包
6 +unused (u) vendor文件中存在但却未使用的包
7 +missing (m) 项目引用但却为发现的包
8 +program (p) main包中包
其中有一些状态存在简写,例如:+std可以用+s表示,+external可以用+ext或者+e表示,+external可以用+exc或者+x表示。
在使用时,你也可以对这些状态进行逻辑组合,例如:
1 +local,grogram表示既满足+local又满足+program。
2 +local +vendor表示只要满足两者之一。
3 +vendor,program +std表示vendor和program是与的关系,整体和std是或的关系
4 +vendor,^program表示满足vendor,但却不满足program。
Go 包依赖管理工具govendor
(官方文档:https://pkg.go.dev/gitee.com/jaykieq/govendor#section-readme)
govendor 是一个基于
vendor
机制实现的 Go 包依赖管理命令行工具。与原生 vendor 无侵入性融合,也支持从其他依赖管理工具迁移,可以很方便的实现同一个包在不同项目中不同版本、以及无相互侵入的开发和管理。
vendor 特性
vendor是go的依赖包管理工具,它将项目依赖的包,特指外部包,复制到当前工程下的vendor目录下,这样go build的时候,会优先从vendor目录寻找依赖包。
将依赖的外部包引进工程下的vendor目录,(随工程到)其他机器上,其他机器就可以直接编译,而不用再去构建外部包的编译环境,一个一个的go get获取外部包(当然你也可以直接拷贝整个GOPATH下的第三方包源码过来,也是起到了vendor相同的作用)。另外,防止了go get 重新拉取的外部包的版本可能和期望的不一致,从而导致编译错误问题
Go 从 1.5 版本开始提供了 vendor 特性,但需要手动设置环境变量 GO15VENDOREXPERIMENT=1 启用。
在执行 go build
或 go run
命令时,会按照以下顺序去查找包:
- 当前包下的 vendor 目录
- 向上级目录查找,直到找到 src 下的 vendor 目录
- 在 GOROOT 目录下查找
- 在 GOPATH 下面查找依赖包
发布 1.6 版本时,该环境变量的值已经默认设置为 1 了,该值可以使用 go env
命令查看。
发布 1.7 版本时,已去掉该环境变量,默认开启 vendor
特性。
vendor 使用建议
- 一个库工程(不包含
main
的 package
)不应该在自己的版本控制中存储外部的包在 vendor
目录中,除非有特殊原因并且知道为什么要这么做。 - 在一个应用中,(包含
main
的 package
),建议只有一个 vendor
目录,且在代码库一级目录。
govendor 简介
govendor 是一个基于 vendor
目录机制的包管理工具。
- 支持从项目源码中分析出依赖的包,并从
$GOPATH
复制到项目的 vendor
目录下 - 支持包的指定版本,并用
vendor/vendor.json
进行包和版本管理,这点与 PHP 的 Composer
类似 - 支持用
govendor add/update
命令从 $GOPATH
中复制依赖包 - 如果忽略了
vendor/*/
文件,可用 govendor sync
恢复依赖包 - 可直接用
govendor fetch
添加或更新依赖包 - 可用
govendor migrate
从其他 vendor
包管理工具中一键迁移到 govendor
- 支持 Linux,macOS,Windows,甚至现有所有操作系统
- 支持 Git、Hg、SVN,BZR(必须指定一个路径)
govendor 使用
要求:
- 项目必须在
$GOPATH/src
目录下 - 如果 Go 版本为 1.5,则必须手动设置环境变量
set GO15VENDOREXPERIMENT=1
安装
go get -u github.com/kardianos/govendor
#go get -u github.com/kardianos/govendor下载govendor工具到本地,一般在$GOPATH/bin目录下,可以用updatedb&&locate govendor|grep -w govendor 查看
为了方便快捷使用 govendor
,建议将 $GOPATH/bin
添加到 PATH 中。Linux/macOS 如下设置:
export PATH="$GOPATH/bin:$PATH"
初始化
在项目根目录下执行以下命令进行 vendor
初始化:
cd "my project in GOPATH"
-
govendor init
项目根目录下即会自动生成 vendor
目录和 vendor.json
文件。此时 vendor.json
文件内容为:
{
"comment": "",
"ignore": "test",
"package": [],
"rootPath": "govendor-example"
}
常用命令
- 将已被引用且在
$GOPATH
下的所有包复制到 vendor
目录
govendor add +external
- 仅从
$GOPATH
中复制指定包
govendor add gopkg.in/yaml.v2
- 列出代码中所有被引用到的包及其状态(可以快速查看你项目中的外部依赖包。)
govendor list
e github.com/gin-contrib/sse
e github.com/gin-gonic/gin
e github.com/gin-gonic/gin/binding
e github.com/gin-gonic/gin/internal/json
e github.com/gin-gonic/gin/render
e github.com/golang/protobuf/proto
e github.com/mattn/go-isatty
e github.com/ugorji/go/codec
e gopkg.in/go-playground/validator.v8
e gopkg.in/yaml.v2
pl govendor-example
m github.com/json-iterator/go
m golang.org/x/sys/unix
- 列出一个包被哪些包引用
govendor list -v fmt
s fmt
├── e github.com/gin-contrib/sse
├── e github.com/gin-gonic/gin
├── e github.com/gin-gonic/gin/render
├── e github.com/golang/protobuf/proto
├── e github.com/ugorji/go/codec
├── e gopkg.in/go-playground/validator.v8
├── e gopkg.in/yaml.v2
└── pl govendor-example
- 从远程仓库添加或更新某个包(不会在
$GOPATH
也存一份)
govendor fetch golang.org/x/net/context
- 安装指定版本的包
govendor fetch golang.org/x/net/context@a4bbce9fcae005b22ae5443f6af064d80a6f5a55
govendor fetch golang.org/x/net/context@v1 # Get latest v1.*.* tag or branch.
govendor fetch golang.org/x/net/context@=v1 # Get the tag or branch named "v1".
- 只格式化项目自身代码(
vendor
目录下的不变动)
govendor fmt +local
- 只构建编译项目内部的包
govendor install +local
- 只测试项目内部的测试案例
govendor test +local
- 构建所有
vendor
包
govendor install +vendor,^program
- 拉取所有依赖的包到
vendor
目录(包括 $GOPATH
存在或不存在的包)
govendor fetch +out
- 包已在
vendor
目录,但想从 $GOPATH
更新
govendor update +vendor
- 已修改了
$GOPATH
里的某个包,现在想将已修改且未提交的包更新到 vendor
govendor update -uncommitted <updated-package-import-path>
- Fork 了某个包,但尚未合并,该如何引用到最新的代码包
govendor fetch github.com/normal/pkg::github.com/myfork/pkg
此时将从 myfork
拉取代码,而不是 normal
。
-
vendor.json
中记录了依赖包信息,该如何拉取更新
govendor sync
govendor 子命令
各子命令详细用法可通过 govendor COMMAND -h
或阅读 github.com/kardianos/govendor/context
查看源码包如何实现的。
子命令 | 功能 |
init | 创建 |
list | 列出&过滤依赖包及其状态 |
add | 从 |
update | 从 |
remove | 从 |
status | 列出所有缺失、过期和修改过的包 |
fetch | 从远程仓库添加或更新包到项目 |
sync | 根据 |
migrate | 从其他基于 |
get | 与 |
license | 列出所有依赖包的 LICENSE |
shell | 可一次性运行多个 |
govendor 状态参数
状态 | 缩写 | 含义 |
+local | l | 本地包,即项目内部编写的包,即项目自身的包组织 |
+external | e | 外部包,即在 |
+vendor | v | 已被 govendor 管理,即在 vendor 目录下 |
+std | s | 标准库里的包 |
+excluded | x | 明确被排除的外部包 |
+unused | u | 未使用的包,即在 |
+missing | m | 被引用了但却找不到的包 |
+program | p | 主程序包,即可被编译为执行文件的包 |
+outside | 相当于状态为 | |
+all | 所有包 |
支持状态参数的子命令有:list
、add
、update
、remove
、fetch
Package specifier
完整的包规范是:<path>[::<origin>][{/...|/^}][@[<version-spec>]]
一些例子:
-
github.com/kardianos/govendor
指定单个包和单个文件夹。 -
github.com/kardianos/govendor/...
指定 govendor 和该路径下的所有引用包。 -
github.com/kardianos/govendor/^
specifies the govendor
folder and all sub-folders. Useful for resources or if you don't want a partial repository. -
github.com/kardianos/govendor/^::github.com/myself/govendor
same as above but fetch from user "myself". -
github.com/kardianos/govendor/...@abc12032
修订版 abc12032 中所有引用的软件包。 -
github.com/kardianos/govendor/...@v1
同上,但获取最新的“v1”标签,例如“v1.4.3”。 -
github.com/kardianos/govendor/...@=v1
获取确切的版本“v1”。
Go modules
普大喜奔的是,从 Go 1.11 版本开始,官方已内置了更为强大的 Go modules 来一统多年来 Go 包依赖管理混乱的局面(Go 官方之前推出的 dep 工具也几乎胎死腹中),并且将在 1.13 版本中正式默认开启。
目前已受到社区的看好和强烈推荐,建议新项目采用 Go modules。
版本管理
(https://zhuanlan.zhihu.com/p/103914406)
不要将整个 vendor/
目录的内容都提到 git 仓库,只提交 vendor/vendor.json
文件就可以了。
当我们拉代码之后,需要安装依赖包时,只需要执行下面这条命令就可以了。
.gitignore
文件,重点在最后两行:
所以,一般的开发流程可以这样来做:如果是新建项目,先安装 govendor 并初始化,然后通过 govendor 来安装依赖包;如果是已有项目,先从版本库拉下来,然后安装 govendor,再执行同步命令即可。
常见错误
1、服务器提示某个依赖包没有找到
原因可能是vendor文件中没有该包或者vendor.json文件中没有该包的描述信息。
假设"github.com/astaxie/beego/logs"包的信息在vendor.json文件中没有找到,则在go命令行中执行govendor add github.com/astaxie/beego/logs。
Go. command not found: govendor.
是因为没有把 $GOPATH/bin 加入 PATH 中, 在自己使用的bash中加入:👇,关闭,然后执行 source .zshrc(自己使用的bash)
$GOPATH 是Go设置的env
参考
- govendor 项目
- golang使用vendor目录来管理依赖包
- Golang包管理工具之govendor的使用
go mod与 govendor 区别与使用
(转自:https://developer.aliyun.com/article/937944)
vendor 目录方式
go vendor 是go 1.5 官方引入管理包依赖的方式
基本思路: 将引用的外部包的源代码放在当前工程的vendor目录下面,go 1.6以后编译go代码会优先从vendor目录先寻找依赖包;找不到再从GOPATH 中寻找
解决的问题
将源码拷贝到当前目录下,这样导包当前工程代码到任意的机器的 ¥GOPATH/src 都可以编译通过,避免项目代码外部依赖过多
未解决的问题
无法精确的引用 外部包进行版本控制,不能指定引用某个特定版本的外部包,只是在开发时将其拷贝过来,但是一旦外部包升级,vendor 下面的包会跟着升级,而且 vendor 下面没有完整的引用包的版本信息, 对包升级带来了无法评估的风险。
什么是 GOROOT 和 GOPATH
- GOROOT:golang的安装路径,当安装好go之后,默认会安装在/usr/local/go之下。GOROOT的主要作用是标识go的当前安装位置。
- GOPATH:存放SDK以外的第三方类库;收藏的可复用的代码,包含三个子目录:-- src : 存放项目源码文件 -- pkg : 编译后的包文件 -- bin :编译后生成的可执行文件
要解决 vendor 目录 未解决的问题,使用 govendor
- 可以平滑的将现有非 vendor 项目转换成 vendor 项目
govendor add inport_out_packagename
- 会生成一个元数据文件,记录工程依赖的外部包,及其版本信息
vendor.json
- 提高命令查看整个工程的依赖关系
goverdor --list goverdor --list -v
govendor
govendor 是一个基于 vendor 机制实现的 Go 包依赖管理命令行工具。与原生 vendor 无侵入性融合,也支持从其他依赖管理工具迁移,可以很方便的实现同一个包在不同项目中不同版本、以及无相互侵入的开发和管理。
在执行 go build 或 go run 命令时,会按照以下顺序去查找包:
- 当前包下的 vendor 目录
- 向上级目录查找,直到找到 src 下的 vendor 目录
- 在 GOROOT 目录下查找
- 在 GOPATH 下面查找依赖包
常用命令
- 安装
go get -u -v github.com/kardianos/govendor
- 初始化
cd xxx govendor init
初始化完成后,项目目录中会生成一个vendor文件夹,包含一个vendor.json文件,json文件中包含了项目所依赖的所有包信息
{ "comment": "", "ignore": "test", "package": [], "rootPath": "govendor-example" }
将已被引用且在 $GOPATH 下的所有包复制到 vendor 目录
govendor add +external
仅从 $GOPATH 中复制指定包
govendor add gopkg.in/yaml.v2
列出代码中所有被引用到的包及其状态
govendor list
运行结果
e github.com/gin-contrib/sse e github.com/gin-gonic/gin e github.com/gin-gonic/gin/binding e github.com/gin-gonic/gin/internal/json e github.com/gin-gonic/gin/render e github.com/golang/protobuf/proto e github.com/mattn/go-isatty e github.com/ugorji/go/codec e gopkg.in/go-playground/validator.v8 e gopkg.in/yaml.v2 pl govendor-example m github.com/json-iterator/go m golang.org/x/sys/unix
列出一个包被哪些包引用
govendor list -v fmt
s fmt ├── e github.com/gin-contrib/sse ├── e github.com/gin-gonic/gin ├── e github.com/gin-gonic/gin/render ├── e github.com/golang/protobuf/proto ├── e github.com/ugorji/go/codec ├── e gopkg.in/go-playground/validator.v8 ├── e gopkg.in/yaml.v2 └── pl govendor-example
使用建议
- 使用govendor管理项目并进行项目协作时,我们每次不需要提交整个vendor目录,而只需要提交json文件,十分方便。一个配置文件全部搞定!
- vendor 目录解决了工程依赖打包的问题,可将依赖与工程一起打包,减少下载依赖的时间。
- 同一个项目只创建一个govendor目录,且在代码库的一级目录。
缺点
- 依赖包全部都在vendor目录下,每个项目都有一份,所以每次拉取项目时都会拉一遍依赖。
- govendor不区分包版本,意味着开发期间拉的依赖的包很可能跟上线后的拉的依赖包版本不一致,很危险。
- govendor add +e会拉取全部外部包,即使是本项目没有用到的,这样会造成大量的冗余。但是只用govendor add +指定包又很麻烦。
go mod
go module是Go1.11版本之后官方推出的版本管理工具,并且从Go1.13版本开始,go module将是Go语言默认的依赖管理工具。
包不再保存在GOPATH中,而是被下载到了$GOPATH/pkg/mod路径下.
go mod vendor 会将依赖包放到 vendor 目录
- go.mod文件记录了项目所有的依赖信息,其结构大致如下:
module github.com/Q1mi/studygo/blogger go 1.12 require ( github.com/DeanThompson/ginpprof v0.0.0-20190408063150-3be636683586 github.com/gin-gonic/gin v1.4.0 github.com/go-sql-driver/mysql v1.4.1 github.com/jmoiron/sqlx v1.2.0 github.com/satori/go.uuid v1.2.0 google.golang.org/appengine v1.6.1 // indirect )
- go.sum是一个构建状态跟踪文件。它会记录当前module所有的顶层和间接依赖,以及这些依赖的校验和,来确保这些模块的将来下载内容与第一次下载的内容相同,但是第一次下载的模块也有可能是非法的(代理服务不可信、模块源被黑等),所以Go 1.13推出GOSUMDB(Go CheckSum Database)用来公证模块的Hash值,从而提供一个可以100%复现的构建过程并对构建对象提供安全性的保证,同时还会保留过去使用的包的版本信息,以便日后可能的版本回退。
go mod
go mod 与 go vendor 区别
Golang 管理go类库版本
之前介绍了如何使用github托管go类库,详见Golang 使用github托管go类库,接着上篇,本篇介绍go类库的版本管理。
Go类库版本规则
go类库版本的规则:主版本号.次版本号.修订号
,其中:
- 主版本号:类库进行了不可向下兼容的修改,例如功能重构,这时候主版本号往上追加;
- 次版本号:类库进行了可向下兼容的修改,例如新增功能,这时候次版本号往上追加;
- 修订号:类库进行了可向下兼容的修改(修改的规模更小),例如修复或优化功能,这时候修订好往上追加。
Go类库发版示例
同样以github.com/vsixz/common-go
类库为示例。
小版本升级
主版本不升级,次版本或修订版本升级。
v0.x.x版本升级至v1.x.x也是可以直接升级的。
当前版本是v1.0.0
,现对该类库进行了功能修改,发布v1.0.1
版本:
1、切换至release/1.x
分支
2、修改类库代码
3、提交代码并发布
4、使用demo-go
测试,升级版本
升级类库方式:
- 使用
go get -u xxx
升级至该主版本号下最新版本; - 使用
go get xxx@version
升级至指定版本。
查看demo-go
下的go.mod
文件,确实升级到了新版本:
5、运行测试
大版本升级
主版本升级。
值得注意的是,使用
go get -u xxx
升级类库版本时,无法跨主版本升级,只能升级至当前主版本下最新小版本;v0.x.x 升级至v1.x.x是个例外,可以直接使用
go get -u xxx
命令升级。
当前版本是v1.0.1
,现对该类库进行了功能重构,发布v2.0.0
版本:
1、继续按照最佳实践,创建2.x
版本的分支
2、在类库根目录下创建v2
目录,并将当前项目go类库和go.mod
全拷贝(或剪切)到v2
目录
3、修改module名称至新版
查看v2/go.mod
文件:
修改了module名称后,如果v2目录下的go文件引用本类库的包,需要更新引用v2,否则无法找到引用包。
4、v2类库新增功能
5、提交代码并发布
6、使用demo-go
测试,升级版本
注意,此时无法通过go get -u xxx
升级至不同于当前主版本的最新版本,需要使用go get xxx@version
升级:
查看go.mod
文件,已经添加了v2
版本依赖包
7、修改测试代码
同时使用v1
版本和v2
版本的包函数:
8、运行测试
使用本地go类库
如果本地的go类库暂未维护到远端,如何引用本地类库的包呢?
在go.mod文件中使用replace引用本地go类库,这个方式有时候更方便于开发。
common-go的module名称为github.com/vsixz/common-go
replace使用go类库相对路径替换module的引用
以下示例将go类库的引用切换为本地引用。
由于是本地引用,版本号只需在主版本号的范围内即可。
结束语
主版本升级会给代码的维护和版本的维护增加难度,并且需要下游用户迁移版本。最好是当存在令人信服的原因时才对类库主版本进行升级,例如为了优化代码大规模重构。