发布包到公共市场
Go 的「公共市场」不是单独上传中心仓,而是:公开 VCS 仓库 + 语义化 tag,再由默认代理 proxy.golang.org 拉取、校验,并在 pkg.go.dev 展示。别人用 go get your/module@vX.Y.Z 即可依赖。
本篇在「包与模块初识」「工程与依赖管理」之上,走通 可发布模块 → 打 tag → 进代理 / 文档站。
先说结论:发布物是 带版本号的模块源码快照(由 git tag 钉住),不是上传 wheel/sdist。同一 tag 推上远程后内容不可改——错了只能发新版本(或按规范 retract)。
和 Python / PyPI 的差别(心智模型)
| Go | Python(PyPI) | |
|---|---|---|
| 公共入口 | 仓库 URL ≈ 模块路径;代理缓存 | 上传到索引站 |
| 版本来源 | git tag(如 v1.2.3) | pyproject.toml 版本 + 上传 |
| 安装命令 | go get example.com/lib@v1.2.3 | pip install / uv add |
| 文档浏览 | pkg.go.dev | pypi.org 项目页 |
发之前要齐什么
| 项 | 说明 |
|---|---|
| 公开仓库 | GitHub / GitLab 等;默认分支可克隆 |
| 模块路径 | go.mod 的 module 与仓库导入路径一致(如 github.com/you/lib) |
| 可导入 API | 导出标识符(大写);不想被外部用的放 internal/ |
| LICENSE / README | 许可证与说明;影响 pkg.go.dev 展示与合规 |
| 测试通过 | go test ./...;至少保证对外包可构建 |
go mod init github.com/you/example-lib # 路径按真实仓库写
本地练习可用假路径;要公开发布时,模块路径必须能被 go get 解析到该仓库(常见即 github.com/<user>/<repo>)。
最小可发布布局
github.com/you/example-lib/
go.mod
go.sum # 有依赖时
LICENSE
README.md
example.go # package example(或子目录包)
example_test.go
# go.mod
module github.com/you/example-lib
go 1.22
// example.go
package example
// Hello 返回问候语。
func Hello(name string) string {
return "hello, " + name
}
注意:
- 模块路径 决定别人的 import 前缀;改路径等于换模块身份
- 仓库里可以有
cmd/放可执行入口;库代码与main分包,避免把 CLI 和库缠死 - 仅本模块内部用的代码放
internal/,外部go get进不来
版本与 tag
Go 模块版本遵循 语义化版本,tag 带 v 前缀:
git tag v0.1.0
git push origin v0.1.0
# 或:git push origin --tags
| 版本 | 含义(约定) |
|---|---|
v0.x.y | 尚不稳定,允许破坏性变更 |
v1.x.y | 稳定 API;破坏性变更应进下一主版本 |
v2.0.0 起 | 模块路径须带主版本后缀,如 module github.com/you/lib/v2,import 也要 /v2 |
预发布可用 v1.2.3-rc.1 等;正式依赖更常见钉稳定 tag。
发版前建议:
go test ./...
go vet ./...
go mod tidy
确认 go.mod / go.sum 已提交,再打 tag。
让代理与文档站「看见」你
推送 tag 后,可主动请求默认代理拉取(加速出现在缓存与 pkg.go.dev):
# 用代理解析该版本(会触发拉取与校验)
GOPROXY=https://proxy.golang.org GO111MODULE=on \
go list -m github.com/you/example-lib@v0.1.0
也可打开:
https://pkg.go.dev/github.com/you/example-lib@v0.1.0
若暂未出现,稍等或点站内「Request」类入口(以 pkg.go.dev 当前 UI 为准)。校验由 sum.golang.org 提供;他人安装时会核对 go.sum。
他人依赖:
go get github.com/you/example-lib@v0.1.0
import "github.com/you/example-lib"
主版本升级(v2+)
破坏性变更进 v2 时:
go.mod改为module github.com/you/example-lib/v2- 本仓库内 import 同步加
/v2 - 打 tag
v2.0.0(路径与主版本一致)
v0/v1 的模块路径不加 /v1;从 v2 起必须加。这与「只改 tag、不改 path」不同——漏改 path 会导致别人装错或装不上。
发错了怎么办
| 情况 | 做法 |
|---|---|
| tag 内容有 bug | 不要强改已推送的 tag;发 v0.1.1 / v1.2.4 |
| 严重错误版本需阻止使用 | 在 go.mod 使用 retract,再发一个含 retract 声明的新版本 |
| 模块路径写错 | 等于新模块;旧路径上的版本仍按原路径存在 |
# go.mod 示例(在后续版本中声明)
retract v0.1.0 // 说明原因
发布检查表
module路径与公开仓库一致,克隆 URL 可访问go test ./.../go build ./...通过;go mod tidy已做- LICENSE、README、导出 API 的文档注释就绪
- tag 形如
vX.Y.Z,且该 tag 未在远程用过不同内容 v2+已改模块路径与 import- 推送 tag 后用
go list -m ...@vX.Y.Z或 pkg.go.dev 确认 - 私有模块不要指望默认公共代理;用
GOPRIVATE/ 公司代理(本篇只覆盖公共市场)
常见坑
| 现象 | 常见原因 |
|---|---|
go get 找不到版本 | 未推 tag、tag 无 v 前缀、仓库私有 |
| v2 装上后 import 对不上 | 模块路径未加 /v2,或调用方仍写旧 path |
| pkg.go.dev 很久不更新 | 代理尚未拉取;主动 go list -m 或站内请求 |
| 改了历史 tag 别人校验失败 | sumdb 已记录旧哈希;只能发新版本 |
把应用和库揉在一个 main 包 | 外部只能当程序用;库应独立非 main 包 |
要点
- 公共市场 ≈ 公开仓库 +
v*tag + 默认模块代理 / pkg.go.dev go.mod的 module 路径必须对准仓库- tag 不可当可变发布物;错了就升版本(必要时
retract) - v2+ 必须改模块路径后缀
- 日常加依赖、整理模块仍见「工程与依赖管理」