Go Modules
Go 在 1.11 推出了 Go Modules,这是一个新的包管理器,解决了 GOPATH 存在的问题。并且 Go 1.13 起不再推荐使用 GOPATH。
Go Modules 机制
Go Modules 将依赖缓存放在 $GOPATH/pkg/mod 目录,并且同一个依赖的版本,只会缓存一份,供所有项目使用。
启用 Go Modules
Go 1.11 引入了环境变量 GO111MODULE 来控制是否启用 Go Modules,GO111MODULE 有三个值可选:
on启用 Go Modulesoff禁用 Go Modulesauto,在GOPATH下的项目,使用GOPATH,否则启用 Go Modules。
Go 1.16 之前 GO111MODULE 的默认值是 auto,Go 1.16 起 GO111MODULE 的默认值为 on。
初始化
初始化 Go Modules 项目,首先要开启 Go Modules,然后在项目目录下运行:
$ go mod init <project-path>下载依赖
下载依赖使用 go get 命令,命令格式为 go get <package[@version]>。
go get golang.org/x/test@latest,@latest表示选择最新的稳定版本,例如v1.2.3。如果没有稳定版本,选择最新的预发布版本,例如v1.2.3-alpha.1。 如果依赖没有 tag,那么选择最新的 commit。go get golang.org/x/test同上。go get golang.org/x/test@v1.2.3下载 tag 为v1.2.3的版本。go get golang.org/x/test@v0下载 tag 前缀为v0的版本。go get golang.org/x/test@master下载 master 分支上最新的 commit。go get golang.org/x/test@37s237s下载哈希值为37s237s的 commit,如果该 commit 存在对应的 tag,转换为 tag 并下载。
go get -u 更新现有的依赖。
Go Modules 代理
国内是无法访问 golang.org 的,Go 1.13 引入了环境变量 GOPROXY,可以用来设置 Go Modules 的代理。
GOPROXY 的默认值为 https://proxy.golang.org,direct,GOPROXY 可以设置多个,用 , 分隔。
执行 go get/install 时会优先从代理服务器下载依赖。如果从一个代理服务器下载失败,当遇见 direct 时,表示回源到依赖的源地址去下载。
设置 GOPROXY
使用 go env -w GOPROXY=https://goproxy.cn,direct 命令来设置 GOPROXY 的值。
GOPRIVATE
如果项目有一个私有依赖,设置 GOPROXY 也无法访问,可以使用 GOPRIVATE。
比如 GOPRIVATE=corp.example.com,github.com/pookt/demo 表示前缀可以匹配 corp.example.com 或者 github.com/pookt/demo 的依赖都会被认为是私有依赖。
GOPRIVATE 支持通配符,例如 *.example.com。
GOPRIVATE 较为特殊,它的值将作为 GONOPROXY 和 GONOSUMDB 的默认值。所以只使用 GOPRIVATE 就足够。
配置 Git
由于 Go 会通过 git clone 下载私有模块,必须确保 Git 有权限访问私有仓库。
- 用户名 + 密码(不推荐):
git config --global url."https://user:token@git.example.com".insteadOf "https://git.example.com"token 可以是 GitLab/GitHub 的 Personal Access Token (PAT)。
- netrc 文件:
machine git.example.com
login user
password token- SSH 认证:
生成 SSH Key(如果还没有):
ssh-keygen -t ed25519 -C "your-email@example.com"配置 Git 使用 SSH:
git config --global url."git@git.example.com:".insteadOf "https://git.example.com"测试 SSH 连接:
ssh -T git@git.example.comgo.mod 文件
go.mod 是 Go Modules 项目所必须的最重要的文件,描述了当前项目的元信息,目前有 5 个关键字:
module:定义当前项目的模块路径。go:预期的 Go 版本。require:指定项目的依赖版本,格式为<依赖的路径> <版本> [// indirect]。exclude:排除一个特定的依赖版本。replace:将一个依赖版本替换为另外一个依赖版本,格式为module => newmodule。
module example.com/foobar
go 1.13
require (
example.com/apple v0.1.2
example.com/pear v1.2.3
example.com/watermelon v3.3.10+incompatible
example.com/banana/v2 v2.3.4 // indirect
example.com/pineapple v0.0.0-20190924185754-1b0db40df49a
)
exclude example.com/banana v1.2.4
replace example.com/apple v0.1.2 => example.com/rda v0.1.0
replace example.com/banana => example.com/hugebananareplace
replace 是用来将一个依赖版本替换为另外一个依赖版本,格式为 module => newmodule。
newmodule可以是本地相对路径,例如github.com/gin-gonic/gin => ./gin。newmodule也可以是本地绝对路径,例如github.com/gin-gonic/gin => /home/root/gin。newmodule可以是网络路径,例如golang.org/x/text v0.3.2 => github.com/golang/text v0.3.2。
依赖的导入路径说明
上面示例中 example.com/banana/v2 v2.3.4,example.com/banana/v2 的导入路径有 /v2 为什么其他依赖的导入路径没有 /v0 或者 /v1。
因为 Go modules 在主版本号为 v0 和 v1 的情况下省略了版本号,不需要在模块导入路径包含主版本的信息。而在主版本号为 v2 及以上则需要在导入路径末尾加上主版本号。
v0.0.0-xxx 是什么版本
Go 拉去的依赖如果没有 tag,那么选择最新的 commit。例如上面示例中的 example.com/pineapple v0.0.0-20190924185754-1b0db40df49a。
v0.0.0 是因为 example.com/pineapple 这个依赖不存在 tag,20190924185754 最新一次 commit 的 commit 时间,1b0db40df49a 是 commit 的哈希值。
indirect
上面示例中的 example.com/banana/v2 v2.3.4 // indirect。indirect 表示该依赖为间接依赖。
通常上 go.mod 中出现的都应该是直接依赖,但是下面的两种情况会在 go.mod 中添加间接依赖:
- 当前项目的某个直接依赖没有使用 Go Modules。
- 当前项目的某个直接依赖的
go.mod文件中缺失某个依赖,那么这个缺失的依赖会被添加在当前项目的go.mod文件中,作为间接依赖。
incompatible
上面示例中的 example.com/watermelon v3.3.10+incompatible。incompatible 表示该依赖的路径跟版本不符合规范,v3.3.10 版本按照规范,引用路径应该为 example.com/watermelon/v3。
所以 Go 会在版本后加上 +incompatible。
go.sum 文件
go.sum 列出了当前项目所有直接或间接依赖的版本,记录每个依赖的哈希值,目的是为了保证项目所依赖的版本不会被篡改。
go mod 命令
go mod 常用的几个子命令:
init:初始化go.mod文件tidy:自动添加项目依赖,并移除无用的依赖download:下载依赖到本地缓存。graph:查看现有的依赖结构why:查看为什么需要一个依赖
迁移回 vendor 模式
go mod vendor 可以将 Go Modules 迁移回到模式。
这个命令并只是单纯地把 go.sum 中的所有依赖下载到 vendor 目录里。
再使用 go build -mod=vendor 来构建项目,因为在 Go Modules 模式下 go build 是屏蔽 vendor 机制的。
注意发布时需要带上 vendor 目录。
其他
设置 HTTP Proxy 却仍然无法下载依赖
通常如果设置了 HTTP Proxy,go get/install 会使用指定的代理去下载依赖,例如:
# windows
set http_proxy=http://[user]:[pass]@[proxy_ip]:[proxy_port]/
set https_proxy=http://[user]:[pass]@[proxy_ip]:[proxy_port]/
# linux
export http_proxy=http://[user]:[pass]@[proxy_ip]:[proxy_port]/
export https_proxy=http://[user]:[pass]@[proxy_ip]:[proxy_port]/但是,如果拉取的依赖是使用 Git 作为源控制管理器,那么还需要配置 Git 的 Proxy,否则还是无法下载依赖:
git config --global http.proxy http://[user]:[pass]@[proxy_ip]:[proxy_port]/
git config --global https.proxy http://[user]:[pass]@[proxy_ip]:[proxy_port]/清理缓存
go clean -modcache 可以用来清理所有缓存的依赖。