目录
1. go vendor
1.1. go mod vendor 如何使用
go mod vendor
的功能是将新增的依赖包自动写入当前项目的 vendor 目录。
-
首先写一个 test,在 test 中 “使用新增依赖包的” 代码
-
go mod tidy
-
go mod vendor
1.2. vendor 里面的 package 要更新
go get -u gitlab.xxx.com/project_name@dev
go mod vendor
go mod tidy
example: go get -u github.com/GuanceCloud/dockertest/v3@v3.9.3
1.3. Update All Go Modules
Wanting to update all dependencies in a way that it doesn’t break is somewhat more complicated than expected.
I used to only do go get -u all
which unfortunately broke, or forgot some dependencies, under certain conditions. After some GitHub issue spelunking, I’ve ended up with the following that works reliably:
$ go get -d -u -t ./...
Specifically the -t flag ensures that test dependencies are updated too.
To ensure that my go.mod and go.sum files contain all necessary checksums and removes the ones I don’t need anymore, I always run the following right after:
$ go mod tidy
Relevant docs:
1.4. replace modules
go.mod
:
require (
github.com/pyroscope-io/pyroscope v0.36.0
)
replace (
github.com/pyroscope-io/pyroscope v0.36.0 => github.com/GuanceCloud/pyroscope v0.36.1
)
1.5. 包版本降级
直接改版本号不行,tidy 后又会改回原来的新版本号。必须使用 replace。
go.mod
:
// replace
replace (
google.golang.org/grpc => google.golang.org/grpc v1.51.0
)
1.6. go mod 全部操作命令
# go mod 初始化
go mod init 模块名
# go mod 下载到本地Cache
go mod download
# go mod 清理本地Cache
go clean -modcache
# go mod 编辑go.mod文件:更多go mod查看 `go help mod edit`
go mod edit
# go mod 打印依赖图
go mod graph
# go mod 删除错误或者不使用的modules
go mod tidy
# go mod 生成vendor目录
go mod vendor
# go mod 验证依赖是否正确
go mod verify
# go mod 查找依赖
go mod why
# go mod 更新依赖到最新版本
go get -u github.com/golang/protobuf
# go mod 更新到指定版本
go get -u github.com/golang/protobuf@指定版本
# go mod 查看有哪些版本
go list -m -versions github.com/golang/protobuf
# go mod 替换包源
go mod edit -replace=golang.org/x/crypto@v0.0.0=github.com/golang/crypto@latest
go mod edit -replace=golang.org/x/sys@v0.0.0=github.com/golang/sys@latest
2. golang 使用 vendor 目录来管理依赖包
2.1. Vendor 目录介绍
随着 Go 1.5 release 版本的发布, vendor
目录被添加到除了 GOPATH
和 GOROOT
之外的依赖目录查找的解决方案。在 Go 1.6 之前, 你需要手动的设置环境变量 GO15VENDOREXPERIMENT=1
才可以使 Go
找到 Vendor
目录, 然而在 Go 1.6 之后, 这个功能已经不需要配置环境变量就可以实现了。
Note, 即使使用 vendor
, 也必须在 GOPATH
中, 在 go 的工具链中, 你逃不掉 GOPATH
的
那么查找依赖包路径的解决方案 (顺序) 如下:
- 当前包下的
vendor
目录。 - 向上级目录查找, 直到找到
src
下的vendor
目录。 - 在
GOPATH
下面查找依赖包。 - 在
GOROOT
目录下查找
2.2. 一些建议
在使用 vendor
中, 给出如下建议:
- 一个库工程 (不包含
main
的package
) 不应该在自己的版本控制中存储外部的包在vendor
目录中, 除非他们有特殊原因并且知道为什么要这么做。 - 在一个应用中, (包含
main
的package
), 建议只有一个vendor
目录在代码库一级目录。
上面建议的原因如下:
- 在目录结构中的每个包的实例, 即使是同一个包的同一个版本, 都会打到最终的二进制文件中, 如果每个人都单独的存储自己的依赖包, 会迅速导致生成文件的二进制爆发(binary bloat)
- 在一个目录的某个
pacage
类型, 并不兼容在同一个package
但是在不同目录的类型, 即便是同一个版本的package
, 那意味着loggers
, 数据库连接, 和其他共享的实例都没法工作。
2.3. 举个例子
工程目录如下:
- $GOPATH/src/github.com/mattfarina/golang-broken-vendor
- foo.go
- vendor/
- a/
- b/
- vendor/a/
在这个例子中, 两个 a
package 都是完全一样的, b package 在代码库中保存了 a package, 在顶级应用代码中也引用了 a
包。
文件 foo.go
做了很简单的事情:
func main() {
var it a.A
it = "foo"
b.Do(it)
}
那么问题来了, 当我们 build
的时候, 发现出问题了, 返回了下面的错误:
$ GO15VENDOREXPERIMENT=1 go build
./foo.go:12: cannot use it (type "github.com/mattfarina/golang-broken-vendor/vendor/a".A) as type "github.com/mattfarina/golang-broken-vendor/vendor/b/vendor/a".A in argument to b.Do
你可以 clone 这个测试工程到本地重现。
2.4. 为什么用 vendor 目录
如果我们已经使用 GOPATH
去存储 packages 了, 问什么还需要使用 vendor
目录呢? 这是一个很实战的问题。
假如多个应用使用一个依赖包的不同版本? 这个问题不只是 Go 应用, 其他语言也会有这个问题。
vendor 目录允许不同的代码库拥有它自己的依赖包, 并且不同于其他代码库的版本, 这就很好的做到了工程的隔离。
2.5. 推荐
2.5.1. Glide
我们发现 Glide 是非常好的包管理解决方案, 他将依赖包平展开存放在顶级 vendor
目录中, 如果一个包被另一个程序引用了, 那么这个包最好不要存储外部依赖项。如果使用 Glide, 你可以在 glide.yml
文件中指定依赖包, Glide 会帮你管理, 并使用正确的版本。
2.6. golang vendor 使用
使用 golang 编译程序时总遇到找不到包之类的情况, 尤其制作 docker 镜像时候如果全部在 Dockerfile
里面 go get
会很卡, 还必须上网下载依赖包, 使用 golang 的包以来管理可以很轻松的解决这些问题, build
镜像时候把 vendor
拷贝进去, 制作 docker image 也是十分的快。下面是步骤
- 先下载
vendor
包
go get -u github.com/kardianos/govendor
cd
到这个目录编译出二进制
go build
生成 govendor
文件
-
把这个可执行文件拷贝到
bin
目录, 或者添加到gopath
(或者知道这个目录地址直接用也行) -
cd
到自己的工程目录 -
执行
./govendor init
生成vendor
目录 -
执行
./vendor add +external
导入依赖包
此时你的工程目录已经导入依赖的编译包, 此时执行 go build
使用的包全在 vendor
下