在代碼發布到 v2 之後,發布的策略與之前稍有不同,這篇文章介紹了 v2 之後版本的發布方法。
原文地址:https://blog.golang.org/v2-go-modules
簡介這個系列的文章總共有五篇,這是第四篇:
隨著一個項目的成熟和新需求的增加,過去的特性和設計決策可能要被廢棄。開發人員可能希望通過刪除棄用的函數、重命名類型或將複雜的包拆分為可管理的部分來實踐所學。這些的更改需要下遊用戶將他們的代碼遷移到新的 API 中,因此應該仔細考慮收益與成本,在收益大於成本的情況下才進行更改。
對於仍處於試驗階段的項目(主版本號是 v0),用戶期望偶爾會有突破性的變化。對於那些被聲明為穩定的項目,在主版本 v1 或更高版本中,更改必須在新的主版本中完成。這篇文章探討了主版本的語義,如何創建和發布一個新的主版本,以及如何維護一個模塊的多個主版本。
主版本和模塊路徑模塊形成了了 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模塊一樣。因為 gopkg.in 與 Go 模塊使用相同的版本後綴方法,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這個方法與對模塊無感知的工具兼容:代碼倉庫中的文件路徑與 GOPATH 模式下使用 go get 相匹配。這個策略還允許不同目錄下的主版本同時進行開發。
其他策略可能將主版本保留在單獨的分支上。但是,如果 v2+ 版本的原始碼在倉庫的的默認分支上(通常是 master 分支),那麼不支持版本感知的工具——包括 GOPATH 模式下的 go 命令——可能無法區分主要版本。
這篇文章中的例子將遵循主版本子目錄策略,因為它有最好的兼容性。我們建議模塊作者,如果有用戶在 GOPATH 模式下進行開發,就遵循這種策略。
發布 v2 及後續版本這篇文章以 github.com/googleapis/gax-go 項目為例:
$ pwd/tmp/gax-go$ lsCODE_OF_CONDUCT.md call_option.go internalCONTRIBUTING.md gax.go invoke.goLICENSE go.mod tools.goREADME.md go.sum RELEASING.mdheader.go$ cat go.modmodule 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 ... donecall_option.gogax.goheader.goinvoke.gotools.go
sent 10588 bytes received 130 bytes 21436.00 bytes/sectotal size is 10208 speedup is 0.95$然後把當前的 go.mod 文件也拷貝到 v2 目錄下,並在模塊路徑後面增加 v2 後綴:
$ 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 已經穩定,並確定不需要做任何破壞性的更改,我們可以發布正式版本,標記 v2.0.0:
$ git tag v2.0.0$ git push origin v2.0.0$到目前為止,有兩個主版本需要維護。向後兼容的更改和錯誤修復將產生新的次版本和補丁版本(例如,v1.1.0, v2.0.1,等等)。
結論主版本變更會產生更多的開發和維護開銷,並需要下遊用戶配合遷移。項目越大,這些管理代價就越大。只有在確定非做不可的原因之後,才應該進行主版本的變更。一旦修改原因被確定是一個破壞性變更,我們建議在主分支中開發多個主版本,因為這種方式能夠最大化的與現有工具兼容。
對 v1+ 版本模塊的破壞性更改應該總是發生在 vN+1 版本模塊中。當新模塊發布時,這意味著模塊的維護者和需要遷移到新版本的用戶需要做額外的工作。因此,維護者應該在發布一個穩定的版本之前驗證他們的 API,並仔細考慮在 v1 版本之外是否真的有必要做大版本的變更。
另外,騰訊雲區塊鏈方向在大量招人,包括前端、後端、架構師、產品等諸多崗位,如果感興趣,請把簡歷投過來 rayjun0412@gmail.com。
譯 / Rayjun