[TOC] go mod 1.13 https://github.com/golang/go/wiki/Modules https://github.com/golang/proposal/blob/master/design/24301-versioned-go.md [goproxy.cn 现已推出首个 Go 模块代理统计数据 API](https://mp.weixin.qq.com/s/ZtQJE-Vv_Ke8SjhrzBoGcw) ## 核心文件: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。 所有前导动词的作用如下: 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). ## 版本管理文件: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 :验证哈希。 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. ## go get `go get -d -v ./...` `-d`标志只下载代码包,不执行安装命令; `-v`打印详细日志和调试日志。这里加上这个标志会把每个下载的包都打印出来; `./...`这个表示路径,代表当前目录下所有的文件。 `-u` 强制使用网络去更新包和它的依赖包 ### go get 指定版本 ``` go get github.com/kataras/iris/v12@master go get github.com/kataras/iris/v12@v12.2.0-alpha2 ``` ### Go1.17 go get变化 go get 只用来下载普通的包,不做编译和安装(以前go get 有一个 flag -d,指示 go get 下载对应的包,但不做编译和安装。将来的版本,-d 会成为默认行为,这样会更快。此外,因为不编译,即使目标依赖在特定平台编译报错,go get 也能正常执行完。) go install 安装可执行程序 `go install -v ./...` 废弃 -insecure;使用 GOINSECURE 环境变量 在模块外,不带 `@version` 是无法安装的! 如果你在模块目录中,并且你不带 `@version` 执行安装的话,只能安装 go.mod 中已经包含的版本。并且不能安装未出现在 go.mod 中的包。 ## Workspace Mode(工作区模式) https://mp.weixin.qq.com/s/S3I919YZb-bgaEnHgKq7fg https://go.googlesource.com/proposal/+/master/design/45713-workspace.md ## 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 ``` #### go env ``` set GONOPROXY= set GONOSUMDB= set GOPRIVATE= set GOPROXY=https://goproxy.cn,direct set GOSUMDB=off # export GOPROXY=https://gocenter.io ``` 设置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.5.6 ):将匹配最接近比较目标的可用标签版本。 < 则为小于该版本的最- 新版本, > 则为大于该版本的最旧版本。当使用类 Unix 系统时,需用引号将字符串包裹起来以防止大于小于号被解释为重定向。如: go get 'github.com/gin-gonic/gin@ 出现:remote origin already exists. 解决方案:git config --global --unset remote.origin.push ### 冲突 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: 当包多了不是人看的 ### 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)