Go 官方部落格

釋出 Go 模組

Tyler Bui-Palsulich
2019 年 9 月 26 日

引言

本文是系列文章的第三部分。

注意:有關開發模組的文件,請參閱開發和釋出模組

本文討論如何編寫和釋出模組,以便其他模組可以依賴它們。

請注意:本文涵蓋了直至幷包括 v1 的開發。如果您對 v2 感興趣,請參閱Go 模組:v2 及後續版本

本文示例中使用 GitMercurialBazaar 等也受支援。

專案設定

對於本文,您需要一個現有專案作為示例。因此,從使用 Go 模組文章末尾的檔案開始。

$ cat go.mod
module example.com/hello

go 1.12

require rsc.io/quote/v3 v3.1.0

$ cat go.sum
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

$ cat hello.go
package hello

import "rsc.io/quote/v3"

func Hello() string {
    return quote.HelloV3()
}

func Proverb() string {
    return quote.Concurrency()
}

$ cat hello_test.go
package hello

import (
    "testing"
)

func TestHello(t *testing.T) {
    want := "Hello, world."
    if got := Hello(); got != want {
        t.Errorf("Hello() = %q, want %q", got, want)
    }
}

func TestProverb(t *testing.T) {
    want := "Concurrency is not parallelism."
    if got := Proverb(); got != want {
        t.Errorf("Proverb() = %q, want %q", got, want)
    }
}

$

接下來,建立一個新的 git 倉庫並新增初始提交。如果您正在釋出自己的專案,請確保包含 LICENSE 檔案。切換到包含 go.mod 的目錄,然後建立倉庫。

$ git init
$ git add LICENSE go.mod go.sum hello.go hello_test.go
$ git commit -m "hello: initial commit"
$

語義版本與模組

go.mod 中的每個必需模組都有一個語義版本,它是構建該模組時使用的依賴項的最低版本。

語義版本的形式為 vMAJOR.MINOR.PATCH

  • 當您對模組的公共 API 進行向後不相容的更改時,增加 MAJOR 版本。這僅在絕對必要時才應執行。
  • 當您對 API 進行向後相容的更改時,例如更改依賴項或新增新函式、方法、結構體欄位或型別時,增加 MINOR 版本。
  • 在進行不影響模組公共 API 或依賴項的次要更改後,例如修復錯誤時,增加 PATCH 版本。

您可以透過附加連字元和點分隔的識別符號來指定預釋出版本(例如 v1.0.1-alphav2.2.2-beta.2)。go 命令優先選擇正常釋出版本而不是預釋出版本,因此如果您的模組有任何正常釋出版本,使用者必須明確請求預釋出版本(例如 go get example.com/hello@v1.0.1-alpha)。

v0 主要版本和預釋出版本不保證向後相容性。它們允許您在向用戶做出穩定性承諾之前完善 API。但是,v1 主要版本及後續版本要求在該主要版本內保持向後相容性。

go.mod 中引用的版本可能是倉庫中標記的顯式釋出版本(例如 v1.5.2),也可能是基於特定提交的偽版本(例如 v0.0.0-20170915032832-14c0d48ead0c)。偽版本是一種特殊的預釋出版本。當用戶需要依賴尚未釋出任何語義版本標籤的專案,或基於尚未標記的提交進行開發時,偽版本很有用,但使用者不應假定偽版本提供穩定或經過充分測試的 API。使用顯式版本標記您的模組向用戶表明特定版本已透過充分測試並可供使用。

一旦您開始使用版本標記倉庫,隨著模組的開發,持續標記新版本非常重要。當用戶請求模組的新版本時(使用 go get -ugo get example.com/hello),go 命令將選擇可用的最高語義釋出版本,即使該版本已存在多年且落後於主分支許多更改。繼續標記新版本將使您的持續改進可供使用者使用。

不要從您的倉庫中刪除版本標籤。如果您發現某個版本存在錯誤或安全問題,請釋出一個新版本。如果人們依賴您已刪除的版本,他們的構建可能會失敗。同樣,一旦您釋出了版本,請不要更改或覆蓋它。模組映象和校驗和資料庫儲存模組、其版本和簽名的加密雜湊值,以確保給定版本的構建在隨時間推移仍然可復現。

v0:初始的不穩定版本

讓我們為模組標記一個 v0 語義版本。v0 版本不提供任何穩定性保證,因此幾乎所有專案在完善其公共 API 時都應從 v0 開始。

標記新版本有幾個步驟

  1. 執行 go mod tidy,這將移除模組可能積累的不再需要的依賴項。

  2. 最後一次執行 go test ./... 以確保一切正常。

  3. 使用 git tag 為專案標記一個新版本。

  4. 將新標籤推送到 origin 倉庫。

$ go mod tidy
$ go test ./...
ok      example.com/hello       0.015s
$ git add go.mod go.sum hello.go hello_test.go
$ git commit -m "hello: changes for v0.1.0"
$ git tag v0.1.0
$ git push origin v0.1.0
$

現在其他專案可以依賴 example.com/hellov0.1.0 版本。對於您自己的模組,您可以執行 go list -m example.com/hello@v0.1.0 來確認最新版本是否可用(此示例模組不存在,因此沒有可用版本)。如果您沒有立即看到最新版本,並且正在使用 Go 模組代理(自 Go 1.13 以來的預設設定),請稍等幾分鐘再試,以便代理有時間載入新版本。

如果您添加了公共 API,對 v0 模組進行了破壞性更改,或者升級了某個依賴項的次要版本或版本,請在下一次釋出時增加 MINOR 版本。例如,v0.1.0 之後的下一個版本將是 v0.2.0

如果您修復了現有版本中的錯誤,請增加 PATCH 版本。例如,v0.1.0 之後的下一個版本將是 v0.1.1

v1:第一個穩定版本

一旦您絕對確定模組的 API 是穩定的,您就可以釋出 v1.0.0v1 主要版本向用戶表明不會對模組的 API 進行不相容的更改。他們可以升級到新的 v1 次要版本和補丁版本,並且他們的程式碼不應該中斷。函式和方法的簽名不會改變,匯出的型別不會被移除等等。如果 API 有變化,它們將是向後相容的(例如,向結構體新增一個新欄位),並將包含在新發布的次要版本中。如果存在錯誤修復(例如,安全修復),它們將包含在補丁版本中(或作為次要版本的一部分)。

有時,保持向後相容性可能會導致 API 設計不太優雅。這沒關係。一個不完美的 API 比破壞使用者的現有程式碼要好。

標準庫的 strings 包是為保持向後相容性而犧牲 API 一致性的一個典型例子。

  • Split 將字串按分隔符分割成所有子字串,並返回這些分隔符之間的子字串切片。
  • SplitN 可以用來控制返回的子字串數量。

然而,Replace 需要一個計數來指定從開頭替換字串的多少個例項(與 Split 不同)。

考慮到 SplitSplitN,您可能會期望有類似 ReplaceReplaceN 的函式。但是,我們不能在不破壞呼叫者程式碼的情況下更改現有的 Replace,而我們承諾過不這樣做。因此,在 Go 1.12 中,我們添加了一個新函式 ReplaceAll。最終的 API 有點奇怪,因為 SplitReplace 的行為不同,但這種不一致比破壞性更改要好。

假設您對 example.com/hello 的 API 很滿意,並且想將 v1 作為第一個穩定版本釋出。

標記 v1 的過程與標記 v0 版本相同:執行 go mod tidygo test ./...,標記版本,然後將標籤推送到 origin 倉庫。

$ go mod tidy
$ go test ./...
ok      example.com/hello       0.015s
$ git add go.mod go.sum hello.go hello_test.go
$ git commit -m "hello: changes for v1.0.0"
$ git tag v1.0.0
$ git push origin v1.0.0
$

此時,example.com/hellov1 API 已確定。這向所有人表明我們的 API 是穩定的,他們應該可以放心使用。

結論

本文詳細介紹了使用語義版本標記模組以及何時釋出 v1 的過程。未來的文章將介紹如何在 v2 及後續版本中維護和釋出模組。

要提供反饋並幫助塑造 Go 依賴項管理的未來,請向我們傳送錯誤報告使用體驗報告

感謝您的所有反饋和幫助改進 Go 模組。

下一篇文章:處理 Go 1.13 中的錯誤
上一篇文章:Go 1.13 釋出
部落格索引