写点什么

发布 Go Modules

用户头像
Rayjun
关注
发布于: 2021 年 01 月 31 日
发布 Go Modules

每当完成一个 Go Modules 之后,如果想让更多的人使用你的代码,就需要把这个模块发布出去,这篇文章说明了如何发布一个新的模块。


原文地址:https://blog.golang.org/publishing-go-modules



简介

这个系列的文章总共有五篇,这是第三篇:


这篇文章讨论如何编写和发布模块,让其他模块可以使用它们。


请注意:这篇文章只覆盖到 v1 版本的开发。如果你对 v2 版本感兴趣,请看 Go Modules: v2 及后续版本


这篇文章中使用 git 版本管理为例子。Mercurial、Bazaar 等其他的版本管理工具也是支持的。


创建项目

在这篇文章中,你需要用一个已经存在的项目作为例子。在这里,继续使用第一篇文章的代码来演示:

$ cat go.modmodule example.com/hello
go 1.12
require rsc.io/quote/v3 v3.1.0
$ cat go.sumgolang.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.gopackage hello
import "rsc.io/quote/v3"
func Hello() string { return quote.HelloV3()}
func Proverb() string { return quote.Concurrency()}
$ cat hello_test.gopackage 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 进行向后不兼容的更改时,才增加主版本。只有在必要的时候才这样做。

  • 当对 API 进行向后兼容更改时,如更改依赖项或添加新函数、方法、结构字段或类型,则增加次版本。

  • 在做了不影响模块的公共 API 或依赖项(如修复 bug)的小更改后增加补丁版本。


你可以通过附加连字符和点分隔的标识符来指定预发布版本(例如,v1.0.1-alpha 或 v2.2.2-beta.2)。go 命令优先使用普通版本而不是预发布版本,所以如果你的模块已经存在普通版本,用户如果要使用预发布版本,必须明确指定版本号(例如,去获取 example.com/hello@v1.0.1-alpha)。


v0 主版本和预发布版本不保证向后兼容。它们允许你对 API 进行优化,不需要向用户做出稳定版本承诺。然而,v1 主版本和其他版本需要在该主版本内向后兼容。


在 go.mod 中引用的版本可能是在存储库中标记的显式发布(例如,v1.5.2),也可能是基于特定提交的 pseudo-version(例如,v0.0.0-20170915032832-14c0d48ead0c)。pseudo-version 是一种特殊类型的预发布版本。当用户需要依赖一个没有发布任何语义版本标记的项目,或者针对一个还没有打标记的提交进行开发时,伪版本是有用的。但用户不应该假定 pseudo-version 提供了一个稳定的或经过良好测试的 API。用显式的版本号标记模块,向用户表明特定的版本已经经过了充分的测试,可以使用了。


一旦你开始用版本号标记你的仓库,在后续模块的开发中,就应该继续标记新版本。当用户请求模块的新版本(使用 go get -u 或 go get example.com/hello)时,go 命令将选择可用的最大语义版本,即使该版本已经存在好几年了,并且主分支后面有很多变化。继续标记新版本将使你的用户能够获得持续的改进。


不要从你的仓库中删除版本标签。如果你发现了一个版本的 bug 或安全问题,那么就发布一个新版本。如果人们依赖于你已经删除的版本,那么他们的构建可能会失败。类似地,一旦你发布了一个版本,不要更改或覆盖它。模块镜像和校验和数据库存储模块的版本和签名加密散列,以确保给定版本的构建在一段时间内是不变的。


v0:初始,不稳定版本

让我们用 v0 语义版本标记这个模块。v0 版本不做任何稳定性保证,所以几乎所有的项目都应该从 v0 开始,以完善公共 API。


标记一个新版本有如下步骤:

  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/hello 的 v0.1.0 版本。对于你自己的模块,可以运行 go list -m example.com/hello@v0.1.0 来确认最新可用版本(该示例模块不存在,因此没有可用的版本)。如果你没有立即看到最新版本,并且正在使用 Go 模块代理(自 Go 1.13 以来的默认设置),可以在几分钟内再次尝试,给代理一点时间来加载新版本。


如果向公共 API 添加内容,对 v0 版本的模块进行破坏性更改,或者升级某个依赖项的次版本,则为下一个发行版增加次要版本。例如,v0.1.0 之后的下一个版本将是 v0.2.0。


如果修复了现有版本中的 bug,则增加补丁版本。例如,v0.1.0 之后的下一个版本将是 v0.1.1。


v1:第一个稳定版本

一旦你确定你的模块 API 已经稳定了,你就可以发布 v1.0.0 了。v1 主版本告诉用户,不会对模块的 API 进行不兼容的更改。他们可以升级到新的 v1 次要版本和补丁版本,他们的代码应该不会出错。函数和方法签名不会改变,导出的类型不会被删除等等。如果 API 发生了更改,它们将向后兼容(例如,向结构添加新字段),并将包含在新的次版本中。如果修复了错误(例如,安全修复),它们将包含在补丁版本中(或者作为次版本的一部分)。


有时,保持向后兼容性会导致 API 不优雅。但没关系,一个不完美的 API 总比破坏用户现有的代码要好。


标准库的 strings 包就是一个以 API 一致性为代价来维护向后兼容性的典型例子。


  • Split 函数将字符串分割成由分隔符分隔的子字符串,并返回一个由这些分隔符分割的子字符串 slice

  • SplitN 函数可以用来控制要返回的子字符串的数量


但是,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 及后续版本的模块。


请向我们发送bug 报告体验报告,帮助我们改善 Go 的依赖管理功能。


感谢你所有的反馈和帮助改进模块的建议。



另外,腾讯云区块链方向在大量招人,包括前端、后端、架构师、产品等诸多岗位,如果感兴趣,请把简历投过来 rayjun0412@gmail.com


译 / Rayjun

本文首发于微信公众号


欢迎关注我的微信公众号


发布于: 2021 年 01 月 31 日阅读数: 20
用户头像

Rayjun

关注

程序员,王小波死忠粉 2017.10.17 加入

非著名程序员,还在学习如何写代码,公众号同名

评论

发布
暂无评论
发布 Go Modules