Go 部落格

釋出 Go 模組

Tyler Bui-Palsulich
2019 年 9 月 26 日

引言

此文章是系列文章的第 3 部分。

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

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

請注意:此文章涵蓋了從開發到 v1 的內容。如果您對 v2 感興趣,請參閱Go 模組:v2 及更高版本

此文章在示例中使用 Git。也支援 MercurialBazaar 和其他版本控制系統。

專案設定

對於此文章,您需要一個現有的專案作為示例。因此,從使用 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. 將新標籤推送到原始倉庫。

$ 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 ./...,標記版本,並將標籤推送到原始倉庫。

$ 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 釋出
部落格索引