在使用Golang过程中,有一个非常令人头大的问题: 缺少依赖库版本功能管理, 比如某些依赖在某个commit之后发生了API变更之后,如果不修改代码很难兼容,然而开发者之间很有可能因为参与的时间不同,导致执行go get命令获取的版本不同,而导致各种问题, 甚至是编译不通过。因此需要有一个包依赖的版本控制工具。
vendor之前
在vendor出来之前, 以godep为主比较流行, godep的原理非常简单:
godep把第三包的版本依赖信息记录在Godeps.json下,并且把第三包完整拷贝一份到vendor下面。通过对Godeps.json文件进行版本管理即可以管理整个项目的第三方包依赖信息。
可以看到godep只是把第三方包进行单独到依赖管理,而新增到第三包还是会被get到GOPATH中, 如果多个项目用同一个第三包的不同版本时, 那就完蛋了
vendor的历史
vendor机制就是用来解决第三方包依赖问题:
- golang 1.5引入, 默认是关闭的, 通过手动设置环境变量:GO15VENDOREXPERIMENT=1开启
- golang 1.6默认开启
- goalng 1.7 vendor作为功能支持,取消GO15VENDOREXPERIMENT环境变量
vendor的原理很简单: 将第三方依赖放入当前项目vendor目录中, 编译的时候从vendor目录中查找依赖而不从GOPATH/src中对应目录中查找。
新增的第三方包直接被get到根目录的vendor文件夹下,不会与其它的项目混用第三方包,完美避免多个项目同用同一个第三方包的不同版本问题。
因此只需要对vendor/vendor.json进行版本控制,即可对第三包依赖关系进行控制。
vendor的使用
安装govendor
go get -u -v github.com/kardianos/govendor创建一个golang的项目
比如我创建一个简单的依赖ssh服务的包
package main
import (
"bytes"
"fmt"
"log"
"golang.org/x/crypto/ssh"
)
func main() {
ce := func(err error, msg string) {
if err != nil {
log.Fatalf("%s error: %v", msg, err)
}
}
client, err := ssh.Dial("tcp", "localhost:1234", &ssh.ClientConfig{
User: "root",
Auth: []ssh.AuthMethod{ssh.Password("xxx")},
})
ce(err, "dial")
session, err := client.NewSession()
ce(err, "new session")
defer session.Close()
modes := ssh.TerminalModes{
ssh.ECHO: 1,
ssh.ECHOCTL: 0,
ssh.TTY_OP_ISPEED: 14400,
ssh.TTY_OP_OSPEED: 14400,
}
err = session.RequestPty("xterm-256color", 80, 40, modes)
ce(err, "request pty")
if err := session.Setenv("LC_USR_DIR", "/usr"); err != nil {
panic("Failed to run: " + err.Error())
}
var b bytes.Buffer
session.Stdout, session.Stderr = &b, &b
if err := session.Run("ls -l $LC_USR_DIR"); err != nil {
panic("Failed to run: " + err.Error())
}
fmt.Println(b.String())初始化vendor文件
➜ govendor_test govendor init
➜ govendor_test cat vendor/vendor.json
{
"comment": "",
"ignore": "test",
"package": [],
"rootPath": "govendor_test"
}
➜ govendor_test tree .
.
├── main.go
└── vendor
└── vendor.json
初始化完成后会生成一个vendor的文件夹, 因为我还没添加依赖, 所以vendor.json里面并没有相关依赖包的描述
添加依赖的第三方包
➜ govendor_test govendor add +external
➜ govendor_test cat vendor/vendor.json
{
"comment": "",
"ignore": "test",
"package": [
{
"checksumSHA1": "C1KKOxFoW7/W/NFNpiXK+boguNo=",
"path": "golang.org/x/crypto/curve25519",
"revision": "453249f01cfeb54c3d549ddb75ff152ca243f9d8",
"revisionTime": "2017-02-08T20:51:15Z"
},
{
"checksumSHA1": "wGb//LjBPNxYHqk+dcLo7BjPXK8=",
"path": "golang.org/x/crypto/ed25519",
"revision": "453249f01cfeb54c3d549ddb75ff152ca243f9d8",
"revisionTime": "2017-02-08T20:51:15Z"
},
{
"checksumSHA1": "LXFcVx8I587SnWmKycSDEq9yvK8=",
"path": "golang.org/x/crypto/ed25519/internal/edwards25519",
"revision": "453249f01cfeb54c3d549ddb75ff152ca243f9d8",
"revisionTime": "2017-02-08T20:51:15Z"
},
{
"checksumSHA1": "fsrFs762jlaILyqqQImS1GfvIvw=",
"path": "golang.org/x/crypto/ssh",
"revision": "453249f01cfeb54c3d549ddb75ff152ca243f9d8",
"revisionTime": "2017-02-08T20:51:15Z"
}
],
"rootPath": "govendor_test"
}
➜ govendor_test tree .
.
├── main.go
└── vendor
├── golang.org
│ └── x
│ └── crypto
│ ├── LICENSE
│ ├── PATENTS
│ ├── curve25519
│ │ ├── const_amd64.h
│ │ ├── const_amd64.s
│ │ ├── cswap_amd64.s
│ │ ├── curve25519.go
│ │ ├── doc.go
│ │ ├── freeze_amd64.s
│ │ ├── ladderstep_amd64.s
│ │ ├── mont25519_amd64.go
│ │ ├── mul_amd64.s
│ │ └── square_amd64.s
│ ├── ed25519
│ │ ├── ed25519.go
│ │ └── internal
│ │ └── edwards25519
│ │ ├── const.go
│ │ └── edwards25519.go
│ └── ssh
│ ├── buffer.go
│ ├── certs.go
│ ├── channel.go
│ ├── cipher.go
│ ├── client.go
│ ├── client_auth.go
│ ├── common.go
│ ├── connection.go
│ ├── doc.go
│ ├── handshake.go
│ ├── kex.go
│ ├── keys.go
│ ├── mac.go
│ ├── messages.go
│ ├── mux.go
│ ├── server.go
│ ├── session.go
│ ├── tcpip.go
│ └── transport.go
└── vendor.json
9 directories, 36 files我们发现vendor.json的package已经记录了第三方包的版本,并且把这些依赖的包都放到vendor目录下了
根据自己的需求,选择是否将vendor目录做版本控制
一般只需要将vendor.json做版本控制即可,但是对于那些需要翻墙才能下载的包也可以直接将vendor都纳入版本控制 添加.ignore.git仅对vendor.json做版本控制
git init
echo "vendor/golang.or" .gitignore
git add .
git commit -m "test commit"
git push -u origin master
...其他小伙伴安装依赖
其他小伙伴如果需要使用这个项目, 拉下该项目
git clone ssh://git@xxx/govendor_test.git
https_proxy=http://localhost:8123 govendor sync (我需要安装golang.org的包,因此需要FQ)这样就安装了该项目的指定版本的第三个依赖。
接下来愉快的玩耍吧!