英文原版:https://blog.golang.org/publishing-go-modules
这是系列文章的第三部分
本文讨论如何编写和发布module,以便其他module可以依赖它们。
请注意:这篇文章涵盖了v1及以下版本的开发。如果您对v2感兴趣,请参阅GoModule: v2及新版本。
本文在示例中使用Git,Mercurial,Bazaar等同样支持。
本文将把使用GoModule文章末尾的文件作为一个现有项目当示例:
$ 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仓库并添加一个初始commit。如果要发布自己的项目,请确保包含LICENSE文件。切换到包含go.mod的目录并创建repo:
$ git init
$ git add LICENSE go.mod go.sum hello.go hello_test.go
$ git commit -m "hello: initial commit"
$
go.mod
中每个必需的module都有一个semantic version
,声明用于构建module的依赖关系及最低版本。
semantic version
具有这种格式:vMAJOR.MINOR.PATCH
。
MAJOR
版本。此操作仅应在绝对必要时执行。MINOR
版本,例如更改依赖关系或添加新的function,method,struct字段或type。PATCH
版本您可以通过附加连字符和点分隔的标识符来指定预发布版本(例如v1.0.1-alpha
或者v2.2.2-beta.2
)。go命令会优先选择普通发布版本而不是预发布版本,所以使用预发布版本时需要显式指定版本号(例如go get example.com/hello@v1.0.1-alpha
)。
v0的major版本和预发行版本不保证向后兼容。它们使您可以在对用户提供稳定版本之前优化API。但是v1的major版本及更高版本要求该major版本内能够向后兼容。
go.mod
中引用的本即可能是代码库中的显式版本(如v1.5.1)也可能是基于一次commit的Pseudo-version
(如v0.0.0-20170915032832-14c0d48ead0c)。Pseudo-version
是一中特殊的预发布版本类型。
当用户需要依赖尚未发布semantic version
标签的项目或针对尚无tag的提交进行开发时,Pseudo-version
很有用,但用户不应假定Pseudo-version
可以提供稳定或经过测试的API。使用显式版本tag的module表明该版本已经过全面测试并可以使用。
一旦开始用版本对repo打tag,在开发module时应持续tag新版本。当用户请求module的新版本时(使用go get -u
或go get example.com/hello
),go命令将选择可用的最大semantic version
,即使该版本已有数年历史并且在主要分支之后进行了许多更改。持续tag新版本可以使用户可以进行持续的改进。
不要从repo删除版本tag。如果发现某个版本有错误或安全问题,请发布新版本。如果人们依赖于被删除的版本,则其构建可能会失败。同样,发布版本后,请勿更改或覆盖版本。module mirror and checksum database
存储module,它们的版本以及签名的加密散列,用于确保给定版本的构建持续保持可复制性。
用v0的semantic version
给module打tag。v0版本没有任何稳定性保证,因此几乎所有项目在改善公共API时都应以v0开始。
打新版本的tag包含如下几个步骤:
go mod tidy
来移除没用的依赖。go get ./...
来确保工作正常。git tag
打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/hello@v0.1.0
确认最新版本可用(该示例模块不存在,因此没有可用的版本)。如果没有立即看到最新版本,并且使用的是GoModuleProxy(Go 1.13以来的默认设置),那么过一会儿再重试,代理可能需要时间加载新版本。
如果添加到公共API,对v0版本的module进行重大更改,或升级其中一个依赖项的minor版本,请为下一个发行版升级MINOR
版本。例如,v0.1.0之后的下一个发行版将是v0.2.0。
如果修复了现有版本中的错误,请升级PATCH
版本。例如,v0.1.0之后的下一个发行版将是v0.1.1。
如果确定module的API稳定,就可以发布v1.0.0。v1的major版本不会对module的API进行任何不兼容的更改。用户可以升级到新v1的minor版本和patch版本,并且其代码不会故障。function和method的签名不会更改,不会删除export的类型等。如果对API进行了更改,且它们能向后兼容(例如,向struct添加新字段),将包含在新的minor版本中。如果存在bug修复(例如,安全修复),它们将包含在patch发行版中(或作为minor发行版的一部分)。
有时,保持向后兼容性可能会导致API笨拙。不过没关系。不完善的API比破坏用户的现有代码更好。
Split
一个string会将其拆分为所有由分隔符分隔的子串,并返回这些分隔符之间的子串。SplitN
可用于控制要返回的子字符串的数量。但是,Replace
从一开始就计算了要替换的字符串个数(不像Split
)。
提供Split
和SplitN
,可能会期望使用Replace
和ReplaceN
之类的功能。但是,如果不中断调用者,我们将无法更改现有的替换,我们承诺不会这样做。因此,在Go 1.12中,我们添加了一个新函数ReplaceAll
。最终的API有点奇怪,因为Split
和Replace
的行为不同,但是这种不一致比重大更改要好。
假设您对example.com/hello
的API感到满意,并想将v1作为第一个稳定版本发布。
打v1的tag与打v0版本tag的过程相同:运行go mod tidy
与go get ./...
,打tag,然后将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 v1.0.0"
$ git tag v1.0.0
$ git push origin v1.0.0
$
此时,example.com/hello
的v1
版本API已固化。这传达了API稳定的信息,用户可以放心使用它。
本文大致介绍了给一个module打semantic version
的tag的流程,以及何时发布v1版本。下一篇文章会介绍如何维护和发布v2及更高版本的module。