publish go module

Publishing Go Modules

这篇的核心不是“怎么上传包”,而是:Go module 的发布 = 在代码仓库里打语义化版本 tag。

Go 没有 go publish 这种命令。别人能安装你的 module,关键取决于:

  1. 代码仓库可访问
  2. go.mod 里的 module path 正确
  3. 仓库里打了符合 Go Modules 规则的版本 tag

1. 发布前的基本项目状态

一个可发布的 module 至少应该有:

  • go.mod
  • go.sum
  • 源码文件
  • 测试文件
  • LICENSE

例如:

module example.com/hello

go 1.12

require rsc.io/quote/v3 v3.1.0

然后初始化 Git 仓库:

git init
git add LICENSE go.mod go.sum hello.go hello_test.go
git commit -m "hello: initial commit"

重点:Go module 的版本来自 VCS tag,常见就是 Git tag。

Go 官方文档也说明,开发给别人使用的 module 时,发布版本就是在仓库中给 module 打版本号 tag。

2. 版本号规则:vMAJOR.MINOR.PATCH

Go Modules 使用语义化版本:

vMAJOR.MINOR.PATCH

例如:

v1.2.3

含义是:

MAJOR = 主版本
MINOR = 次版本
PATCH = 修订版本

升级规则:

类型 什么时候改 例子
MAJOR 破坏兼容性 v1.4.2v2.0.0
MINOR 向后兼容地新增能力 v1.4.2v1.5.0
PATCH 修 bug,不改 API v1.4.2v1.4.3
  • 破坏兼容升 MAJOR;
  • 新增能力升 MINOR;
  • 修 bug 升 PATCH。

3. v0:初始不稳定版本

大多数项目应该从 v0 开始。

例如:

v0.1.0

v0 的含义是:API 还不稳定,不承诺向后兼容。

所以在 v0 阶段,你还可以调整 API、重命名函数、修改结构体设计。

发布 v0.1.0 的流程:

go mod tidy
go test ./...

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 main
git push origin v0.1.0

然后别人可以这样安装:

go get example.com/hello@v0.1.0

如果只是想安装最新版本:

go get example.com/hello@latest

4. v1:第一个稳定版本

当你确认 API 稳定后,可以发布:

v1.0.0

v1 的含义是:从这里开始,同一个 major version 内必须保持向后兼容。

也就是说,发布了 v1.0.0 之后:

  • v1.1.0 不能删掉已有公开函数
  • v1.2.0 不能随便改已有函数签名
  • v1.3.0 不能移除已有导出的类型

可以做的是:

  • 新增函数
  • 新增方法
  • 新增结构体字段
  • 修复 bug
  • 优化实现

不应该做的是:

  • 删除 public API
  • 修改 public API 的含义
  • 修改函数签名导致用户代码编译失败

发布 v1.0.0 的流程和 v0 一样:

go mod tidy
go test ./...

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 main
git push origin v1.0.0

然后别人可以安装:

go get example.com/hello@v1.0.0

5. pre-release:预发布版本

Go Modules 支持预发布版本:

v1.0.1-alpha
v2.2.2-beta.2

安装时需要显式指定:

go get example.com/hello@v1.0.1-alpha

原因是:如果同时存在正式版本和预发布版本,go get @latest 默认优先选择正式版本。

预发布版本适合测试,但不应该被当成稳定 API。

6. pseudo-version:伪版本

如果某个仓库没有正式 tag,Go 也可以依赖某个具体 commit。

这时版本可能长这样:

v0.0.0-20170915032832-14c0d48ead0c

这叫 pseudo-version,伪版本。

它的作用是:在没有正式语义化版本 tag 的情况下,让别人依赖某个具体 commit。

但它不是稳定发布版本。

所以对 module 作者来说:正式发布时应该打明确版本 tag,而不是让用户长期依赖 pseudo-version。

Go Modules Reference 也说明,pseudo-version 通常用于引用还没有语义化版本 tag 的 revision,或在打 tag 前测试某个 commit。

7. 不要删除或改写已经发布的 tag

这是这篇文章里很重要的一点。

一旦发布了:

v1.0.0

就不要再做这些事:

git tag -d v1.0.0
git push origin :refs/tags/v1.0.0

也不要重新打同名 tag 指向另一个 commit。

原因是:Go module proxy 和 checksum database 会记录 module 版本及其内容哈希。

同一个版本应该永远对应同一份代码。

如果你发现 v1.0.0 有 bug,正确做法不是改 v1.0.0,而是发布新版本:

v1.0.1

如果是安全修复,也发布新 patch 版本:

v1.0.2

一句话:版本 tag 一旦发布,就应该视为不可变。

8. 发布后的验证

发布 tag 后,可以用:

go list -m example.com/hello@v0.1.0

或:

go list -m example.com/hello@latest

确认 Go 是否能发现这个版本。

如果刚推送 tag 后查不到,可能是 Go module proxy 还没同步。

Go 1.13 开始默认使用 Go module proxy,所以新 tag 可能需要等一小段时间才可见。

9. 用户如何安装你的 module

别人使用你的 module,一般是在自己的项目里执行:

go get example.com/hello@latest

或指定版本:

go get example.com/hello@v1.0.0

然后在代码里 import:

import "example.com/hello"

如果你的 module path 是 GitHub 地址,通常类似:

go get github.com/user/project@v1.0.0

代码里:

import "github.com/user/project"

10. 发布前检查清单

每次发布前,按这个顺序做:

go mod tidy
go test ./...
git status
git add .
git commit -m "xxx"
git tag vX.Y.Z
git push origin main
git push origin vX.Y.Z

含义:

go mod tidy     清理 go.mod / go.sum
go test ./...   确保所有 package 测试通过
git commit      固化发布内容
git tag         声明 module 版本
git push tag    让外部用户可获取这个版本

评论