GoModule:v2及新版本
英文原版:https://blog.golang.org/v2-go-modules
介紹
這是系列文章的第四部分
- 1-使用GoModule
- 2-遷移到GoModule
- 3-發佈GoModule
- 4-GoModule: v2及新版本(當前)
隨着項目中新需求的添加,之前的功能和設計可能不太合適。開發者可能移除廢棄的function,重命名type、或者將package分割成更好管理的多個部分。這種變動需要下游使用者調整代碼來適應新的API,因此在做這種改動之前要仔細權衡利弊。
處於實驗階段的項目(majro版本爲v0)可能會有大的改動,而stable階段的項目(major版本爲v1或以上)如果有大的改動則需要發佈新的major版本。本文主要介紹如何發佈新的major版本以及如何維護多個majro版本。
majro版本及module路徑
module的正式且重要的規則:import compatibility rule
:
如果新的包與舊的包使用相同的導包路徑,那麼新的包必須兼容舊的包
根據定義,package的新major版本不會兼容之前的版本,意味着新的major版本必須有與之前版本不同的module路徑。從v2開始,major版本必須出現在module路徑的結尾(在go.mod
文件的module
語句中聲明)。例如,當github.com/googleapis/gax-go
發佈了v2版本,那新的module路徑就是github.com/googleapis/gax-go/v2
,使用v2的用戶需要修改import爲github.com/googleapis/gax-go/v2
。
major版本作爲後綴是GoModule與其他依賴管理的區別之一。後綴需要解決diamond dependency problem
。在GoModule之前,gopkg.in
允許package維護人員遵循我問現在稱之爲import compatibility rule
的內容。對於gopkg.in
,如果當前依賴包的import是gopkg.in/yaml.v1
且另一個包的import是gopkg.in/yaml.v2
這種,跟新的規則並不衝突,因爲這兩個yaml包的導包路徑不同。go命令接受.v2
這種major版本作爲路徑後綴,這是爲gopkg.in
做的特殊兼容,其他域名的module仍應使用/v2
這種後綴。
major版本策略
推薦的策略是在新的以major版本後綴命名的目錄下進行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
這種方法兼容非module模式的工具:代碼庫的文件路徑與GOPATH
模式下的go get
兼容,這個策略也允許各個major版本在獨立的目錄下開發。
有的策略將不同majro版本放在不同分支,但是如果v2及以上的代碼在代碼庫的默認分支上,那麼不支持版本的工具(包括GOPATH
模式下的go命令)可能無法識別major版本。
本文的示例將遵循major版本子目錄的策略,這樣可以保證最大的兼容性。我們建議module開發者遵循這種策略使得其用戶也可以在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
)
$
要開始v2版本的開發,首先新建一個v2
文件夾並將package複製進去
$ 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
創建一個v2的go.mod
並添加v2
後綴到module路徑中:
$ cp go.mod v2/go.mod
$ go mod edit -module github.com/googleapis/gax-go/v2 v2/go.mod
$
注意v2版本會認爲是與v0、v1獨立的版本:這兩個版本共存在同一個build。所以如果v2級以上版本的module用於多個package,他們都需要升級到新的/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的module,但是我們想在發佈之前先試一下。在發佈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
的tag:
$ git tag v2.0.0
$ git push origin v2.0.0
$
這時就維護了兩個major版本,兼容修改或者bug修復將需要發佈新的minor或者patch版本(如v1.1.0、v2.0.1等)。
總結
升級major版本會帶來開發及維護成本,並且下游的用戶需要做點事情才能進行遷移。項目越大開銷越大,所以最好在確實需要的情況下進行major版本的升級。一旦確定了有重大變更,建議在master分支中開發多個major版本,這樣可以與其他現有工具兼容。
對v1及以上module做重大變更應始終發生在vN+1版本上。發佈新module意味着開發維護人員需要做更多的工作來遷移到新版本。因此請在發佈新版本前驗證其API並仔細考慮是否有必要在v1之後做重大變更。