這篇文章來自知識星球球友的問題:
關於 Go 語言的 mod 引用問題,比如一個主項目,裡面引用了其他人寫的 lib1,lib2,lib3 等等,lib1 中又被lib2,lib3 引用,也可能互相引用,這樣,當我更新 lib1 後,影響的 lib 就會很多,有沒有辦法在主項目中直接 go mod tidy 將所有 lib 都升級到最新版
問題描述可能不嚴謹,但大概意思是希望能夠將項目的所有依賴都升級到最新版本(兼容的)。這裡我引申一下,有如下 3 個問題:
這裡不想直接給出答案,而是想和大家一起探討下遇到類似這樣的問題怎麼找到答案,以及藉此對相關知識點有一個更全面、深入的了解。
怎麼尋找答案對於 Go 來說,遇到問題(特別基礎的問題除外),我認為應該優先去 golang-nuts 郵件組搜索。比如這個問題通過關鍵詞 go mod update direct dependencies` 搜索。(此外,還可以嘗試在 go 倉庫的 issue 中搜索)
第 1、2 個就是相關的。查看這兩個帖子,總結解決該問題大概的方法如下:
1)查看有更新的直接依賴項的方法
go list -u -f '{{if (and (not (or .Main .Indirect)) .Update)}}{{.Path}}{{end}}' -m all該方法還有變種,比如查看更新的版本信息:
go list -u -f '{{if (and (not (or .Main .Indirect)) .Update)}}{{.Path}}: {{.Version}} -> {{.Update.Version}}{{end}}' -m all你可以找一個倉庫試試,比如 https://github.com/studygolang/studygolang 項目,結果如下:
code.gitea.io/sdk/gitea: v0.0.0-20191106151626-e4082d89cc3b -> v0.12.0
github.com/PuerkitoBio/goquery: v1.5.0 -> v1.5.1
github.com/go-sql-driver/mysql: v1.4.1 -> v1.5.0
github.com/go-validator/validator: v0.0.0-20180514200540-135c24b11c19 -> v0.0.0-20200605151824-2b28d334fa05
github.com/jaytaylor/html2text: v0.0.0-20190408195923-01ec452cbe43 -> v0.0.0-20200412013138-3577fbdbcff7
github.com/labstack/echo/v4: v4.1.8 -> v4.1.16
github.com/polaris1119/config: v0.0.0-20160609095218-06a751e884f3 -> v0.0.0-20160628025248-e4f8b7e9e2ef
github.com/sundy-li/html2article: v0.0.0-20170724020440-d0b6c083441f -> v0.0.0-20180131134645-09ac198090c2
github.com/tidwall/gjson: v1.3.2 -> v1.6.0
golang.org/x/net: v0.0.0-20190607181551-461777fb6f67 -> v0.0.0-20200625001655-4c5254603344
golang.org/x/oauth2: v0.0.0-20190226205417-e64efc72b421 -> v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/text: v0.3.2 -> v0.3.3
xorm.io/core: v0.7.2 -> v0.7.3
xorm.io/xorm: v0.8.0 -> v1.0.22)更新它們
已經找出了哪些需要更新,具體更新是通過 go get 命令,這也有兩種方式:
go list -u -f '{{if (and (not (or .Main .Indirect)) .Update)}}{{.Path}}{{end}}' -m all | xargs go get -ugo get -u $(go list -u -f '{{if (and (not (or .Main .Indirect)) .Update)}}{{.Path}}{{end}}' -m all)回到我們開頭的問題,根據 .Indirect 來進行分別處理即可。不過,對於更新項目所有的依賴,有一個更簡便的方法,那就是直接 go get -u ./…。
當然,其實大部分問題,官方文檔都會有相關的說明,只是可能你對文檔不熟悉。
知識點學習以上涉及到的知識點主要是 go list 命令的使用。Go 命令的學習,最權威的文檔自然是官方文檔。
$ go help list以上命令可以查看 go list 的官方文檔,命令使用方式:
go list [-f format] [-json] [-m] [list flags] [build flags] [packages]該命令對於 GOPATH 和 Module 有些不同,這裡忽略 GOPATH,只關注 Module 相關的內容,上面使用的幾個命令行選項說明下:
選項說明-m針對使用了 Module 的項目,Module 項目必須的選項-u加上可用升級的信息-f進行格式化輸出,使用 text/template 語法-json通過 json 格式輸出最後的 packages,可以指定多個 package,還有兩個特殊的:
all:表示當前項目所有的活躍(當前項目使用的)模塊…:匹配特定模式的模塊,比如 github.com/… 表示匹配所有 github.com 開頭的模塊註:Go 命令中,all、... 和 std 一般有特殊用途
針對我們的問題,重點在於 -f ,因此需要了解一個結構:Module
type Module struct {
Path string // module path
Version string // module version
Versions []string // available module versions (with -versions)
Replace *Module // replaced by this module
Time *time.Time // time version was created
Update *Module // available update, if any (with -u)
Main bool // is this the main module?
Indirect bool // is this module only an indirect dependency of main module?
Dir string // directory holding files for this module, if any
GoMod string // path to go.mod file used when loading this module, if any
GoVersion string // go version used in module
Error *ModuleError // error loading module
}通過命令 go list -m -json all 可以通過 json 格式查看依賴信息,類似這樣:
...
{
"Path": "xorm.io/core",
"Version": "v0.7.2",
"Time": "2019-09-28T05:59:35Z",
"Dir": "/Users/xuxinhua/go/pkg/mod/xorm.io/core@v0.7.2",
"GoMod": "/Users/xuxinhua/go/pkg/mod/cache/download/xorm.io/core/@v/v0.7.2.mod"
}
{
"Path": "xorm.io/xorm",
"Version": "v0.8.0",
"Time": "2019-10-16T06:55:10Z",
"Dir": "/Users/xuxinhua/go/pkg/mod/xorm.io/xorm@v0.8.0",
"GoMod": "/Users/xuxinhua/go/pkg/mod/cache/download/xorm.io/xorm/@v/v0.8.0.mod",
"GoVersion": "1.11"
}
...因此如果需要更新所有直接依賴的版本,需要先找出所有的直接依賴,根據以上結構加上 text/template 模板的語法,可以寫出如下命令:
go list -m -f '{{if not (or .Main .Indirect)}}{{.Path}}{{end}}' all但結果包含了沒有更新的依賴。有沒有可用更新可以通過 Module 結構的 Update 欄位判斷,但需要加上 -u 選項:
go list -m -u -f '{{if and (not (or .Main .Indirect)) .Update}}{{.Path}}{{end}}' all找到了需要更新的依賴,然後就是更新了。這就需要使用到 go get 命令。關於這個命令,我留幾個問題希望你能找到答案。以 github.com/tidwall/gjson 包為例,比如 studygolang 項目目前依賴的版本是 v1.3.2,而 v1.3.x 最新版本是 v1.3.6,v1.x.x 最新版本是 v1.6.0。
1)如何更新到 v1.3.6?
2)go get -u github.com/tidwall/gjson@none 是什麼意思?
3)在 module 項目中,go get -u 和 go get -u ./... 有什麼區別?
補充通過這個問題和尋找答案的過程,還有其他收穫:
1)有人建議 go get 可以將直接依賴和間接依賴分開更新。見 issue 28424。
2)有一個庫用於更新依賴,它通過交互的方式進行,該庫叫 go-mod-upgrade。
簡單介紹下這個庫。
交互式更新過期依賴請注意,目前只支持補丁(patch)和次要版本更新(minor updates)。
為什麼開發此庫?Go Wiki 在 如何升級和降級依賴關係 文檔中,介紹了一個命令:
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 get -u github.com/oligot/go-mod-upgrade
使用在使用模塊的 Go 項目中,你現在可以運行:
$ go-mod-upgrade這樣就會出現類似上圖的界面。其中顏色有助於標識更新類型:
交互界面中,通過空格鍵選中某個包,上下箭頭移動待選擇包,還支持直接輸入進行包的過濾,比如下圖的 gjson 就是輸入的。選中後回車,就會開始更新。
不過這個工具沒法控制升級 patch 還是 minor update。