Go 包管理中的常見問題
- 2020 年 5 月 26 日
- 筆記
- GO111MODULE, golang
隨處可見的「GO111MODULE=on」
在GitHub上,經常看到不少項目的readme里都有這麼一句:
✗ GO111MODULE=on go get golang.org/x/tools/gopls@latest
2009年,Go被官方發布的時候,沒有自帶包管理功能。go get
命令會根據import
路徑,把所有源碼下載到$GOPATH/src
目錄下。這導致,我們無法對依賴包進行版本控制,只有master分支能代表一個包的穩定版本。
Go 1.11版本中,引入了Go Modules
(即,Go模組),可以通過go.mod
文件存儲依賴包的版本號。
從那時起,GOPATH模式
和Go模組模式
之間的交互就成為了Go最大的陷阱之一。
要想避開這些陷阱,離不開一個環境變數——GO111MODULE
。
「GO111MODULE=on」是幹嘛用的
簡單來講,GO111MODULE=on
開啟Go模組模式
,允許用戶在go get
時指定依賴包的版本。
✗ GO111MODULE=off go get golang.org/x/tools/[email protected]
go: cannot use path@version syntax in GOPATH mode
# 報錯:GOPATH模式無法指定版本
使用「go get」時,「go.mod」會自動更新
這裡要小心踩坑。默認情況下,如果當前目錄下有go.mod
文件,go get
命令會把你剛剛安裝的依賴包更新到go.mod
。
在 安裝一些開發工具(比如gopls, kind)時,如果你不想更新go.mod
文件,可以使用cd && go get
命令避免污染 go.mod
。
「go get」 VS 「go install」
- go get: 解析、添加 pkg 之間的依賴關係,然後下載、編譯、安裝pkg。【默認修改go.mod】
- go install: 編譯、安裝 程式碼中import的pkg。【不修改go.mod】
# 更詳細的解釋,可見官方文檔
✗ go help get
✗ go help install
不要在「go get」中混用 「-u」 和 「@version」
比如 在使用@latest
時,你只想要更新當前包到最新tag。加上-u
選項後,go會把這個包的所有依賴包也更新。如果依賴包里有breaking changes,那就坑慘了。
# 只更新 protos-go
✗ go get git.bala.com/hahaha/protos-go@latest
go: git.bala.com/hahaha/protos-go latest => v0.0.6
# 依賴包也全都更新
✗ go get -u git.bala.com/hahaha/protos-go@latest
go: git.bala.com/hahaha/protos-go latest => v0.0.6
go: github.com/golang/protobuf upgrade => v1.4.2
go: google.golang.org/protobuf upgrade => v1.23.0
再比如,如果使用@v0.1
加上-u
選項,go get
會獲取1.0標籤
下最新的版本。
如何修改依賴包內的程式碼
有時候,我們import了一個依賴包,但是需要修改其中的程式碼,怎麼辦呢?
- 1)最簡單的方法: 在
go.mod
文件最後加上replace
。
# 替換成本地路徑
replace github.com/maelvls/beers => ../beers
# 替換成 github 路徑
replace github.com/facebookincubator/ent v0.0.1 => github.com/mine/ent v0.0.1
- 2)使用vendor
使用 go mod vendor
+ go build -mod=vendor
命令,這會強制go
使用vendor/
路徑下的文件,而不是$GOPATH/pkg/mod
下的。
這倆命令,也可以幫助你的 vim 和 VSCode 解決找不到 某個指定版本的包的問題,畢竟現在所有的依賴包都在 /vendor
目錄下了。
如何使用私有庫的 pkg
大家在公司一般使用 Gitlab/Github 上的私有程式碼庫。Go 1.13之後,使用GOPRIVATE
變數,我們可以跳過包代理,更快捷地獲取私有庫程式碼。
# 也可以在 .gitconfig 文件中加上相應配置
git config --global url."//foo:${GITHUB_TOKEN}@github.com/company".insteadOf "//github.com/company"
export GOPRIVATE=github.com/company/\*