go-mod.md 13.0 KB
Newer Older
kingreatwill's avatar
kingreatwill 已提交
1
[TOC]
kingreatwill's avatar
kingreatwill 已提交
2 3
go mod 1.13

kingreatwill's avatar
go mod  
kingreatwill 已提交
4 5
https://github.com/golang/go/wiki/Modules
https://github.com/golang/proposal/blob/master/design/24301-versioned-go.md
kingreatwill's avatar
完善.  
kingreatwill 已提交
6
[goproxy.cn 现已推出首个 Go 模块代理统计数据 API](https://mp.weixin.qq.com/s/ZtQJE-Vv_Ke8SjhrzBoGcw)
kingreatwill's avatar
kingreatwill 已提交
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

## 核心文件:go.mod
```
module xx.com/x
go 1.12
require other/thing v1.0.2 // 这是注释
require new/thing/v2 v2.3.4 // indirect
require(
  new/thing v2.3.4
  old/thing v0.0.0-20190603091049-60506f45cf65

exclude old/thing v1.2.3
replace bad/thing v1.4.5 => good/thing v1.4.5
```
```
module
go
require
exclude
replace
```
indirect 注释标记了该模块不是被当前模块直接导入的,只是被间接导入。

replace my/example/pkg => ./pkg // 可以替换本地包

Go 命令行工具会自动处理 go.mod 中指定的模块版本。当源代码中 import 指向的模块不存在于 go.mod 文件中时,Go 命令行工具会自动搜索这个模块,并将最新版本(最后一个 tag 且非预发布的稳定版本)添加到 go.mod 文件中。

如果没有 tag,则使用伪版本(第 7 行),这是一种版本语法,专门用于标记没有 tag 的提交(一些 golang.org/x/ 下的包就是没有 tag 的)。如: v0.0.0-20190603091049-60506f45cf65 。

前面部分为语义化版本号,用于标记版本;中间部分为 UTC 的提交时间,用于比较两个伪版本以其确定先后顺序;后面部分是 commit 哈希的前缀,用于标记该版本位于哪个 commit。

kingreatwill's avatar
go mod  
kingreatwill 已提交
38
所有前导动词的作用如下:
kingreatwill's avatar
kingreatwill 已提交
39

kingreatwill's avatar
go mod  
kingreatwill 已提交
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
module:定义模块路径。
go:设置预期的语言版本。
require:要求给定版本或更高版本的特定模块。
exclude:排除特定版本模块的使用,不允许的模块版本被视为不可用,并且查询无法返回。
replace:使用不同的模块版本替换原有模块版本。

```
module github.com/example/project

require (
    github.com/SermoDigital/jose v0.0.0-20180104203859-803625baeddc
    github.com/google/uuid v1.1.0
)

exclude github.com/SermoDigital/jose v0.9.1

replace github.com/google/uuid v1.1.0 => git.coolaj86.com/coolaj86/uuid.go v1.1.1
```
exclude
In the case of the github.com/SermoDigital/jose package, it has a proper git tag for v0.9.1, but the current version is v1.1, which is NOT a proper git tag (missing the "patch" version).

By excluding the properly-versioned (but not working) code it causes go mod to fetch from master instead (which is not properly versioned, but has the working code).
kingreatwill's avatar
kingreatwill 已提交
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83

## 版本管理文件:go.sum
每行由模块导入路径、模块的特定版本和预期哈希组成。
在每次缺少模块时,如果缓存中不存在,则需要下载并计算其哈希添加到 go.sum 中;如果缓存中存在,则需要匹配 go.sum 中的已有条目。
这样,构建软件的用户就可以使用哈希验证其构建是否跟你的构建相同( go mod verify ),而无论他们怎样获取依赖项,都可以得到相同的版本。同时也保证了项目依赖不会发生预料之外的恶意修改和其他问题。这也是为什么要将 go.sum 文件加入版本管理(Git)的原因。



语义化版本号格式为: X.Y.Z (主版本号.次版本号.修订号),使用方法如下:
- 进行不向下兼容的修改时,递增主版本号。
- API 保持向下兼容的新增及修改时,递增次版本号。
- 修复问题但不影响 API 时,递增修订号。

## 常用命令
- go mod init :创建一个新模块,初始化 go.mod 文件,参数为该模块的导入路径,推荐使用这种形式。如: go mod init github.com/linehk/example 。
- go get :更改依赖项版本(或添加新的依赖项)。
- go build 、 go test 等命令:Go 命令行工具会根据需要添加新的依赖项。如: go test ./... ,测试当前模块。
- go list -m all :打印当前模块依赖。
- go mod tidy :移除无用依赖。
- go list -m -versions github.com/gin-gonic/gin :列出该模块的所有版本。
- go mod verify :验证哈希。

kingreatwill's avatar
up  
kingreatwill 已提交
84 85 86 87 88 89
We do this by using our good old friend go get:
- run go get -u to use the latest minor or patch releases (i.e. it would update from 1.0.0 to, say, 1.0.1 or, if available, 1.1.0)
- run go get -u=patch to use the latest patch releases (i.e., would update to 1.0.1 but not to 1.1.0)
- run go get package@version to update to a specific version (say, github.com/robteix/testmod@v1.0.1)

version 1.0.1. go get -u will not get version 2.0.0.
kingreatwill's avatar
docker  
kingreatwill 已提交
90 91 92 93 94 95
## go get
`go get -d -v ./...`
`-d`标志只下载代码包,不执行安装命令;
`-v`打印详细日志和调试日志。这里加上这个标志会把每个下载的包都打印出来;
`./...`这个表示路径,代表当前目录下所有的文件。
`-u` 强制使用网络去更新包和它的依赖包
kingreatwill's avatar
up  
kingreatwill 已提交
96

kingreatwill's avatar
kingreatwill 已提交
97 98 99 100 101
### go get 指定版本
```
go get github.com/kataras/iris/v12@master
go get github.com/kataras/iris/v12@v12.2.0-alpha2
```
kingreatwill's avatar
sql  
kingreatwill 已提交
102 103 104 105
### Go1.17 go get变化
go get 只用来下载普通的包,不做编译和安装(以前go get 有一个 flag -d,指示 go get 下载对应的包,但不做编译和安装。将来的版本,-d 会成为默认行为,这样会更快。此外,因为不编译,即使目标依赖在特定平台编译报错,go get 也能正常执行完。)

go install 安装可执行程序
kingreatwill's avatar
docker  
kingreatwill 已提交
106
`go install -v ./...`
kingreatwill's avatar
sql  
kingreatwill 已提交
107 108 109

废弃 -insecure;使用 GOINSECURE 环境变量

kingreatwill's avatar
kingreatwill 已提交
110 111 112 113
在模块外,不带 `@version` 是无法安装的!
如果你在模块目录中,并且你不带 `@version` 执行安装的话,只能安装 go.mod 中已经包含的版本。并且不能安装未出现在 go.mod 中的包。


kingreatwill's avatar
up.  
kingreatwill 已提交
114 115 116 117 118
## Workspace Mode(工作区模式)
https://mp.weixin.qq.com/s/S3I919YZb-bgaEnHgKq7fg

https://go.googlesource.com/proposal/+/master/design/45713-workspace.md

kingreatwill's avatar
up  
kingreatwill 已提交
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
## Module dependencies
![](../img/go/module_gopath.png)

修改mod文件
```
# require a new dependency
go mod edit -require one/thing@version

# drop a requirement
go mod edit -droprequire one/thing

# exclude a dependency
go mod edit -exclude bad/thing@version

# drop an exclusion
go mod edit -dropexclude bad/thing@version

# replace a dependency
go mod edit -replace src/thing@version=dst/thing@version

# drop a replacement
go mod edit -dropreplace src/thing@version
```


kingreatwill's avatar
kingreatwill 已提交
144 145 146 147 148 149 150
#### go env
```
set GONOPROXY=
set GONOSUMDB=
set GOPRIVATE=
set GOPROXY=https://goproxy.cn,direct
set GOSUMDB=off
kingreatwill's avatar
kingreatwill 已提交
151
# export GOPROXY=https://gocenter.io
kingreatwill's avatar
kingreatwill 已提交
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
```

设置GOPROXY代理:
```
go env -w GOPROXY=https://goproxy.cn,direct
```
设置GOPRIVATE 会同事设置GONOPROXY GONOSUMDB
设置GOPRIVATE来跳过私有库,比如常用的Gitlab或Gitee,中间使用逗号分隔:
```
go env -w GOPRIVATE=*.gitlab.com,*.gitee.com
```
关闭验证包的有效性
```
go env -w GOSUMDB=off
```
还原设置
```
go env -u GOSUMDB
```
忽略http安全(也不会走sumdb)
```
go get -insecure modxxx
```



所以在使用 Go 命令行工具或 go.mod 文件时,就可以使用语义化版本号来进行 模块查询 ,具体规则如下:
- 默认值( @latest ):将匹配最新的可用标签版本或源码库的最新未标签版本。
- 完全指定版本( @v1.2.3 ):将匹配该指定版本。
- 版本前缀( @v1 或 @v1.2 ):将匹配具有该前缀的最新可用标签版本。
- 版本比较( @<v1.2.3 或 @>=v1.5.6 ):将匹配最接近比较目标的可用标签版本。 < 则为小于该版本的最- 新版本, > 则为大于该版本的最旧版本。当使用类 Unix 系统时,需用引号将字符串包裹起来以防止大于小于号被解释为重定向。如: go get 'github.com/gin-gonic/gin@<v1.2.3' 。
- 指定某个 commit( @c856192 ):将匹配该 commit 时的版本。
- 指定某个分支( @master ):将匹配该分支版本。
![](../img/go/go-mod-1.png)

如上图所示,为了能让 Go Modules 的使用者能够从旧版本更方便地升级至新版本,Go 语言官方提出了两个重要的规则:
- 导入兼容性规则(import compatibility rule):如果旧包和新包具有相同的导入路径,则新包必须向后兼容旧包。
- 语义化导入版本规则(semantic import versioning rule):每个不同主版本(即不兼容的包 v1 或 v2)使用不同的导入路径,以主版本结尾,且每个主版本中最多一个。如:一个 rsc.io/quote、一个 rsc.io/quote/v2、一个 rsc.io/quote/v3。

```
module my-module/v2

require (
  some/pkg/v2 v2.0.0
  some/pkg/v2/mod1 v2.0.0
  my/pkg/v3 v3.0.1
)
```
格式总结为pkgpath/vN,其中N是大于1的主要版本号。在代码里导入时也需要附带上这个版本信息,如import "some/pkg/v2"。如此一来包的导入路径发生了变化,也不用担心名称相同的对象需要向后兼容的限制了,因为golang认为不同的导入路径意味着不同的包。

不过这里有几个例外可以不用参照这种写法:
1. 当使用gopkg.in格式时可以使用等价的require gopkg.in/some/pkg.v2 v2.0.0
2. 在版本信息后加上+incompatible就可以不需要指定/vN,例如:require some/pkg v2.0.0+incompatible
3. 使用go1.11时设置GO111MODULE=off将取消这种限制,当然go1.12里就不能这么干了
除此以外的情况如果直接使用v2+版本将会导致go mod报错。

v2+版本的包允许和其他不同大版本的包同时存在(前提是添加了/vN),它们将被当做不同的包来处理。

另外/vN并不会影响你的仓库,不需要创建一个v2对应的仓库,这只是go modules添加的一种附加信息而已。

当然如果你不想遵循这一规范或者需要兼容现有代码,那么指定+incompatible会是一个合理的选择。不过如其字面意思,go modules不推荐这种行为。

而与 Git 分支的集成如下:
![](../img/go/go-mod-git.png)

vendor 目录
以前使用 vendor 目录有两个目的:
- 可以使用依赖项的确切版本用来构建。
- 即使原始副本消失,也能保证这些依赖项是可用的。

而模块现在有了更好的机制来实现这两个目的:
- 通过在 go.mod 文件中指定依赖项的确切版本。
- 可用性则由缓存代理($GOPROXY)实现。

而且 vendor 目录也很难管理这些依赖项,久而久之就会陷入与 node_modules 黑洞一样的窘境。

node_modules 黑洞
![](../img/go/go-mod-2.png)

所以,默认情况下使用 Go Modules 将完全忽略 vendor 的依赖项,但是为了平稳过度,可以使用 go mod vendor 命令可以创建 vendor 目录。

并在 Go 命令行工具使用 -mod=vendor 参数,如:go test -mod=vendor ./...;或设置环境变量 GOFLAGS 为 -mod=vendor,这样会假定 vendor 目录包含正确的依赖项副本,并忽略 go.mod 文件中的依赖项描述来构建。


## go-mod服务
proxy.golang.org-符合上提供的规范的模块镜像go help goproxy。

sum.golang.org-一个可审核的校验和数据库,go命令将使用该数据库对模块进行身份验证。有关更多详细信息,请查看“ 确保公共安全模块生态系统提案 ”。

index.golang.org-一个索引,用于提供新模块版本的供稿, proxy.golang.org可以使用这些新版本。可以在 https://index.golang.org/index 上查看提要。该提要以新行分隔的JSON形式提供,提供了模块路径(作为Path),模块版本(作为Version)以及proxy.golang.org首次缓存它的时间(作为Timestamp)。该列表按时间顺序排序。有两个可选参数:

- 'since':返回列表中模块版本的最早允许时间戳(RFC3339格式)。默认为时间的开始,例如 https://index.golang.org/index?since=2019-04-10T19:08:52.997264Z
- 'limit':返回列表的最大长度。默认值= 2000,最大值= 2000,例如 https://index.golang.org/index?limit=10

246

kingreatwill's avatar
sql  
kingreatwill 已提交
247
## go mod
248 249 250 251 252 253
go mod why
go mod why github.com/coreos/etcd
go mod graph 依赖输出

go mod graph|grep github.com/coreos/etcd

kingreatwill's avatar
kingreatwill 已提交
254 255 256 257
实际上,调试 go mod 问题最好的工具是 go get,加上 -x 选项后,更是屡试不爽。
go get-x github.com/coreos/etcd

> 出现:remote origin already exists. 解决方案:git config --global --unset remote.origin.push
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280

### 冲突
https://github.com/cch123/gomod-conflict-detect

编译成gomod-conflict-detect文件
or
go install github.com/cch123/gomod-conflict-detect

执行
go mod graph | gomod-conflict-detect


### go mod graph 可视化
安装 graphviz
choco install graphviz.portable

pcman安装 pacman -S mingw-w64-x86_64-graphviz

go install github.com/poloxue/modv

go mod graph | modv | dot -T svg -o graph.svg; start graph.svg

TIP: 当包多了不是人看的
kingreatwill's avatar
kingreatwill 已提交
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296


### go-mod-upgrade 交互式更新依赖
https://github.com/oligot/go-mod-upgrade

[Go Wiki 在 如何升级和降级依赖关系](https://github.com/golang/go/wiki/Modules#how-to-upgrade-and-downgrade-dependencies) 中介绍了一个命令:
```
go list -u -f '{{if (and (not (or .Main .Indirect)) .Update)}}{{.Path}}: {{.Version}} -> {{.Update.Version}}{{end}}' -m all 2> /dev/null
```
它查看直接依赖项的可用升级。然而,过程不可控,即我们不能通过它方便的更新某些依赖项。

此工具旨在通过交互的方式,使更新多个依赖项变得更加容易。这类似于 yarn upgrade-interactive ,但适用于 Go。

安装go-mod-upgrade
![](img/go-mod-upgrade.jpg)

kingreatwill's avatar
sql  
kingreatwill 已提交
297