依赖代码管理

这里记录用于Go项目的依赖管理工具。

Go Modules

Go 1.11和Go 1.12引入了Go Modules,在Go 1.13中,Go Module将是默认的依赖管理方法:Go 1.11和1.12引入的新的依赖代码管理方法:Go Modules

Dep

dep是一个比较新的依赖管理工具,它计划成为Go的标准工具(截止2017-12-26 20:50:43)。

安装

可以下面的方法安装:

go get -u github.com/golang/dep/cmd/dep

在Mac上还可以用brew安装:

brew install dep
brew upgrade dep

使用

在项目的根目录中执行dep init完成初始化设置。

$ dep init
$ tree
.
├── Gopkg.lock
├── Gopkg.toml
└── vendor

1 directory, 2 files

Gopkg.tomlGopkg.lock用来记录项目的依赖包,如果项目中没有代码,这两个文件是空的。

使用dep ensure -add添加依赖包(项目目录中需要有源码):

dep ensure -add  k8s.io/client-go

这时依赖包的依赖包还没有安装到项目本地,需要执行dep ensure进行安装。

代码中引用的其它未通过dep添加的依赖包,也需要执行dep ensure进行安装。

如果要更改依赖的版本,编辑Gopkg.toml之后,执行dep ensure

dep ensure将所有的依赖包信息更新到Gopkg.lock文件中,并将所有的依赖包下载到vendor目录中。

发布

可以只将Gopkg.tomlGopkg.lock包含到项目代码库中。之后在项目根目录中执行dep ensure下载依赖包:

dep ensure

Glide

glide是一个使用比较多的依赖管理工具。

安装

在linux上可以用下面的方法安装:

curl https://glide.sh/get | sh

Mac上还可以用brew安装:

brew install glide

使用

glide create初始化

在项目的根目录中执行glide create完成初始化设置。

$ glide create
[INFO]	Generating a YAML configuration file and guessing the dependencies
[INFO]	Attempting to import from other package managers (use --skip-import to skip)
[INFO]	Scanning code to look for dependencies
[INFO]	Writing configuration file (glide.yaml)
[INFO]	Would you like Glide to help you find ways to improve your glide.yaml configuration?
[INFO]	If you want to revisit this step you can use the config-wizard command at any time.
[INFO]	Yes (Y) or No (N)?
N
[INFO]	You can now edit the glide.yaml file. Consider:
[INFO]	--> Using versions and ranges. See https://glide.sh/docs/versions/
[INFO]	--> Adding additional metadata. See https://glide.sh/docs/glide.yaml/
[INFO]	--> Running the config-wizard command to improve the versions in your configuration

如果项目中已经有代码,会将已有代码的依赖写入到glide.yaml中。

之后直接使用glide up --quick将已有代码的依赖收集到vendor目录中。

glide get 添加依赖包

使用glide安装依赖包的时候,会自动提示版本。例如用glide的命令引入k8s.io/client-go时:

$ glide get k8s.io/client-go
[INFO]	Preparing to install 1 package.
[INFO]	Attempting to get package k8s.io/client-go
[INFO]	--> Gathering release information for k8s.io/client-go
[INFO]	The package k8s.io/client-go appears to have Semantic Version releases (http://semver.org).
[INFO]	The latest release is v6.0.0. You are currently not using a release. Would you like
[INFO]	to use this release? Yes (Y) or No (N)
Y
[INFO]	The package k8s.io/client-go appears to use semantic versions (http://semver.org).
[INFO]	Would you like to track the latest minor or patch releases (major.minor.patch)?
[INFO]	The choices are:
[INFO]	 - Tracking minor version releases would use '>= 6.0.0, < 7.0.0' ('^6.0.0')
[INFO]	 - Tracking patch version releases would use '>= 6.0.0, < 6.1.0' ('~6.0.0')
[INFO]	 - Skip using ranges
[INFO]	For more information on Glide versions and ranges see https://glide.sh/docs/versions
[INFO]	Minor (M), Patch (P), or Skip Ranges (S)?
P
[INFO]	--> Adding k8s.io/client-go to your configuration with the version ~6.0.0
[INFO]	Downloading dependencies. Please wait...
[INFO]	--> Fetching updates for k8s.io/client-go
[INFO]	Resolving imports
[INFO]	Downloading dependencies. Please wait...
[INFO]	--> Detected semantic version. Setting version for k8s.io/client-go to v6.0.0
[INFO]	Exporting resolved dependencies...
[INFO]	--> Exporting k8s.io/client-go
[INFO]	Replacing existing vendor dependencies

安装完成之后目录结构如下:

$ tree -L 2
.
├── glide.lock
├── glide.yaml
└── vendor
    └── k8s.io

glide get在glide.yaml和glide.lock中记录了依赖的client-go的版本。

这时,依赖包已经安装到了项目本地,但是依赖包的依赖包还需要通过执行glide up进行安装。

glide up 更新vendor目录

项目的源代码中直接引入的不是通过glide命令安装的包,也需要执行glide up安装。

glide up会在glide.lock中记录依赖包的依赖包和源码引用的依赖包,并将这些依赖包安装到项目本地。

⚠️ glide up安装的依赖包是直接从github或者其它网址下载的,既不会使用、也不会更改$GOPATH中的源码。

⚠️ 没有指定版本的依赖包,glide up每次执行的时候,都会去获取安装最新的代码。

⚠️ glide up默认递归搜集依赖,可以用glide up --quick

$ glide up
[INFO]	Downloading dependencies. Please wait...
[INFO]	--> Fetching updates for k8s.io/client-go
[INFO]	--> Detected semantic version. Setting version for k8s.io/client-go to v5.0.1
[INFO]	Resolving imports
[INFO]	--> Fetching github.com/lijiaocn/GoPkgs
[INFO]	Found Godeps.json file in /Users/lijiao/.glide/cache/src/https-k8s.io-apimachinery
[INFO]	--> Parsing Godeps metadata...
...
[INFO]	--> Exporting k8s.io/kube-openapi
[INFO]	--> Exporting k8s.io/api
[INFO]	Replacing existing vendor dependencies
[INFO]	Project relies on 31 dependencies.

$ head glide.lock
hash: 1002f0b1fae48b0c9e90737a6071f892d98b6bd9016d55f91cca24d25672e4cb
updated: 2017-12-27T10:44:30.038822286+08:00
imports:
- name: github.com/davecgh/go-spew
  version: 782f4967f2dc4564575ca782fe2d04090b5faca8
  subpackages:
  - spew
- name: github.com/emicklei/go-restful
  version: ff4f55a206334ef123e4f79bbf348980da81ca46
  subpackages:

如果要更改依赖包的版本,修改glide.yaml文件的version后,也执行glide up

$ cat glide.yaml
package: github.com/lijiaocn/handbook-go/codes/04-01-version/glide
import:
- package: k8s.io/client-go
  version: ~6.0.0
$ glide up -v

管理

查看依赖包:

glide list

删除依赖包:

glide rm

设置镜像站:

glide mirror

发布

发布的时候将glide.lockglide.yaml包含到项目源码库中。

获取到项目代码后,项目的根目录中执行glide install下载项目的依赖包。

$ glide install
[INFO]	Downloading dependencies. Please wait...
[INFO]	--> Found desired version locally github.com/davecgh/go-spew 782f4967f2dc4564575ca782fe2d04090b5faca8!
[INFO]	--> Found desired version locally github.com/emicklei/go-restful ff4f55a206334ef123e4f79bbf348980da81ca46!
[INFO]	--> Found desired version locally github.com/emicklei/go-restful-swagger12 dcef7f55730566d41eae5db10e7d6981829720f6!
...
[INFO]	--> Exporting gopkg.in/inf.v0
[INFO]	--> Exporting k8s.io/client-go
[INFO]	--> Exporting k8s.io/kube-openapi
[INFO]	Replacing existing vendor dependencies

Godep

Go语言早期没有提供依赖包管理的功能,godep是一个比较简单的第三方依赖管理工具。

安装

可以用下面的命令安装godep:

go get github.com/tools/godep

下载

godep不会自动拉取代码,使用前,先要确认中$GOPATH中的源码的版本是正确的。

例如,如果要使用k8s.io/client-go的v6.0.0版本的代码。

先下载源码:

go get k8s.io/client-go/...

然后将下载的client-go源码切换到v6.0.0:

cd $GOPATH/src/k8s.io/client-go
git checkout v6.0.0

因为client-go自身也使用godep进行了依赖管理,所以还需要在client-go中执行:

godep restore ./...

执行restore的目的下载client-go的依赖包。

这时可以直接在项目中引用目标版本的代码,但依赖包的文件还是到$GOPATH中读取。

如果另一个项目使用了同名依赖包的另一个版本,两者就会冲突。所以还需要将项目的依赖信息、依赖文件保存到项目本地。

保存

通过以下命令,将项目引用的第三方代码以及版本信息保存在本地:

godep save          #保存当前目录下的go文件(不遍历子目录)引用的第三方代码
godep save ./...    #保存当前目录以及子目录下的go文件引用的第三方代码

在Go1.5之前,godep将版本信息和第三方代码保存的项目的Godeps目录下。因此在go1.5之前,需要通过godep调用go命令,才会使用保存在项目本地的依赖包。例如:

godep go build

在Go1.5以后,godep将版本信息保存在godeps目录中,将依赖包保存在项目本地的vendor目录中,可以直接使用go命令。

发布

可以将项目本地的依赖包,即Godep目录和vendor目录提交到项目的代码库中。

也可以只将Godep目录提交到项目的代码库中。这样下载了项目代码后,需要用restore下载依赖包:

go restore

restore会命令将$GOPATH中的源码更新为Godeps/Godeps.json中指定的版本,因此最好还是使用第一种发布方式。

例如Godeps.json中的指定metadata的版本为3b1ae45394a234c385be014e9a488f2bb6eef821

{
    "ImportPath": "k8s.io/client-go",
    "GoVersion": "go1.9",
    "GodepVersion": "v79",
    "Packages": [
        "./..."
    ],
    "Deps": [
        {
            "ImportPath": "cloud.google.com/go/compute/metadata",
            "Rev": "3b1ae45394a234c385be014e9a488f2bb6eef821"
        },
    ...(省略)...

执行restore命令之后,$GOPATH/cloud.google.com/go/compute/metadata中的版本将为变成指定的版本:

$ cd $GOPATH/cloud.google.com/go/compute/metadata
$ git log |head
commit 3b1ae45394a234c385be014e9a488f2bb6eef821
Author: Sarah Adams <[email protected]>
Date:   Thu Sep 8 15:39:53 2016 -0700

    datastore: adds support for loading entity values

    Adds support for conditionally loading either flattened fields or
    entity values into nested structs on read.

    Issue #75

设置代理

godep是用go语言开发,go语言开发的程序都支持环境变量http_proxy

$ http_proxy=127.0.0.1:53100 godep restore ./...

godep在加载源码时,会使用git等版本管理工具,所以可能还需要给这些版本管理设置代理。

git可以在~/.gitconfig中设置:

[https]
proxy = 127.0.0.1:53100
sslverify = false
[http]
proxy = 127.0.0.1:53100

Vendor

vendor是1.5中的一个试验特性,在1.6版本中被正式引入。编译过程中,会先引用vendor目录中的代码。

对于同样的代码main.go:

package main

import (
    "github.com/lijiaocn/GoPkgs/version"
)

func main() {
    version.Show()
}

没有vendor之前,项目vendor_test目录结构如下:

▾ vendor_test/
  ▾ main/
      main.go

main.go中引用的是$GOPATH/.../version中的文件。

使用vendor之后,目录结构如下:

▾ vendor_test/
  ▸ Godeps/
  ▾ main/
      main.go
  ▾ vendor/
    ▾ github.com/
      ▾ lijiaocn/
        ▾ GoPkgs/
          ▸ version/
            LICENSE

main.go中引用的是本地的vendor/.../version中的文件。

不需要对main.go做任何修改。

参考

  1. godep
  2. glide
  3. dep