Go Modules : v2 及更高版本
本文是一篇翻譯文章: blog.golang.org/v2-go-modul…
原文作者:Jean de Klerk 和 Tyler Bui-Palsu 譯者:befovy 校對:polaris1119
譯文由 GCTT 原創編譯,Go語言中文網 榮譽推出
簡介
本文是 Go modules 系統的第四部分
- Part 1: 使用 Go Modules 譯文
- Part 2: 遷移到 Go Modules 譯文
- Part 3: 釋出 Go Modules 譯文
- Part 4: Go Modules : v2 及更高版本 (本文)
隨著成功的專案逐漸成熟以及新需求的加入,早期的功能和設計決策可能不再適用。 開發者們可能希望通過刪除廢棄使用的功能、重新命名型別或將複雜的程式拆分為可管理的小塊來融入他們的經驗教訓。這種型別的變更要求下游使用者進行更改才能將其程式碼遷移到新的 API,因此在沒有認真考慮收益成本比重的情況下,不應進行這種變更。
對於還在早期開發階段的專案(主版本號是 v0
),使用者會期望偶爾的重大變更。對於聲稱已經穩定的專案(主版本是 v1
或者更高版本),必須在新的主版本進行重大變更。這篇文章探討了主版本語義、如何建立併發布新的主版本以及如何維護一個 Go Modules 的多個主版本。
主版本和模組路徑
模組在 Go 中確定了一個重要的原則,即 “匯入相容性規則”
如果舊包和新包的匯入路徑相同,新包必須向後相容舊的包
根據這條原則,一個軟體包新的主版本沒有向後相容以前的版本。這意味著這個軟體包新的主版本必須使用和之前版本不同的模組路徑。從 v2
開始,主版本號必須出現在模組路徑的結尾(在 go.mod 檔案的 module
github.com/googleapis/gax-go
的開發者們開發完 v2
,他們用了新的模組路徑 github.com/googleapis/gax-go/v2
。想要使用 v2
的使用者必須把他們的包匯入和模組要求更改為 github.com/googleapis/gax-go/v2
需要主版本號字尾是 Go 模組和大多數其他依賴管理系統不同的方式之一。字尾用於解決菱形依賴問題。在 Go 模組之前,gopkg.in 允許軟體包維護者遵循我們現在稱為匯入相容性規則的內容。使用 gopkg.in 時,如果你依賴一個匯入了 gopkg.in/yaml.v1
的包以及另一個匯入了 gopkg.in/yaml.v2
yaml
包有著不同的匯入路徑(它們使用和 Go Modules 類似的版本字尾)。由於 gopkg.in 和 Go Modules 共享相同的版本號字尾方法,因此 Go 命令接受 gopkg.in/yaml.v2
中的 .v2
作為有效的版本號。這是一個為了和 gopkg.in 相容的特殊情況,在其他域託管的模組需要使用像 /v2
這樣的斜槓字尾。
主版本策略
推薦的策略是在以主版本字尾命名的目錄中開發 v2+
模組。
github.com/googleapis/gax-go @ master branch
/go.mod → module github.com/googleapis/gax-go
/v2/go.mod → module github.com/googleapis/gax-go/v2
複製程式碼
這種方式與不支援 Go Modules 的一些工具相容:倉庫中的檔案路徑與 GOPATH
模式下 go get
命令預期的路徑匹配。這一策略也允許所有的主版本一起在不同的目錄中開發。
其他的策略可能是將主版本放置在單獨的分支上。然而,如果 v2+
的原始碼在倉庫的預設分支上(一般是 master),不支援版本的工具(包括 GOPATH 模式下的 Go 命令)可能無法區分不同的主版本。
本文中的示例遵循主版本子目錄策略,所以提供了最大的相容性。我們建議模組的作者遵循這種策略,只要他們還有使用者在使用 GOPATH
模式開發。
釋出 v2 及更高版本
這篇文章以 github.com/googleapis/gax-go
為例:
$ pwd
/tmp/gax-go
$ ls
CODE_OF_CONDUCT.md call_option.go internal
CONTRIBUTING.md gax.go invoke.go
LICENSE go.mod tools.go
README.md go.sum RELEASING.md
header.go
$ cat go.mod
module github.com/googleapis/gax-go
go 1.9
require (
github.com/golang/protobuf v1.3.1
golang.org/x/exp v0.0.0-20190221220918-438050ddec5e
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b
google.golang.org/grpc v1.19.0
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099
)
$
複製程式碼
要開始開發 github.com/googleapis/gax-go
的 v2
版本,我們將建立一個新的 v2/
目錄並將包的內容複製到該目錄中。
$ mkdir v2
$ cp *.go v2/
building file list ... done
call_option.go
gax.go
header.go
invoke.go
tools.go
sent 10588 bytes received 130 bytes 21436.00 bytes/sec
total size is 10208 speedup is 0.95
$
複製程式碼
現在,我們通過複製當前的 go.mod
檔案並且在 module 路徑上新增 /v2
字尾來建立屬於 v2 的 go.mod
檔案。
$ cp go.mod v2/go.mod
$ go mod edit -module github.com/googleapis/gax-go/v2 v2/go.mod
$
複製程式碼
注意: v2
版本被視為與 v0 / v1
版本分開的模組,兩者可以共存於同一構建中。因此,如果你的 v2+
模組具有多個軟體包,你應該更新它們使用新的 /v2
匯入路徑,否則,你的 v2+
模組會依賴你的 v0 / v1
模組。要升級所有 github.com/my/project
為 github.com/my/project/v2
,可以使用 find
和 sed
命令:
$ find . -type f \
-name '*.go' \
-exec sed -i -e 's,github.com/my/project,github.com/my/project/v2,g' {} \;
$
複製程式碼
現在我們有了一個 v2
模組,但是我們要在版本釋出之前進行實驗並進行修改。在我們釋出 v2.0.0
(或者其他沒有預釋出字尾的版本)之前,我們可以進行開發並且可以做出重大變更,就如同我們決定實現新 API 一樣。 如果我們希望使用者能夠在正式釋出新 API 之前對其進行試驗,可以選擇釋出 v2
預釋出版本:
$ git tag v2.0.0-alpha.1
$ git push origin v2.0.0-alpha.1
$
複製程式碼
一旦我們對 v2
API 感到滿意並且確定不會再有別的重大變更,我們可以打上 Git 標記 v2.0.0
。
$ git tag v2.0.0
$ git push origin v2.0.0
$
複製程式碼
到那時,就有兩個主版本需要維護。向後相容的更改和錯誤修復使用新的次版本或者補丁版本釋出(比如 v1.1.0
, v2.0.1
等)。
總結
主版本變更會帶來開發和維護的開銷,並且需要下游使用者的額外付出才能遷移。越大的專案中這種主版本變更的開銷就越大。只有在確定了令人信服的理由之後,才應該進行主版本變更。一旦確定了令人信服的重大變更原因,我們建議在 master 分支進行多個主版本的開發,因為這樣能與各種現有工具相容。
對 v1+
模組的重大變更應該始終發生在新的 vN+1
模組中。一個新模組釋出時,對於維護者和需要遷移到這個新軟體包的使用者來說意味著更多的工作。因此,維護人員應該在釋出穩定版本之前對其 API 進行驗證,並仔細考慮在 v1
版本之後是否確有必要進行重大變更。