Skip to content

版本管理 ​

版本策略 ​

项目遵循语义化版本 2.0.0,当前处于 0.x 开发阶段。

版本递增规则 ​

遵循 semver 2.0.0,0.x 开发阶段同样适用:

提交类型递增位示例
feat(新功能)MINOR0.7.0 → 0.8.0
fix / perf(修复/优化)PATCH0.7.0 → 0.7.1
其余(docs/ci/chore/refactor/test/build/style)不触发发版—
大版本由发布者手动决定1.0.0 及以后

升级到 1.0.0 后的配置变更 ​

配置项当前值1.x 值
major_on_zerofalsetrue

版本管理自动化 ​

python-semantic-release ​

采用 python-semantic-release v10 实现全自动发版:

  • 触发:push 到 main
  • 解析:Conventional Commits(feat:/fix:/perf:/…)
  • 自动执行:bump 版本 → 更新 CHANGELOG → 打 tag → 创建 GitHub Release → 打包上传安装包

版本源 ​

文件作用谁维护
core/version.py唯一版本源 __version__ = "0.7.0"python-semantic-release 自动改写
pyproject.toml [project].version包元数据版本python-semantic-release 自动改写
uv.lock 本项目条目锁文件里的项目版本(package = false,仅记录)uv lock / uv sync 同步
CHANGELOG.md更新日志(Keep-a-Changelog 格式)python-semantic-release 自动插入
git tag v{version}发版标记python-semantic-release 自动创建

⚠️ version_toml 必须写点号路径:pyproject.toml:project.version([project] version 在 dotty 里是 project.version)。 2026-09-26 前这里写的是 pyproject.toml:version —— PSR 的 TomlVersionDeclaration.replace() 在 key 不存在时直接返回原文(其源码里标着 # TODO: v11 raise KeyError),于是静默跳过: pyproject / uv.lock 长期停在 0.24.2,而 core/version.py 与 CHANGELOG 已到 0.25.0, 期间没有任何检查发现(check_version.py 当时只比对后两者)。 现已修正路径,并让 check_version.py 把 pyproject 一并纳入校验。

一致性校验 ​

scripts/check_version.py 在 CI 中阻断合并:

core/version.py.__version__ == CHANGELOG.md 最新版本段
                           == pyproject.toml [project].version
                           == git tag(发版态)
  • 开发态(HEAD 无 tag):校验 version.py == CHANGELOG == pyproject.toml
  • 发版态(HEAD 有 tag):四源必须完全一致

uv.lock 不纳入硬校验:PSR 只改写 version_variables / version_toml 列出的文件, 锁文件的项目版本要等下一次 uv lock / uv sync 才跟上。它只是记录([tool.uv] package = false, 本项目不打包成 wheel),所以「锁文件版本滞后」不影响安装结果,不值得为它增加发布流程步骤。

发版流程 ​

关键:semantic-release 使用 GITHUB_TOKEN push,不会再次触发 workflow(防止死循环)。

「手改版本号的小版本」这条回退路径的说明从哪来 ​

PSR 判定「无需发版」(只有 docs/ci/chore 之类提交,或版本号已手动改好并打上 tag)时, release.yml 回退用 core/version.py 的版本打包,Release 由 action-gh-release 创建。 ⚠️ 这个 action 没有 body 时会把提交信息当 Release 说明 —— 2026-10-06 实测 v0.26.0 页面被贴上整段提交正文(异动榜那份长文),所以回退分支现在显式跑 scripts/release_notes.py <版本> release_notes.md,从 CHANGELOG.md 抽出该版本段当说明; PSR 正常发版那条路径 body_path 留空,保留 PSR 自己写好的说明(实测不会被清掉)。

本地一步发版(不依赖 CI) ​

CI 那条路(push main → release.yml)是常规路径。需要在本地一次做完(改版本 → CHANGELOG → commit → tag → push → 建 GitHub Release)时:

powershell
# 中文 Windows 必须带 PYTHONUTF8:PSR 按 GBK 解码,读 UTF-8 的提交信息时会崩
# ('gbk' codec can't decode byte 0x80 in position ...,2026-10-01 实测)
$env:PYTHONUTF8 = "1"
# token 只放进**当前会话**的环境变量,用完随 shell 消失。
# 不要写进 $PROFILE / .bashrc / .env —— 那才是落盘。
$env:GH_TOKEN   = (gh auth token)

.\.venv\Scripts\semantic-release.exe version    # bump + CHANGELOG + commit + tag + push
.\.venv\Scripts\semantic-release.exe publish    # 用 CHANGELOG 当 notes 建 GitHub Release
  • version 自己会 push(含 tag),不需要再 git push --tags
  • ⚠️ uv.lock 不会被改写(它不在 version_variables / version_toml 里)→ 发版后补一次: uv lock,或手工改「本项目条目」的 version,再单独提交一次 chore(release): uv.lock 同步到 x.y.z
  • ⚠️ 不要加 -vv:详细日志可能把请求头里的 token 打进终端
  • 自检(只算版本、不改任何文件,安全):semantic-release version --print
  • 没有 GH_TOKEN 时的退路:version --no-push → git push origin main --tags → gh release create v{version} --title "v{version}" --notes-file <CHANGELOG 里那一段> (gh 用系统凭据库里的登录态,与本条等价,只是多一次手动调用)

手动发版 Checklist ​

如需手动发版(不通过 semantic-release):

  1. 修改 core/version.py 的 __version__
  2. 同步 pyproject.toml 的 [project].version(别再靠 PSR 之外的路径补)
  3. 更新 CHANGELOG.md(在 <!-- version list --> 下方添加新版本段)
  4. 执行 uv lock 让 uv.lock 的项目版本跟上(uv sync 也会做)
  5. 执行 python scripts/check_version.py 确认一致
  6. python build_release.py 打包
  7. git commit + git tag v{version} + git push --tags