Go Module 為目前最主流的依賴解決的方案
發佈於 Go 11.1版,於Go 1.14推薦在Production環境上使用
Go Modules的出現解決了以下幾點爭議:
- 依賴問題
- 淘汰GOPATH的機制
- 統一社群中其他依賴管理工具
為何 GOPATH 不在被推薦使用
簡單介紹
GOPATH原先是透過定義好的目錄結構如下:
1 | go |
各目錄分別為:
- bin: 儲存所編譯產生的二進位檔案
- pkg: 儲存預先編譯的目的檔案,以加快程式的後續編譯速度
- src: 儲存所有 .go 檔案或原始程式碼
- 一般會以
$GOPATH/src/github.com/
儲存 Go的應用程式和函式庫
- 一般會以
棄用原因: 無法明確定義與參照依賴(Dependency)的版本
因為.go
專案都必須要儲存在 $GOPATH/src
底下,但該依賴管理方式並沒有提供版本控制的概念,會造成以下問題:
- 執行
go get
取得遠端的依賴包時,會無法得知目前所載的版本 - 無法處理 v1,v2,v3等不同版本的參考問題,假設有的Library叫做
/foo/bar
但不管幾版,都會在GOPATH底下,其路徑都會是一樣的 (都在github.com/foo/bar)
Go modules的基本使用方式
指令
go mod init
: 產生 go.mod 檔案go mod download
: 下載go.mod檔案中指明的所有依賴go mod tidy
: 整理所有依賴go mod graph
: 檢視現有的依賴結構go mod edit
: 編輯 go.mod 檔案go mod vendor
: 會出專案所有的依賴到vendor目錄go mod verify
: 驗證一個模組是否被竄改過go mod why
: 檢視為何需要依賴該模組
環境變數
可透過 go env
來檢視常用的環境變數,以下為與Go Module相關的環境變數
1 | GOMODULES新增的環境變數 |
GO111MODULE
透過GO111MODULE作為該專案是否使用 Go modules的開關,可設定以下內容
- auto: 只要專案包含
go.mod
檔案,就使用Go modules (在Go 1.11~1.14,為預設) - on: 啟用Go modules (推薦使用)
- off: 禁用Go modules
因為go modules是在 go1.11版本所提出的
GOPROXY
設定GO模組的proxy, 使Go在後續拉取模組的版本時,直接透過映像檔網站快速拉取,而非到Github這種Version control service平台
GOPROXY預設值為 https://proxy.golang.org,direct
,透過逗號將代理的地方給隔開
若不想使用GOPROXY, 可設置為 GOPROXY=“off”
其中最後有 direct
一詞的用意是,若前面的proxy值都沒辦法載到
模組,例如在 https://proxy.golang.org
抓不到,那麼就會回到原位置(例如Github)去下載
GOSUMDB
GOSUMDB為Go checksum database,驗證拉取的模組版本資料未經過竄改
預設值為 sum.golang.org
,也可設置成 off
,禁止Go在後續操作中做驗證版本的動作
GONOPROXY/GONOSUMDB/GOPRIVATE
設定私有模組的環境變數,一般來說建議直接設定 GONOPROXY,會作為 GONOPROXY與GONOSUMDB的預設值
值可設定多組,例如
1 | go env -w GOPRIVATE="git.xxx.com,github.com/eddycjy/mquote" |
設定之後字首為 git.xxx.com
以及 github.com/eddycjy/mquote
的模組會被認為是私有模組
也可以直接用設定 *
,以下表示來自.example.com
的子域名的都表示來自私有倉庫
1 | go env -w GOPRIVATE="*.example.com" |
啟用方式
Go modules預設不是開啟的,透過GO111MODULE
來做設定,可用auto
, on
, off
做設定
透過env進行設定 go env -w
1 | go env -w GO111MODULE=on |
初始化一個專案
在一個資料夾內,透過 go mod init
初始化一個專案
1 | go mod init github.com/jellyhola/module-repo |
這時會產生一個 go.mod
檔案,會紀錄目前專案模組為 github.com/jellyhola/module-repo
,go版本為1.15
1 | module github.com/jellyhola/module-repo |
這時定義一份 main.go
檔案,import github.com/example/hello
1 | package main |
這時專案目錄會自動產生 go.sum
檔案, 以及go.mod
會多紀錄使用了 rsc.io/quote
模組以及使用的版本為 1.5.2
go.sum
1 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8= |
go.mod
1 | module github.com/jellyhola/module-repo |
建議將 go.sum與go.mod這兩個檔案提交到版本控制
由於本身rsc.io/quote
這個模組,下載go時就已經安裝完畢,故不用透過 go get
來額外下載
接著可用go list -m all
檢視目前專案所使用的依賴
1 | go list -m all |
可看到目前 golang.org/x/text
這項模組,為untagged的狀況,接著可以透過 go get
來更新此模組到最新的tag
1 | go get golang.org/x/text |
可看到 go.mod內容改變了
1 | module github.com/jellyhola/module-repo |
其中 // indirect
表示該模組為間接依賴,表示目前應用程式的import敘述中,並沒有發現這模組的明確參考,有可能是
- 事先手動go get拉取下來的模組
- 該專案所依賴模組之所依賴的
試著跑一下專案,驗證模組是否能正常運作
1 | go run main.go |
拉取的模組會存在哪?
go mod會將拉取的模組放置在 $GOPATH/pkg/mod
與 $GOPATH/pkg/sumdb
目錄下!
1 | ls $GOPATH/pkg |
若想要清除已經快取的模組版本,使用 go clean -modcache
指令
Go module 進階探討
Go modules的go get探討
使用go get取得模組時,會分別進行3個步驟
- finding: 查找模組是否存在
- downloading: 進行下載
- extracting: 分析下載的模組的雜湊值是否正確
像上一節所拉取的模組 golang.org/x/text
其拉取的資訊如下
1 | 版本資訊-commit的時間-雜湊值 |
- commit的時間會是以UTC時區為準
- go get拉取的指令若沒有指定版本,會直接拉取到 v0.0.0版
go get行為
- go get: 直接拉取dependency,只會更新指定的他自己
- go get -u: 更新現有的dependency,會強制更新該denpendency所依賴的其他模組
- go get -u -t: 更新所有denpendency,以及包含單元測試使用到的
go get版本指定
- go get golang.org/x/text@latest : 拉取最新的版本,若存在tag,優先使用之
- go get golang.org/x/text@master : 拉取master分支的最新commit
- go get golang.org/x/text@v0.3.2 : 拉取tag為v0.3.2的commit
- go get golang.org/x/text@342b2e : 拉取hash為342b2e的commit,最後轉為 v0.3.2
Go modules的go run與go build
執行go build時,會根據go.mod自動下載該專案所需的模組,才進行編譯
若是要使用 vendor目錄作為denpendency, 在執行 go mod vendor產生vendor目錄後,需要執行 go build -mod=vendor
才可使用 go build -mod=vendor 使用vendor目錄作為denpendency來編譯
Go modules匯入路徑說明
Go modules在主版本編號為 v0與v1的情況會省略其編號,在v2以上則需要明確指定!
假如要匯入 v2以上的golang.org/example@v2.0.0,則必須要在import底下明確指定v2
1 | import ( |
為何省略v0與v1??
因為官方鼓勵開發人員將模組建立到v1版本就不在變動。
所以若開發人員在發佈v2版本時,會被擁有明確的v1版本尾綴,近一步導致v1版本變成雜訊且沒有什麼意義
在匯入路徑忽略v0版本,因為根據語意化版本標準,v0的這些版本大多沒有相容性保證
Go modules 語意化版本控制
一個go modules的版本資訊如下
1 | //主版本號.次版本號.修訂號 |
- 主版本編號:做了不相容的API修改之類的
- 次版本編號: 做了向下相容的功能性新增
- 修訂好: 向下相容的功能除錯
若是先行版本的話,那加上 pre
後綴
1 | v1.2.3-pre |
發佈新版本時應遵循語意化版本規則,否則無法被go get所拉取
go list的功能
提供該專案所有denpendency的資訊
透過 go list -m -u all
可以檢視所有的dependency資訊
1 | $ go list -m -u all |
- -m: 顯示所有依賴的模組
- -u: 顯示能夠升級的版本