Go 1.16 中 Module 功能新變化

kevin發表於2021-02-19
原文地址:https://blog.golang.org/go116-module-changes
原文作者:Jay Conrod
譯者:Kevin

希望您喜歡 Go 1.16! 這個版本有很多新功能,特別是對 Module 而言。發行說明中簡要介紹了這些變化,但讓我們深入發掘一下其中的一些變化。

Module 功能預設開啟

go 命令現在預設以 module-aware 模式構建包,即使沒有go.mod檔案存在。這是向在所有專案中使用 Module 功能邁出的一大步。

通過設定GO111MODULE環境變數為off,仍然可以在GOPATH模式下構建包。你也可以將GO111MODULE設定為auto,只有噹噹前目錄或任何父目錄中存在go.mod檔案時才啟用 module-aware 模式。這在以前是預設的。請注意,您可以使用go env -w來永久地設定GO111MODULE和其他變數。

go env -w GO111MODULE=auto

我們計劃在 Go 1.17 中放棄對GOPATH模式的支援。換句話說,Go 1.17 將忽略GO111MODULE。如果您的專案沒有以 module-aware 模式構建,現在是時候遷移了。如果有問題妨礙您遷移,請考慮提交問題體驗報告

不會自動更改 go.mod 和 go.sum

在之前的版本中,當go命令發現go.modgo.sum有問題時,比如缺少require指令或缺少sum,它會嘗試自動修復問題。我們收到了很多反饋,認為這種行為是出乎大家意料的,尤其是對於像go list這樣通常不會產生副作用的命令。自動修復並不總是可取的:如果一個匯入的包沒有被任何需要的 Module 提供,go命令會新增一個新的依賴關係,可能會觸發普通依賴關係的升級。即使是拼寫錯誤的匯入路徑也會導致(失敗的)網路查詢。

在 Go 1.16 中,module-aware 命令在發現go.modgo.sum中的問題後會報告一個錯誤,而不是嘗試自動修復問題。在大多數情況下,錯誤資訊建議使用命令來修復問題。

$ go build
example.go:3:8: no required module provides package golang.org/x/net/html; to add it:
    go get golang.org/x/net/html
$ go get golang.org/x/net/html
$ go build

和之前一樣,如果存在vendor目錄,go命令可能會使用該目錄(詳見Vendoring)。像go getgo mod tidy這樣的命令仍然會修改go.modgo.sum,因為它們的主要目的是管理依賴關係。

在特定版本上安裝可執行檔案

go install命令現在可以通過指定@version字尾來安裝特定版本的可執行檔案。

go install golang.org/x/tools/gopls@v0.6.5

當使用這種語法時,go install命令會從該 Module 的制定版本安裝,而忽略當前目錄和父目錄中的任何 go.mod 檔案。(如果沒有@version字尾,go install 會像往常一樣繼續執行,使用當前 Module 的 go.mod 中列出的版本要求和替換來構建程式。)

我們曾經推薦使用go get -u程式來安裝可執行檔案,但這種使用方式對go.mod中新增或更改 Module 版本需求的意義造成了太多的混淆。而為了避免意外修改go.mod,人們開始建議使用更復雜的命令,比如:

cd $HOME; GO111MODULE=on go get program@latest

現在我們都可以用go install program@latest來代替。詳情請看go install

為了消除使用哪個版本的歧義,當使用這種安裝語法時,對程式的 go.mod 檔案中可能存在的指令有一些限制。特別是,至少在目前,替換和排除指令是不允許的。從長遠來看,一旦新的go install program@version在足夠多的用例中執行良好,我們計劃讓go get停止安裝命令二進位制檔案。詳情請參見issue 43684

Module 撤回

您是否曾經在 Module 版本準備好之前不小心釋出過?或者您是否在版本釋出後就發現了一個需要快速修復的問題?釋出的版本中的錯誤是很難糾正的。為了保持 Module 構建的確定性,一個版本在釋出後不能被修改。即使你刪除或更改了版本標籤,proxy.golang.org和其他代理可能已經有了原始版本的快取。

Module 作者現在可以使用go.mod中的retract指令撤回 Module 版本。撤回的版本仍然存在,並且可以被下載(所以依賴它的構建不會中斷),但在解析@latest這樣的版本時,go命令不會自動選擇它,go getgo list -m -u會列印關於現有使用版本的警告。

例如,假設一個流行庫example.com/lib的作者釋出了v1.0.5,然後發現了一個新的安全問題。他們可以在他們的 go.mod 檔案中新增如下指令。

// Remote-triggered crash in package foo. See CVE-2021-01234.
retract v1.0.5

接下來,作者可以標記並推送v1.0.6版本,即新的最高版本。在這之後,已經依賴 v1.0.5 的使用者在檢查更新或升級依賴的軟體包時,就會被通知版本撤回。通知資訊可能會包含來自retract指令上方註釋的文字。

$ go list -m -u all
example.com/lib v1.0.0 (retracted)
$ go get .
go: warning: example.com/lib@v1.0.5: retracted by module author:
    Remote-triggered crash in package foo. See CVE-2021-01234.
go: to switch to the latest unretracted version, run:
    go get example.com/lib@latest

關於互動式的、基於瀏覽器的使用指南,請檢視play-with-go.dev上的Retract Module Versions。可以檢視retract指令文件以瞭解語法細節。

用 GOVCS 控制版本管理工具

go命令可以從proxy.golang.org這樣的映象中下載 Module 原始碼,或者直接從使用githgsvnbzrfossil的版本管理倉庫中下載。直接的版本控制訪問是很重要的,特別是對於那些在代理上無法使用的私有 Module,但這也是一個潛在的安全問題:版本控制工具中的一個 bug 可能會被惡意伺服器利用,執行非預期的程式碼。

Go 1.16 引入了一個新的配置變數GOVCS,讓使用者可以指定哪些 Module 可以使用特定的版本控制工具。GOVCS接受一個以逗號分隔的pattern:vcslist規則列表。pattern是一個path.Match模式,匹配一個 Module 路徑的一個或多個字首元素。特殊模式 public 和 private 匹配公共和私有 Module(private 被定義為由 GOPRIVATE 中的模式匹配的 Module;public 是其他所有 Module)。vcslist是一個以管道符分隔的允許的版本控制命令列表,或關鍵字alloff

例如

GOVCS=github.com:git,evil.com:off,*:git|hg

在此設定下,路徑在github.com上的 Module 可以使用git下載;路徑在evil.com上的 Module 不能使用任何版本管理程式下載,其他所有路徑(* 匹配所有)可以使用githg下載。

如果沒有設定GOVCS,或者一個 Module 不符合任何模式,go命令就會使用這個預設值:公共 Module 允許使用githg,私有 Module 允許使用所有工具。只允許GitMercurial的理由是,這兩個系統作為不受信任的伺服器的客戶端執行的問題最受關注。相比之下,BazaarFossilSubversion主要是在受信任的、經過認證的環境中使用,作為攻擊面的審查程度不高。也就是說,預設的設定是

GOVCS=public:git|hg,private:all

更多細節請參見使用GOVCS控制版本管理工具

下一步?

我們希望您覺得這些功能很有用。我們已經在努力為 Go 1.17 開發新的 Module 功能,特別是懶惰Module載入,這將使 Module 載入過程更快、更穩定。和以往一樣,如果您遇到新的 bug,請在問題跟蹤上告訴我們。Happy coding!

更多原創文章乾貨分享,請關注公眾號
  • Go 1.16 中 Module 功能新變化
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章