Go 博客
发布 Go 模块
简介
这篇文章是系列文章的第 3 部分。
- 第 1 部分 — 使用 Go 模块
- 第 2 部分 — 迁移到 Go 模块
- 第 3 部分 — 发布 Go 模块(本文)
- 第 4 部分 — Go 模块:v2 及其之后
- 第 5 部分 — 保持您的模块兼容
注意:有关开发模块的文档,请参阅 开发和发布模块。
本文讨论如何编写和发布模块,以便其他模块可以依赖它们。
请注意:本文涵盖了v1
及其之前的开发。如果您对v2
感兴趣,请参阅 Go 模块:v2 及其之后。
本文在示例中使用 Git。 Mercurial、Bazaar 等也受支持。
项目设置
对于本文,您需要一个现有的项目作为示例。因此,从 使用 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
版本,例如修复 bug。
您可以通过附加连字符和点分隔的标识符来指定预发布版本(例如,v1.0.1-alpha
或 v2.2.2-beta.2
)。go
命令优先使用正常版本而不是预发布版本,因此用户必须明确要求预发布版本(例如,go get example.com/[email protected]
),前提是您的模块有任何正常版本。
v0
主版本和预发布版本不保证向后兼容性。它们允许您在对用户进行稳定性承诺之前完善您的 API。但是,v1
主版本及更高版本要求在该主版本内保持向后兼容性。
go.mod
中引用的版本可以是存储库中标记的显式版本(例如,v1.5.2
),也可以是基于特定提交的 伪版本(例如,v0.0.0-20170915032832-14c0d48ead0c
)。伪版本是预发布版本的一种特殊类型。当用户需要依赖尚未发布任何语义版本标记的项目,或者针对尚未标记的提交进行开发时,伪版本很有用,但用户不应假设伪版本提供稳定的或经过充分测试的 API。使用显式版本标记您的模块,向您的用户表明特定版本已完全测试并可以随时使用。
一旦您开始使用版本标记您的存储库,随着您开发模块,就必须继续标记新版本。当用户请求您模块的新版本(使用go get -u
或 go get example.com/hello
)时,go
命令将选择可用的最大语义发布版本,即使该版本是几年前的版本,并且与主分支相差很多更改。继续标记新版本将使您的持续改进对您的用户可用。
不要从您的存储库中删除版本标记。如果您发现某个版本存在 bug 或安全问题,请发布新版本。如果您删除了用户依赖的版本,他们的构建可能会失败。同样,一旦您发布某个版本,请勿更改或覆盖它。 模块镜像和校验和数据库存储模块、它们的版本和签名的加密哈希,以确保给定版本的构建随着时间的推移保持可重现。
v0:初始的不稳定版本
让我们使用v0
语义版本标记模块。v0
版本不提供任何稳定性保证,因此几乎所有项目都应该从v0
开始,因为它们会完善它们的公共 API。
标记新版本需要几个步骤
-
运行
go mod tidy
,这将删除模块可能累积的任何不再需要的依赖项。 -
最后一次运行
go test ./...
以确保一切正常。 -
使用
git tag
标记项目的新版本。 -
将新标记推送到源存储库。
$ 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/hello
的v0.1.0
。对于您自己的模块,您可以运行go list -m example.com/[email protected]
以确认最新版本可用(此示例模块不存在,因此没有版本可用)。如果您没有立即看到最新版本,并且您使用的是 Go 模块代理(自 Go 1.13 以来默认),请在几分钟后重试,让代理有时间加载新版本。
如果您向公共 API 添加内容,对v0
模块进行重大更改,或升级依赖项的次要或版本,请为您的下一个版本增加MINOR
版本。例如,在v0.1.0
之后的下一次发布将是v0.2.0
。
如果您修复了现有版本中的 bug,请增加PATCH
版本。例如,在v0.1.0
之后的下一次发布将是v0.1.1
。
v1:第一个稳定版本
一旦您完全确定模块的 API 稳定,就可以发布v1.0.0
。v1
主版本向用户表明,不会对模块的 API 进行任何不兼容的更改。他们可以升级到新的v1
次要版本和补丁版本,他们的代码应该不会中断。函数和方法签名不会更改,导出的类型不会被删除,等等。如果对 API 进行了更改,它们将是向后兼容的(例如,向结构体添加新字段),并将包含在新的小版本中。如果有 bug 修复(例如,安全修复),它们将包含在补丁版本中(或作为小版本的一部分)。
有时,保持向后兼容性会导致奇怪的 API。这没关系。不完美的 API 比破坏用户的现有代码要好。
标准库的strings
包是保持向后兼容性以牺牲 API 一致性的一个主要例子。
但是,Replace
从开头获取要替换的字符串实例的数量(与Split
不同)。
鉴于Split
和SplitN
,您会期望出现类似Replace
和ReplaceN
的函数。但是,我们无法更改现有的Replace
,而不会破坏调用者,而这是我们承诺不会做的事情。因此,在 Go 1.12 中,我们添加了一个新函数,ReplaceAll
。由于Split
和Replace
的行为不同,因此生成的 API 有点奇怪,但这种不一致性比重大更改要好。
假设您对example.com/hello
的 API 感到满意,并且您希望将v1
作为第一个稳定版本发布。
标记v1
使用与标记v0
版本相同的过程:运行go mod tidy
和go 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/hello
的v1
API 已固定。这向所有人表明我们的 API 稳定,他们应该可以放心地使用它。
结论
本文介绍了使用语义版本标记模块以及何时发布v1
的过程。以后的文章将介绍如何在v2
及其之后维护和发布模块。
为了提供反馈并帮助塑造 Go 中依赖项管理的未来,请向我们发送 bug 报告 或 使用体验报告。
感谢您提供的所有反馈和帮助改进 Go 模块。
下一篇文章:在 Go 1.13 中使用错误
上一篇文章:Go 1.13 发布
博客索引