Anki 版本发布全流程指南:从 prepare 脚本到 GitHub Actions 的自动化发布管线 Anki 版本发布全流程指南从 prepare 脚本到 GitHub Actions 的自动化发布管线【免费下载链接】ankiAnki is a smart spaced repetition flashcard program项目地址: https://gitcode.com/GitHub_Trending/an/anki导读Anki间隔重复记忆卡片程序的版本发布并不是手工打 tag、上传安装包的简单操作而是一条**「本地脚本准备 CI 自动验证 GitHub Actions 跨平台构建发布」**的自动化流水线。本文以仓库中的官方发布文档 docs/releasing.md 为主体结合 tools/prepare_release.py、.github/workflows/release.yml、release.just 等源码完整讲解 Anki 的版本号规范、release/YY.MM分支工作流、发布参数矩阵、环境审批门禁、测试发布方法以及独立的anki-audio音频包发布流程。读完后你将能理解并复现 Anki 从开发分支到 PyPI/GitHub Release 的完整发布链路。发布流水线总览Anki 的版本发布由两部分组成tools/prepare_release.py本地执行—— 由维护者在自己的机器上运行负责校验版本号、切换对应 release 分支、检查 CI 是否通过、确认没有重复的 tag 或 release、同步翻译文件最后提交更新后的.version并推送分支.github/workflows/release.ymlCI 执行—— 等分支上的 CI 通过后手动触发workflow_dispatch负责构建全平台安装包和 wheel并可选地对 macOS/Windows 产物签名、创建 GitHub 草稿 Release、发布 wheel 到 TestPyPI 与 PyPI。两个阶段的分工可以这样理解prepare 脚本负责决定发什么版本并产出带版本号的提交release 工作流负责把该提交变成真正的发布物。release.yml使用了名为release的 concurrency group且cancel-in-progress: false因此同一时间只允许一个发布构建运行不会出现两个发布互相覆盖的情况。流程时序图Prepare 脚本的环境要求tools/prepare_release.py在维护者本机运行因此对环境有明确要求要求说明干净的工作区脚本第一步执行git status --porcelain检查存在未提交变更会直接报错please commit any outstanding changes first已认证的ghCLI需要 push 权限访问ankitects/anki以及两个翻译仓库ftl/core-repo/core与ftl/qt-repo/desktoprsync在 PATH 中用于把翻译模板拷贝到翻译仓库带packaging模块的 Python项目通过uv环境提供该模块从源码看脚本的sync_translations()会先对两个翻译模块执行git checkout maingit pull origin main拉取最新翻译再用rsync -ai --delete把ftl/core、ftl/qt下的模板同步过去有变更则提交并推送Update templates。脚本在提交.version之前就会推送翻译模板提交所以如果这一步之后失败翻译仓库的推送已经生效但重新运行脚本是安全的——当没有新内容需要拷贝时翻译步骤是空操作git commit遇到 nothing to commit 会静默跳过。版本号格式日历化 PEP 440Anki 采用日历版本calendar versioning并遵循 PEP 440 规范。仓库根目录的.version文件当前内容为26.08.1格式规则由 tools/validate_version.py 中的正则^\d\.(\d{2})(\.\d)?(a\d|b\d|rc\d)?$强制约束YY.MM稳定版如26.04可选.patch如26.04.1预发布后缀a1alpha、b1beta、rc1release candidate月份必须两位补零\d{2}且校验月份 ≤ 12即26.05合法、26.5非法。官方文档给出的示例26.05b1beta、26.05rc1候选版、26.05稳定版、26.05.1补丁版。validate_version还会用packaging.version.Version做 PEP 440 解析并强制要求新版本号必须大于当前.version否则抛出version must be greater than current ...。prepare_release.py中的release_branch()从版本号推导分支名取base_version的release部分并把月份补零后拼成release/YY.MM例如26.08b1→release/26.08。因此脚本没有 branch 参数分支完全由版本号决定。Release 分支工作流所有版本都从release/YY.MM分支切出。分支名只含主版本号YY.MM不含预发布后缀——同一个周期的 beta、rc、稳定版都从同一条分支产出。标准发布流程# 1. 从 main 创建 release 分支并推送 git checkout -b release/26.05 main git push origin release/26.05 # 2. CI 在 push 到 release/** 分支时自动运行 # 3. 准备发布在分支上更新 .version just release::prepare --version 26.05b1 # 4. 在 TestPyPI 上验证 just release::testpypi --ref release/26.05 # 5. 发布完整版本 just release::public --ref release/26.05 # 6. 同一周期的后续预发布或稳定版用新版本号重复步骤 3-5 # 例如 26.05b2、26.05rc1、26.05 # 7. 稳定版发布后把 release 分支合并回 main带回 .version 更新和 cherry-pick 的修复 git checkout main git merge release/26.05 git push origin main # 8. 稳定版发布后删除 release 分支安全修复与热修复hotfix发布安全修复流程与标准流程的关键差异先建 Security Advisory 临时私有 fork在私有 fork 里走正常 PR 流程修 bug在修复就绪前不公开 PR 也不发布 advisory。修复就绪后的步骤# 1. 从最新 release tag 创建 release 分支 git checkout -b release/26.05 26.05 # 2. 把修复 cherry-pick 到 release 分支 # 3. 推送分支并等待 CI git push origin release/26.05 # 4. 准备并发布 just release::prepare --version 26.05.1 just release::public --ref release/26.05 # 5. 合并回 main # 6. 安全补丁发布 advisory 并视情况致谢报告者热修复场景下 prepare 脚本可以通过--skip-ci-check跳过 CI 检查release.yml的skip-ci-check输入项在注释中明确写明for hotfix releases from non-main branches。Release 工作流的任务拓扑.github/workflows/release.yml的prepare作业首先运行三个门禁检查拒绝不兼容输入draft-releasetrue且signfalse时报错Draft releases must be signed、校验版本与.version是否一致、检查 CI 结论和重复 tag/release。随后各平台构建作业并行执行值得注意的实现细节来自 release.yml 源码macOS ARM/Intel 分成两个独立 job 而非矩阵注释说明二者很可能分叉签名怪癖、Xcode 版本、交叉编译标志、Rosetta 兼容处理条件环境表达式已经足够复杂再加矩阵维度会更难维护Windows ARM 走四段 job 链因为azure/artifact-signing-action不支持 ARM runner必须ARM 构建 → x64 签 EXE → ARM 打包 MSI → x64 签 MSILinux x86 与 ARM 分开wheel 构建用 ubuntu-22.04目标 glibc 2.35ARM 安装包用 ubuntu-24.04-armQt wheels 只提供 24.04 的 ARM 包且 Briefcase 不捆绑 glibc导致 ARM 安装包运行时要求 glibc 2.39比 x86 安装包的 2.35 更高Linux 构建作业还会编译打包 fcitx5-qt输入法框架插件保证 Linux 用户的中文等输入法体验。输入参数详解prepare_release.py 参数脚本只接受一个必填的version位置参数和一个可选的--skip-ci-check标志。用法python3 tools/prepare_release.py 26.06 python3 tools/prepare_release.py 26.06b1 --skip-ci-checkrelease.yml 的 workflow_dispatch 输入输入效果sign对 macOS 和 Windows 产物签名。需要release环境。为 false 时对应 job 上传未签名产物且不接触签名密钥draft-release创建带自动生成发布说明和安装包的 GitHub 草稿 Release。要求signtrue、release环境、CI 通过除非跳过、无重复 tag/release、version与.version一致publish-testpypi发布 wheel 到 TestPyPI。需要release环境publish-pypi发布 wheel 到 PyPI。需要release环境、CI 通过除非跳过、version与.version一致会先运行并等待 TestPyPI 发布 job除非同时开启draft-release否则不要求签名skip-ci-check跳过 CI 状态检查用于热修复发布version对draft-release或publish-pypi必须与.version匹配对纯构建、仅签名或仅 TestPyPI 的运行会被忽略自动使用分支上的.version所有布尔输入默认false。非发布运行直接使用仓库中已有的.version因此不需要 prepare 步骤也能构建。一次正常公开发布需要开启前四个布尔量signtrue, draft-releasetrue, publish-testpypitrue, publish-pypitrue从源码看preparejob 的校验逻辑只有draft-release或publish-pypi为 true 时才认为这是 public release此时才强制version输入与.version文件一致并执行 CI 检查与重复检查其余情况会打印Non-release run — ignoring version input并回退使用.version。环境审批门禁Environment Gatesrelease.yml使用 GitHubenvironments作为人工审批门禁。凡是接触签名凭据或发布产物的 job 都需要评审人先批准部署release环境当sign、draft-release、publish-testpypi或publish-pypi任一开启时启用。保护代码签名密钥、release token、PyPI/TestPyPI 的 trusted publishing/OIDC 凭据testpypi/pypi环境分别保护 TestPyPI 与 PyPI 的 OIDC 发布权限publish-testpypijob 声明id-token: write权限并使用pypa/gh-action-pypi-publish走 trusted publishing不依赖 API token当sign关闭时macOS 与 Windows 构建 job 的环境表达式为${{ inputs.sign true release || }}即不绑定release环境——它们不需要审批、也无法访问签名密钥。用 just 运行发布release.just中定义的release模块封装了 prepare 脚本和release.yml的触发命令。release::prepare在本地运行且不带--ref其余所有 recipe 都是派发release.yml必须显式传入指向 release 分支的--ref参数。version变量自动取自.version文件version : \cat .version。Recipe等价于说明just release::prepare --version ver本地运行python3 tools/prepare_release.py校验版本、检查 CI、更新.version并推送just release::build --ref branchgh workflow run release.yml 全部 false全平台构建不签名不发布just release::sign --ref branch同上 signtrue构建并签名产物just release::testpypi --ref branch同上 publish-testpypitrue构建并发布到 TestPyPIjust release::pypi --ref branch同上 publish-testpypitrue, publish-pypitrue发布到 TestPyPI 和 PyPIjust release::public --ref branch同上 signtrue, draft-releasetrue, publish-testpypitrue, publish-pypitrue完整公开发布just release::custom --ref branch --sign ... --draft-release ... --publish-testpypi ... --publish-pypi ... --skip-ci-check ...手动逐项指定所有开关最灵活的触发方式运行just --list --list-submodules可查看全部可用 recipe 及参数。从特性分支测试发布工作流release.yml可以从任意分支派发用于测试。发布门禁只在开启draft-release或publish-pypi时生效因此测试构建非常安全# 1. 从你的分支派发 release.yml所有布尔输入保持 false just release::build --ref your-branch # 2. 工作流按原样读取分支上的 .version非发布运行忽略 version 输入无需 prepare 步骤 # 3. 所有发布门禁CI 检查、重复 tag 检查都被跳过 # 4. 产物上传到 workflow run但不会发布或打 tag带代码签名测试# 1. 在仓库 Settings → Environments → release 中临时把你的分支加入 allowed deployment branches # 2. 派发工作流 just release::sign --ref your-branch # 3. 出现环境部署提示时批准 # 4. 测试完成后把分支从环境 allowed branches 中移除注意workflow_dispatch工作流只有在默认分支上存在该文件时才会出现在 GitHub Actions 的 UI 下拉菜单里。如果release.yml在你的分支上是新增或修改的要用gh workflow run触发——合并回 main 之前 UI 里看不到它。重要注意事项release 工作流构建github.sha处的精确提交它不写.version那是 prepare 脚本的职责。如果在 prepare 提交推送之前就派发 release构建会使用派发时 HEAD 上的.versiondraft-releasetruesignfalse会被拒绝——草稿发布必须使用已签名的安装包publish-pypitrue时wheel 先发布到 TestPyPITestPyPI job 成功后再发 PyPI若同时设置draft-releasetruePyPI 发布还会等待草稿 GitHub Release 成功预发布版本如26.05b1会在 GitHub 草稿 Release 上自动标记为 pre-releasepreparejob 用tools/validate_version.py判断is_prereleasereleasejob 据此追加--prerelease标志。发布后的通告与收尾草稿 Release 创建后按需修改生成的 changelog然后点击Publish release在 Anki 官方论坛的 Beta Testing 板块创建公告帖稳定版发布后锁定该帖并让用户到新帖反馈问题稳定版发布后更新ankitects/anki-landing-page仓库中的版本号。音频包anki-audio的独立发布音频播放与录制由独立的anki-audio包处理位于 qt/audio其pyproject.toml声明Audio binaries (mpv, lame) for Anki打包 mpv 与 lame 二进制。它有自己的版本文件、构建脚本和发布工作流与主发布管线解耦更新 Windows 预编译二进制修改 build/configure/src/audio.rs 中的归档链接该文件按平台给出 mpv v0.41.0 与 lame 3.100 的下载 URL 和 SHA256 校验值然后运行./tools/ninja audio_wheel验证更新 macOS 构建脚本修改 qt/audio/mpv.rbHomebrew 打包脚本本地用 qt/audio/build.sh 测试构建该脚本也出现在.github/workflows/publish-audio-package.yml的 macOS job 中Windows 平台则调用tools\ninja audio_wheel提升版本号写入 qt/audio/.version——wheel 的版本号由 qt/audio/version.py 读取该文件得到提交 PR合入 main派发发布工作流.github/workflows/publish-audio-package.yml发布到 PyPIgh workflow run publish-audio-package.yml -f sign-macostrue -f publish-testpypitrue -f publish-pypitrue该工作流在四个平台macOS ARM/Intel、Windows x64/ARM上矩阵构建 wheelpublish-pypitrue时强制要求sign-macostruemacOS 二进制必须签名同样走 TestPyPI → PyPI 的顺序更新主项目锁定版本包发布后把 qt/pyproject.toml 中固定的anki-audio依赖版本更新到新版本。音频包之所以单独发布是因为它在项目构建中作为外部依赖被引用见build/configure/src/audio.rs的audio_wheel构建动作主发布与音频包发布因此可以各自独立迭代、独立打版本。结语Anki 的发布体系把版本校验、分支管理、翻译同步、CI 确认、跨平台构建、签名、多仓库发布、人工审批全部串成一条可追溯的流水线prepare 脚本保证发布的正确性版本合法、无重复、CI 绿release.yml的门禁与 job 拓扑保证产物的完整性与安全性全平台覆盖、签名可控、OIDC 发布而just release::*命令则把最常用的发布场景收敛成一行命令。无论是想理解日历版本发布的工程实践还是需要为自己的开源项目搭建类似的自动化发布管线这条链路都提供了非常完整的参考实现。【免费下载链接】ankiAnki is a smart spaced repetition flashcard program项目地址: https://gitcode.com/GitHub_Trending/an/anki创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考