使用 vendor 管理 Golang 项目依赖

我们在项目中除了 大量的使用 Python 外,也大量的使用了 Golang 构建高效基础运行服务。在使用 Golang 过程中,我们发现 Golang 程序缺少依赖库版本功能是一个非常令人头大的问题:某些依赖在某个 commit 之后发生了 API 变更之后,如果不修改代码很难兼容,然而开发者之间很有可能因为参与的时间不同,导致执行

get``` 命令获取的版本不同,而导致在不同电脑上出现编译不通过问题。同时,在多个程序中,如果使用的 commit 版本不同,也可能会导致程序编译过程中出现不同的问题。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

在之前,我们解决这个问题有两个方案,一种是拆解 ```go get``` 命令的执行,首先创建对应依赖目录,利用 git 命令切换至指定的 commit,然后执行 ```go install``` 命令。另外一种比较省事的方法是使用 [godep](https://github.com/tools/godep) 工具,这里就不做过多介绍了,具体可以参考文档或者搜索中文教程。

在 Golang1.5 之后,Go 提供了 ```GO15VENDOREXPERIMENT``` 环境变量,用于将 go build 时的应用路径搜索调整成为 ``` 当前项目目录 / vendor``` 目录方式。通过这种形式,我们可以实现类似于 ```godep``` 方式的项目依赖管理。不过起码在程序编译过程中,再也无需在其他端部署一个 ```godep``` 工具。

在使用之前,需要安装一个辅助工具(如果 Golang 自改一个就好了):```go get -u -v github.com/kardianos/govendor```

下面,我们用一个例子来说明。首先有一个名为 ```vendorproj``` 的项目。假如只有一个文件:

```go
package main

import ("github.com/yeeuu/echoic")

func main() {e := echoic.New()
e.SetDebug(true)
e.Run("127.0.0.1:4321")
}

执行一下命令就可以生成 vendor 文件夹:

1
2
3
4
5
6
$ govendor init
$ ls
main.go vendor
$ cd vendor/
$ ls
vendor.json

这个

会类似 ```godep``` 工具中的描述文件版本的功能。接下来,需要执行命令将当前应用必须的文件包含进来
1
2


$ govendor add +external

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

如果需要更新或移除,可以参考一下工具的具体文档使用 ```update``` 或者 ```remove``` 命令即可。这样会在 vendor 目录下将必须的编译文件移入进来(注意:测试所需依赖并不包含,依赖项目的测试文件也不会包含)。

```bash
$ ls
github.com golang.org vendor.json
$ cat vendor.json
{"comment": "","ignore":"test","package": [{"path":"github.com/yeeuu/echoic","revision":"a7d6994f92e2dc60cff071ae38b204fbd4bd2a3f","revisionTime":"2015-12-18T11:14:29+08:00"},
{"path": "golang.org/x/net/context",
"revision": "1d9fd3b8333e891c0e7353e1adcfe8a612573033",
"revisionTime": "2015-11-13T15:40:13-08:00"
}
]
}
$ cd github.com/yeeuu/echoic
$ ls
LICENSE context.go group.go router.go
README.md echoic.go response.go

通过设置环境变量 GO15VENDOREXPERIMENT=1 使用 vendor 文件夹构建文件。可以选择 export GO15VENDOREXPERIMENT=1 或者干脆 GO15VENDOREXPERIMENT=1 go build 执行编译。

通过这种方式就可以保证程序能够实现类似 Python 中 Virtualenv 的模式,实现不同程序使用不同版本依赖的目的。