发布流程(中文)¶
本页说明 ztxexp 从改版本号到 PyPI 自动发包的完整链路。发布完全由 git tag 驱动,人工只需要改版本、提交、打 tag,剩下交给 CI。
1. 一次发布要改什么¶
版本号有两个落点,必须同步修改,否则发布前的校验脚本会直接中止流水线:
| 位置 | 字段 | 示例 |
|---|---|---|
pyproject.toml |
[project] version |
version = "1.1.0" |
ztxexp/__init__.py |
__version__ |
__version__ = "1.1.0" |
pyproject.toml 是发行版本的唯一来源,__version__ 是运行时自报的版本,两者与 tag 三方必须一致。
另外还要更新 CHANGELOG.md:新版本号、发布日期、Breaking / Added / Fixed / Docs / Migration 各分类下的条目。README 里的版本历史是给用户看的摘要,可以顺带补一条,但不是发布的硬性前置条件。
2. 本地发布步骤¶
# 1. 改版本号:pyproject.toml 与 ztxexp/__init__.py 两处
# 2. 更新 CHANGELOG.md
# 3. 发布前校验:tag 名、pyproject 版本、__version__ 三方比对
# 脚本用标准库 tomllib 读 pyproject.toml,需要 Python >= 3.11
python3.12 scripts/check_release.py v1.1.0
# 4. 提交改动并打 tag
git add pyproject.toml ztxexp/__init__.py CHANGELOG.md
git commit -m "chore(release): v1.1.0"
git tag v1.1.0
# 5. 推 main 与 tag(tag 推送才会触发发布)
git push origin main
git push origin v1.1.0
python3.12 scripts/check_release.py v1.1.0 只读校验,退出码 0 表示三方一致,1 表示不一致、参数非法或版本信息解析失败。脚本依赖 Python 3.11 起才有的标准库 tomllib,用 3.10 及以下的解释器跑会直接报"需要标准库 tomllib(Python >= 3.11)"并列出当前版本,不会抛一堆 traceback;CI 里 build 作业钉的是 3.12。建议本地先跑一次,避免 tag 推上去才发现版本号漏改。
3. CI 侧的发布链路¶
推 v* 形式的 tag 会触发 .github/workflows/publish.yml,它与 ci.yml 分开:ci.yml 只跑 main 与 PR 的 lint / 测试 / 文档构建,发布不走那里。工作流分三个作业,前一个不通过就不会进入下一个:
build:先校验 tag 格式(必须以v加数字开头),再跑python scripts/check_release.py "$tag"比对 tag、pyproject.toml、ztxexp/__init__.py三方版本,然后python -m build构建 sdist + wheel,twine check dist/*检查元数据,最后把产物作为distartifact 上传。verify:在 Python 3.10–3.13 四个版本的干净环境里只安装 wheel 做冒烟测试(import ztxexp与ztxexp --help)。这一步故意不 checkout 源码,能 import 成功只可能来自打好的包,所以漏打包的文件会在这里暴露。publish:用pypa/gh-action-pypi-publish上传,部署目标为 GitHub Environmentpypi,靠id-token: write换取 PyPI 的 OIDC 短期凭证。这个权限在 workflow 里声明,仓库里不需要任何 PyPI token。
手工补发走 workflow_dispatch:需要显式填一个已经存在的 tag——工作流会把 ref 解析成 refs/tags/<填的 tag>,tag 不在仓库里连 checkout 都过不去。所以补发不是凭空发一个新版本,它只是把某次 tag 发布的链路重跑一遍,版本校验与推 tag 完全相同,没有绕过校验直接上传的入口。补发时用这个入口,不要重推同名 tag。
4. 一次性仓库配置(只做一次)¶
Trusted Publishing 靠 OIDC 信任关系代替 API token,所以第一次发布前要在两边各登记一次:
- PyPI 项目页
Publishing→Add a new pending publisher,填写: - Owner:
ztxtech - Repository:
ztxexp - Workflow name:
publish.yml - Environment name:
pypi - GitHub 仓库
Settings→Environments新建pypi,不需要填任何 secret。
完成后不再需要 PYPI_API_TOKEN、TWINE_USERNAME、TWINE_PASSWORD 一类的长期凭证,仓库里也不要再存 PyPI token。若之后改了发布工作流的文件名或 environment 名,PyPI 那边的登记要同步改,否则 OIDC 换取凭证会被拒绝。
5. 常见问题¶
tag 推上去了但流水线报版本不一致:check_release.py 比对的是 tag 名、[project] version、__version__ 三处,最常见的是漏改 __init__.py。补一次提交、删掉旧 tag 重新打即可:
想重发同一个版本:PyPI 不允许覆盖已上传的版本,只能升版本号后重新发。本地 dist/ 随便删,已上传的文件不能覆盖。发布工作流也没有加并发锁去"放开重跑",因为真正的约束在 PyPI 侧:同名同版本第二次上传必然被拒。
只想验证、不想发布:推 main 分支即可,ci.yml 会跑 lint、多版本测试、mkdocs build --strict、python -m build 与 twine check,覆盖发布前的全部检查,只是没有上传步骤。