publish go module
这篇的核心不是“怎么上传包”,而是:Go module 的发布 = 在代码仓库里打语义化版本 tag。
Go 没有 go publish 这种命令。别人能安装你的 module,关键取决于:
- 代码仓库可访问
- go.mod 里的 module path 正确
- 仓库里打了符合 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.2 → v2.0.0 |
| MINOR | 向后兼容地新增能力 | v1.4.2 → v1.5.0 |
| PATCH | 修 bug,不改 API | v1.4.2 → v1.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 让外部用户可获取这个版本
评论