Go 部落格
2019 年的 Go Modules
不平凡的一年!
2018 年對於 Go 生態系統來說是偉大的一年,其中包管理是我們的主要關注點之一。2 月份,我們發起了一場社群範圍的討論,關於如何將包管理直接整合到 Go 工具鏈中,並在 8 月份的 Go 1.11 版本中釋出了該功能的第一個初步實現,稱之為 Go modules。遷移到 Go modules 將是 Go 1 以來 Go 生態系統中最深遠的變化。將整個生態系統——程式碼、使用者、工具等——從 GOPATH 轉換為 modules 需要在許多不同領域進行工作。反過來,模組系統將幫助我們為 Go 生態系統提供更好的認證和構建速度。
本文預覽了 Go 團隊在 2019 年針對 modules 的計劃。
版本釋出
於 2018 年 8 月釋出的 Go 1.11 引入了對 modules 的初步支援。目前,module 支援與傳統的基於 GOPATH 的機制並行維護。當在 GOPATH/src 之外的目錄樹中執行,並且根目錄由 go.mod
檔案標記時,go
命令預設進入 module 模式。此設定可以透過將過渡性環境變數 $GO111MODULE
設定為 on
或 off
來覆蓋;預設行為是 auto
模式。我們已經看到 Go 社群廣泛採用了 modules,同時也收到了許多有益的建議和錯誤報告,幫助我們改進 modules。
計劃於 2019 年 2 月釋出的 Go 1.12 將進一步完善 module 支援,但預設仍保留 auto
模式。除了許多錯誤修復和其他細微改進之外,Go 1.12 中最顯著的變化可能在於,像 go
run
x.go
或 go
get
rsc.io/2fa@v1.1.0
這樣的命令現在可以在沒有顯式 go.mod
檔案的情況下以 GO111MODULE=on
模式執行。
我們的目標是讓計劃於 2019 年 8 月釋出的 Go 1.13 預設啟用 module 模式(即將預設值從 auto
更改為 on
)並廢棄 GOPATH 模式。為此,我們一直在努力改進工具支援,並更好地支援開源模組生態系統。
工具和 IDE 整合
在使用 GOPATH 的八年裡,湧現了大量的工具,這些工具都假定 Go 原始碼儲存在 GOPATH 中。遷移到 modules 需要更新所有基於這一假定的程式碼。我們設計了一個新包,golang.org/x/tools/go/packages,它抽象了查詢和載入特定目標 Go 原始碼資訊的邏輯。這個新包能自動適應 GOPATH 和 modules 模式,並且還可以擴充套件以支援特定工具的程式碼佈局,例如 Bazel 使用的那種。我們一直與 Go 社群的工具作者合作,幫助他們在工具中採用 golang.org/x/tools/go/packages。
作為這項工作的一部分,我們還一直在努力將 gocode、godef 和 go-outline 等各種原始碼查詢工具統一到一個單獨的工具中,該工具可以從命令列使用,並且支援現代 IDE 使用的語言伺服器協議。
向 modules 的過渡和包載入方式的變化也推動了 Go 程式分析的重大變革。作為重構 go
vet
以支援 modules 的一部分,我們引入了一個用於 Go 程式增量分析的通用框架,其中分析器一次為一個包呼叫。在這個框架中,對一個包的分析可以輸出事實,供匯入該包的其他包的分析使用。例如,go
vet
對 log 包的分析確定並記錄了 log.Printf
是 fmt.Printf
的包裝函式這一事實。然後 go
vet
可以檢查呼叫 log.Printf
的其他包中的 printf 風格格式字串。這個框架應該能催生許多新的、複雜的程式分析工具,幫助開發者更早地發現 bug 並更好地理解程式碼。
模組索引
go
get
原始設計中最重要的部分之一是它的去中心化:我們當時相信——今天仍然相信——任何人都可以將他們的程式碼釋出到任何伺服器上,這與 Perl 的 CPAN、Java 的 Maven 或 Node 的 NPM 等中央註冊中心形成對比。在 go
get
匯入路徑的開頭放置域名重用了現有的去中心化系統,並避免了重新解決誰可以使用哪個名稱的問題。這也允許公司從私有伺服器匯入程式碼以及從公共伺服器匯入程式碼。在我們轉向 Go modules 時,保持這種去中心化至關重要。
Go 依賴項的去中心化帶來了許多好處,但也帶來了一些顯著的缺點。首先是太難找到所有公開可用的 Go 軟體包。每個希望提供軟體包資訊的網站都必須自己進行抓取,否則就必須等到使用者詢問特定軟體包時才去獲取它。
我們正在開發一項新服務,即 Go 模組索引,它將提供進入 Go 生態系統的軟體包的公共日誌。像 godoc.org 和 goreportcard.com 這樣的網站將能夠監視此日誌以查詢新條目,而無需各自獨立實現查詢新軟體包的程式碼。我們還希望該服務允許使用簡單的查詢來查詢軟體包,以便 goimports
可以為尚未下載到本地系統的軟體包新增匯入。
模組認證
目前,go
get
依賴於連線級認證(HTTPS 或 SSH)來檢查它是否正在與正確的伺服器通訊以下載程式碼。對程式碼本身沒有額外的檢查,如果 HTTPS 或 SSH 機制以某種方式受到損害,則存在中間人攻擊的可能性。去中心化意味著構建所需的程式碼是從許多不同的伺服器獲取的,這意味著構建依賴於許多系統來提供正確的程式碼。
Go modules 設計透過在每個模組中儲存一個 go.sum
檔案來改進程式碼認證;該檔案列出了模組每個依賴項的預期檔案樹的加密雜湊值。使用 modules 時,go
命令使用 go.sum
來驗證依賴項與預期版本完全一致(逐位相同),然後才在構建中使用它們。但 go.sum
檔案只列出了該模組使用的特定依賴項的雜湊值。如果您正在新增新的依賴項或使用 go
get
-u
更新依賴項,則 go.sum
中沒有相應的條目,因此無法直接驗證下載的資料。
對於公開可用的模組,我們計劃執行一個我們稱之為公證服務的服務,它會跟蹤模組索引日誌,下載新模組,並對形如“模組 M 在版本 V 時具有檔案樹雜湊 H”的宣告進行加密簽名。公證服務將在一個可查詢、證書透明風格的防篡改日誌中釋出所有這些經過公證的雜湊值,這樣任何人都可以驗證公證服務的行為是否正確。該日誌將作為一個公共的、全域性的 go.sum
檔案,go
get
可以在新增或更新依賴項時使用它來認證模組。
我們的目標是在 Go 1.13 中,讓 go
命令開始檢查尚未在 go.sum
中的公開可用模組的經過公證的雜湊值。
模組映象
由於去中心化的 go
get
從多個源伺服器獲取程式碼,因此獲取程式碼的速度和可靠性取決於最慢、最不可靠的伺服器。在 modules 出現之前唯一可用的防禦措施是將依賴項 vendor 到自己的倉庫中。雖然 vendoring 將繼續得到支援,但我們更傾向於一種適用於所有模組(不僅僅是您正在使用的模組),並且不需要將依賴項複製到每個使用它的倉庫中的解決方案。
Go module 設計引入了模組代理的概念,這是一個 go
命令請求模組的伺服器,而不是直接向源伺服器請求。一種重要的代理是模組映象,它透過從源伺服器獲取模組並將其快取起來,供將來請求使用來響應模組請求。一個執行良好的映象即使在某些源伺服器宕機時也應該快速可靠。我們計劃在 2019 年推出一個針對公開可用模組的映象服務。其他專案,如 GoCenter 和 Athens,也在計劃提供映象服務。(我們預計公司也將有多種選項來執行自己的內部映象,但本文重點關注公共映象。)
映象的一個潛在問題是,它們恰好是中間人伺服器,這使得它們成為攻擊的天然目標。Go 開發者需要一些保證,確保映象提供的與源伺服器提供的資料是相同的。我們在上一節中描述的公證過程恰恰解決了這個問題,並且它將適用於使用映象下載以及使用源伺服器下載的情況。映象本身不需要被信任。
我們的目標是讓由 Google 運營的模組映象在 Go 1.13 中預設由 go
命令使用。使用替代映象或根本不使用映象,配置起來將非常簡單。
模組發現
最後,我們之前提到模組索引將使得構建像 godoc.org 這樣的網站變得更容易。我們 2019 年工作的一部分將是對 godoc.org 進行重大改造,使其對需要發現可用模組並決定是否依賴某個模組的開發者更加有用。
總體概覽
此圖顯示了本文所述設計中模組原始碼的流動方式。

之前,所有 Go 原始碼的消費者——go
命令和像 godoc.org 這樣的網站——都直接從每個程式碼託管源獲取程式碼。現在他們可以從一個快速、可靠的映象獲取快取的程式碼,同時仍然能認證下載的資料是正確的。索引服務使得映象、godoc.org 以及任何其他類似網站能夠輕鬆跟上每天新增到 Go 生態系統中的所有優秀新程式碼。
我們對 2019 年 Go modules 的未來感到興奮,希望您也一樣。新年快樂!
下一篇文章:Go 1.12 釋出
上一篇文章:Go 2,我們來了!
部落格索引