当前位置: 首页 > 工具软件 > GO-YEA > 使用案例 >

发布GoModule(Publishing Go Modules译文)

翟棋
2023-12-01

发布GoModule

英文原版: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"
$

semantic version和module

go.mod中每个必需的module都有一个semantic version,声明用于构建module的依赖关系及最低版本。

semantic version具有这种格式:vMAJOR.MINOR.PATCH

  • 对module的公共API进行向后不兼容的更改时,请升级MAJOR版本。此操作仅应在绝对必要时执行。
  • 在对API进行向后兼容的更改时升级MINOR版本,例如更改依赖关系或添加新的function,method,struct字段或type。
  • 在进行不影响模块公共API或依赖项的细微更改(例如修复bug)之后,升级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 -ugo get example.com/hello),go命令将选择可用的最大semantic version,即使该版本已有数年历史并且在主要分支之后进行了许多更改。持续tag新版本可以使用户可以进行持续的改进。

不要从repo删除版本tag。如果发现某个版本有错误或安全问题,请发布新版本。如果人们依赖于被删除的版本,则其构建可能会失败。同样,发布版本后,请勿更改或覆盖版本。module mirror and checksum database存储module,它们的版本以及签名的加密散列,用于确保给定版本的构建持续保持可复制性。

v0:初始的不稳定版本

用v0的semantic version给module打tag。v0版本没有任何稳定性保证,因此几乎所有项目在改善公共API时都应以v0开始。

打新版本的tag包含如下几个步骤:

  1. 执行go mod tidy来移除没用的依赖。
  2. 执行go get ./...来确保工作正常。
  3. git tag打tag。
  4. 将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/hellov0.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。

v1:第一个稳定版本

如果确定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)。

提供SplitSplitN,可能会期望使用ReplaceReplaceN之类的功能。但是,如果不中断调用者,我们将无法更改现有的替换,我们承诺不会这样做。因此,在Go 1.12中,我们添加了一个新函数ReplaceAll。最终的API有点奇怪,因为SplitReplace的行为不同,但是这种不一致比重大更改要好。

假设您对example.com/hello的API感到满意,并想将v1作为第一个稳定版本发布。

打v1的tag与打v0版本tag的过程相同:运行go mod tidygo 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/hellov1版本API已固化。这传达了API稳定的信息,用户可以放心使用它。

结论

本文大致介绍了给一个module打semantic version的tag的流程,以及何时发布v1版本。下一篇文章会介绍如何维护和发布v2及更高版本的module。

 类似资料: