Go 部落格

Go 1.16 中的新模組變化

Jay Conrod
18 February 2021

我們希望您喜歡 Go 1.16!這個版本有很多新特性,特別是關於模組的。發行說明 簡要描述了這些變化,但讓我們深入探討其中幾個。

模組預設開啟

現在,go 命令預設在模組感知模式下構建軟體包,即使不存在 go.mod 檔案也是如此。這是朝著在所有專案中使用模組邁出的重要一步。

透過將 GO111MODULE 環境變數設定為 off,仍然可以在 GOPATH 模式下構建軟體包。您也可以將 GO111MODULE 設定為 auto,以便僅在當前目錄或任何父目錄中存在 go.mod 檔案時啟用模組感知模式。這之前是預設設定。請注意,您可以使用 go env -w 永久設定 GO111MODULE 和其他變數。

go env -w GO111MODULE=auto

我們計劃在 Go 1.17 中放棄對 GOPATH 模式的支援。換句話說,Go 1.17 將忽略 GO111MODULE。如果您有專案尚未在模組感知模式下構建,現在是遷移的時候了。如果存在阻止您遷移的問題,請考慮提交一個 issue 或一份 體驗報告

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

以前,當 go 命令發現 go.modgo.sum 中存在問題(例如缺失的 require 指令或缺失的 checksum)時,它會嘗試自動修復問題。我們收到了很多反饋,認為這種行為令人意外,特別是對於像 go list 這樣通常沒有副作用的命令。自動修復並不總是理想的:如果匯入的軟體包未由任何所需的模組提供,go 命令會新增一個新的依賴項,這可能會觸發常用依賴項的升級。即使是拼寫錯誤的匯入路徑也會導致(失敗的)網路查詢。

在 Go 1.16 中,模組感知命令在發現 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 將安裝來自該確切模組版本的命令,忽略當前目錄和父目錄中的任何 go.mod 檔案。(如果沒有 @version 字尾,go install 會像往常一樣繼續操作,使用當前模組的 go.mod 中列出的版本要求和替換來構建程式。)

我們過去推薦使用 go get -u program 來安裝可執行檔案,但這種用法與 go getgo.mod 中新增或更改模組版本要求的含義混淆太多。為了避免意外修改 go.mod,人們開始建議使用更復雜的命令,例如

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

現在我們都可以改為使用 go install program@latest。有關詳情,請參閱 go install

現在我們都可以改用 go install program@latest。詳情請參閱 go install

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

模組撤回

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

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

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

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

$ 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 指令文件

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

對於互動式的、基於瀏覽器的指南,請檢視 play-with-go.dev 上的撤回模組版本。有關語法詳情,請參閱 retract 指令文件

使用 GOVCS 控制版本控制工具

go 命令可以從像 proxy.golang.org 這樣的映象或直接使用 githgsvnbzrfossil 從版本控制倉庫下載模組原始碼。直接訪問版本控制非常重要,特別是對於在代理上不可用的私有模組,但它也可能是一個安全問題:版本控制工具中的錯誤可能被惡意伺服器利用來執行非預期的程式碼。

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

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

例如

GOVCS=public:git|hg,private:all

有關詳情,請參閱 使用 GOVCS 控制版本控制工具

透過此設定,路徑位於 github.com 的模組可以使用 git 下載;路徑位於 evil.com 的模組不能使用任何版本控制命令下載;所有其他路徑(* 匹配所有)可以使用 githg 下載。

如果未設定 GOVCS,或者模組不匹配任何模式,go 命令將使用此預設設定:允許公共模組使用 githg,允許私有模組使用所有工具。僅允許 Git 和 Mercurial 的理由是,這兩個系統作為不受信任伺服器的客戶端執行時,其安全性問題受到了最多的關注。相比之下,Bazaar、Fossil 和 Subversion 主要用於受信任、經過身份驗證的環境,並且作為攻擊面的審查不夠充分。也就是說,預設設定是

下一篇文章:上下文和結構體
詳情請參閱 使用 GOVCS 控制版本控制工具
接下來是什麼?