关于 prepare-release
prepare-release 是用于 Cherry Studio 的发布准备技能,可从变更整理双语发布说明、更新版本文件并启动发布分支与 CI/CD 流程。它涵盖版本校验、发布状态检查及后续发布链路约束。
主要功能
- 01解析版本类型或指定版本
- 02整理提交并生成双语说明
- 03更新发布元数据文件
- 04创建发布分支并触发 CI/CD
- 05校验发布状态与版本顺序
适用场景
- 01为 Cherry Studio 准备补丁或次版本
- 02生成中英文发布说明
- 03在发布分支启动构建与发布流程
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ cherryhq/ cherry-studio --skill prepare-release安装后在 Claude Code 中输入/prepare-release确认已加载。
详细介绍
准备发布
自动化 Cherry Studio 的发布工作流:收集变更 → 生成双语文档发布说明 → 更新文件 → 创建发布分支 → 触发 CI/CD。
参数
从用户的消息中解析版本意图。接受以下任意形式:
- 版本号递增类型关键字:
patch、minor、major - 确切版本:严格的
x.y.z或x.y.z-<prerelease>,不带构建元数据(例如1.8.0、1.8.0-beta.1、1.8.0-rc.1) - 自然语言:"prepare a beta release"、"bump to 1.8.0-rc.2" 等。
如果未指定版本,则默认为 patch。在进行任何文件编辑之前,始终向用户回显解析出的目标版本。
--dry-run:仅预览,不创建发布分支。
工作流
步骤 1:确定版本
- 对于交互式本地运行,获取
origin/main和所有标签,然后验证检出内容是否为干净的main,且恰好位于origin/main:
如果任何检查失败,则在编辑文件之前停止。这可防止独立运行从任意或过期的检出内容创建发布分支。 在 GitHub Actions 中,使用工作流冻结的 dispatch SHA,并将检出验证交给工作流处理。不要获取或比较之后的git fetch origin refs/heads/main:refs/remotes/origin/main --tags test "$(git branch --show-current)" = main test -z "$(git status --porcelain)" test "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)"origin/mainhead。 - 从
package.json读取当前版本。即使该发布后来已被撤回,也用它来进行版本递增;绝不要复用已撤回的版本。 - 选择最新的已发布、非草稿的 GitHub Release,其标签为严格的
v<semver>,作为发布说明基线。非 semver 的预览发布和草稿永远不能作为基线:
如果不存在已发布的基线,或其 Git 标签缺失(gh release list --limit 1000 --json isDraft,publishedAt,tagName --jq '[.[] | select(.isDraft == false and (.tagName | test("^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?$")))] | sort_by(.publishedAt) | last | .tagName // empty'git rev-parse --verify refs/tags/{baseline-tag}),则停止。使用semver将其版本与当前 package 版本进行比较:如果 main 落后,则停止并要求合并最新的 Post Release 元数据 PR。如果 main 领先,则允许使用已发布基线生成发布说明,并使用 package 版本进行版本递增。例如,在撤回 2.1.1 后,main 位于 2.1.1,而已发布基线为 2.1.0,则准备 2.1.2 并包含自 2.1.0 以来的变更。 在 GitHub Actions 中,使用工作流提供的基线标签和收集基准,无需重复这些检查。 - 根据参数计算新版本:
patch/minor/major:从当前版本递增。- 确切版本必须匹配
^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$并通过semver.valid;不接受诸如+build.1这样的构建元数据。 - 在两种情况下,都要求结果按照 semver 优先级严格大于当前版本。拒绝相同版本和降级。
步骤 2:收集提交
- 根据步骤 1 中选定的已发布基线确定 release note 收集基准,而不是根据包版本:
- 如果基线标签是
HEAD的祖先,则使用该标签。 - 否则,使用完整提交信息中包含精确标记
release-metadata-boundary: <baseline-tag>的最新提交。该机器标记被添加到 Post Release pull request 正文中,并在必需的 squash merge 后保留下来。 - 对于在机器标记存在之前创建的元数据 pull request,接受主题恰好等于
chore(release): sync <baseline-tag> metadata的提交,或该主题后仅跟随 GitHub 的 squash 后缀(#<PR-number>)的提交。 - 如果该标签不是祖先且其元数据同步提交缺失,则以错误停止;否则已发布的 hotfix 可能会被再次包含。
- 如果基线标签是
- 列出该基准以来的所有提交:
git log <collection-base>..HEAD --format="%H %s" --no-merges - 对每个提交,获取完整正文:
git log <hash> -1 --format="%B" - 从每个提交正文中提取
```release-note代码块内的内容。 - 从标题中提取 conventional commit 类型(
feat、fix、refactor、perf、docs等)。 - 将以下提交作为独立 release note 候选项跳过,但在步骤 3 中将候选项与最终代码核对时,仍要检查其影响:
- 标题以
🤖 Daily Auto I18N开头的提交 - 标题以
Merge开头的提交 - 标题以
chore(deps)开头的提交 - 标题以
chore: release开头的提交 - 标题以
chore(release)开头的提交 - release-note 块中写有
NONE的提交
- 标题以
步骤 3:生成双语言 Release Notes
根据相对于已发布基线的最终用户可见变更,生成英文和中文两种语言的 release notes。提交标题和 release-note 块是候选描述,并不证明某项变更一定会发布。
起草前先核对净变更:
- 检查
git diff --name-status <baseline-tag> <release-head>,然后在两个端点处阅读相关补丁和代码。<release-head>是发布准备之前的源HEAD(CI 中冻结的 dispatch SHA)。比较两个端点树,而不是三点 merge-base diff。收集基准仅用于发现提交;当它与已发布标签不同时,还要检查该提交范围内缺失的用户可见差异。 - 按用户可见行为对候选项分组,并按时间顺序追踪相关补丁,包括步骤 2 中排除的提交。从代码中检测显式 revert、手动撤销、替换、部分回退和重新引入;不要仅依赖提交措辞或匹配的哈希值。
- 省略在本周期内引入并完全撤销的变更,包括仅针对该临时变更的修复。对于部分回退、替换或重新引入,只描述与已发布基线不同的最终结果,每个不同的用户可见变更描述一次。
- 如果回退移除了或改变了已发布基线中已存在的行为,则描述由此产生的用户可见移除或恢复。不要不加区分地丢弃所有 revert 提交。保持相同用户可见行为的实现重写本身并不足以成为 release-note 条目的理由。
- 根据端点 diff 和最终代码验证每个提议的条目,并确保英文和中文版本描述相同的结果。如果所声称的效果无法证实,则省略它,并在准备摘要中报告该不确定性,而不是编造 release-note 声明。
推荐格式:
Cherry Studio {version} - {Brief English Title}
✨ New Features
- [Component] Description
🐛 Bug Fixes
- [Component] Description
💄 Improvements
- [Component] Description
⚡ Performance
- [Component] Description
Cherry Studio {version} - {简短中文标题}
✨ 新功能
- [组件] 描述
🐛 问题修复
- [组件] 描述
💄 改进
- [组件] 描述
⚡ 性能优化
- [组件] 描述
语言标记是机器可读的契约:每个标记包含一次,保持其顺序,并提供非空的英文和中文部分。标题和周围的说明文字是呈现方式的选择,不是验证要求。 规则:
- 只保留有内容的分类(省略空分类)。
- 每个不同的、用户可见的最终变更在相应分类中只出现一次;合并相关提交,省略已取消或被取代的条目。
- 优先采用
release-note字段的措辞,否则采用提交标题,但仅当其与验证后的最终结果一致时。 - 组件标签应简短:
[Chat]、[Models]、[Agent]、[MCP]、[Settings]、[Data]、[Build]等。 - 中文翻译应自然,不要机翻。
- 不要包含提交哈希或 PR 编号。
- 撰写前先阅读
electron-builder.yml中现有的发布说明作为风格参考。
重要:仅限面向用户的内容
发布说明面向最终用户,而非开发者。排除用户不关心的内容:
- 排除内部重构、代码清理或架构变更
- 排除 CI/CD、构建工具或测试基础设施变更
- 排除依赖更新(除非它们带来了用户可见的功能)
- 排除文档更新
- 排除开发者体验改进
- 排除无用户可见影响的技术债修复
- 排除过于技术化的描述(例如“修复 Redux 中间件中的竞态条件”)
仅包含用户会注意到的变更:
- 他们可以使用的新功能
- 影响其工作流的缺陷修复
- 他们能看到的 UI/UX 改进
- 他们能感受到的性能改进
- 安全修复(简化描述,不涉及实现细节)
保持描述简单、非技术化:
- ❌ “修复流式竞态条件导致 Redux 状态中工具响应状态不完整”
- ✅ “修复中止时工具状态不停止的问题”
- ❌ “为 OpenAI 兼容提供商自动将 reasoning_effort 转换为 reasoningEffort”
- ✅ “修复深度思考模式在部分提供商下无法使用的问题”
步骤 4:更新文件
package.json:将"version"字段更新为新版本号。electron-builder.yml:用生成的说明替换releaseInfo.releaseNotes: |下的内容。保留块标量内容的 4 空格 YAML 缩进。resources/cherry-studio/release-history.json:切勿手工编辑;说明必须与electron-builder.yml逐字节一致。运行node scripts/release/sync-release-history.js --target-version {version},它会为稳定版预置(或替换)对应条目,而对预发布版保持文件不变。在 GitHub Actions 中,工作流会在 Claude 步骤之后自行运行此命令。- 校验源元数据:对于交互式本地运行,在生成产品清单之前运行
node scripts/release/validate-prepared-release.js --target-version {version},如果它拒绝了变更路径、版本顺序、双语章节或稳定版历史记录,则停止。在 GitHub Actions 中,将校验交给 Claude 之后运行的工作流步骤处理。 - 内置知识库:对于交互式本地运行,在校验后运行
pnpm build:builtin-knowledge。这会用新的包版本刷新resources/builtin-agents/cherry-assistant/product-manifest.json。切勿手工编辑生成的产品清单。在 GitHub Actions 中,不要运行生成器:工作流会先运行同一个校验器,然后自行运行受信任的生成器。
步骤 5:提交审阅
向用户展示:
- 新版本号。
- 生成的完整发布说明。
- 修改了哪些文件的摘要。
如果指定了 --dry-run,则在此停止。
否则,请用户确认后再继续到步骤 6。
步骤 6:创建发布分支
- 对于交互式本地运行,在创建分支之前立即重复步骤 1 的第 1-3 项,从
HEAD:package.json读取原始包版本。要求已发布的基线仍与用于收集发布说明的基线一致;否则在继续之前重新生成发布说明。由于步骤 4 已特意准备并验证了发布元数据,请用git status --short替换步骤 1 的干净工作树断言,并且除非列出的每个路径都是四个允许的发布元数据文件之一,否则停止。然后创建并推送一个签名的、符合 DCO 的发布提交:git fetch origin refs/heads/main:refs/remotes/origin/main --tags test "$(git branch --show-current)" = main test "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)" test "$(node -p "require('./package.json').version")" = "{version}" CURRENT_VERSION="$(git show HEAD:package.json | jq -r .version)" LATEST_PUBLISHED="$(gh release list --limit 1000 --json isDraft,publishedAt,tagName --jq '[.[] | select(.isDraft == false and (.tagName | test("^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?$")))] | sort_by(.publishedAt) | last | .tagName // empty')" test "$LATEST_PUBLISHED" = "{baseline-tag}" git rev-parse --verify "refs/tags/$LATEST_PUBLISHED" node -e "const semver = require('semver'); process.exit(semver.gte(process.argv[1], process.argv[2]) ? 0 : 1)" "$CURRENT_VERSION" "${LATEST_PUBLISHED#v}" REPO="$(gh repo view --json nameWithOwner --jq .nameWithOwner)" gh api --paginate --slurp "repos/$REPO/releases?per_page=100" | TAG="v{version}" node scripts/release/validate-release-state.js prepare test -z "$(git ls-remote --heads origin refs/heads/release/v{version})" git status --short UNEXPECTED_RELEASE_PATHS="$(git status --porcelain | cut -c4- | grep -Ev '^(package\.json|electron-builder\.yml|resources/cherry-studio/release-history\.json|resources/builtin-agents/cherry-assistant/product-manifest\.json)$' || true)" test -z "$UNEXPECTED_RELEASE_PATHS" git checkout -b release/v{version} git add package.json electron-builder.yml resources/cherry-studio/release-history.json resources/builtin-agents/cherry-assistant/product-manifest.json git commit -S --signoff -m "chore(release): prepare v{version}" git cat-file commit HEAD | grep -q '^gpgsig ' git log -1 --format=%B | grep -q '^Signed-off-by: ' git push -u origin release/v{version} - 在 GitHub Actions 中,更新
package.json和electron-builder.yml后停止。允许使用临时辅助文件和本地 Git 操作;工作流提取这两个文件变更,恢复冻结的源 SHA,并丢弃其他所有内容。然后它推导发布历史、进行验证、生成产品清单、创建分支,并使用 GitHub 的 API 创建和验证签名的、符合 DCO 的提交。切勿从 Claude 步骤推送。 - 报告发布分支和后续步骤。暂时不要创建 PR:必须先从此分支构建并发布版本。
CI 触发链
- 等待新
release/v{version}提交上的 CI 推送运行成功。auto-release-build.yml会重新验证该确切的活动分支头,并以all调度release.yml;它构建 macOS、Windows 和 Linux,并创建或更新草稿 GitHub Release。仅在重试失败的 all-platform 构建或针对未更改的已标记提交重试单个平台时,才手动使用release.yml。 - 当单个草稿语义版本发布处于活动状态时,
backport-release-fixes.yml会为来自main的第一个已合并的hotfix: <description>或hotfix(<kebab-case-scope>): <description>PR 打开一个 backport PR,应用任何可选的双语发布说明,然后将连续的 hotfix 和源标记追加到同一个打开的主题分支。它管理每个源 PR 的hotfix和 backport-status 标签,并在源 PR 上报告失败;切勿将main合并到发布分支。 - 审查 backport PR,等待其 CI,然后合并它。在由此产生的发布分支推送通过 CI 后,确切头的 all-platform 草稿重建会自动开始。
- 成功的精确头 all-platform 构建会启动
publish-release.yml。在检查草稿后批准releaseEnvironment 部署。然后发布获取 release-state 锁,重新验证已批准的运行、发布分支、标签、草稿、产物、打开的 PR 和待处理的 hotfix,并且仅当它们仍然一致时才发布。草稿正文包含双语electron-builder.yml说明,后跟 GitHub 生成的更改。最终获取的mainSHA 是 hotfix 截止点;在该快照之后合并的 hotfix 属于下一个版本。发布触发post-release.yml,它使用已发布的标签作为其源,仅将发布元数据增量应用于最新的main,并创建一个release-sync/v{version}仅元数据 PR。 - 元数据 PR 仅同步
package.json、electron-builder.yml、发布历史和生成的产品清单。它触发ci.yml;仅在 CI 通过后合并它。 - 在 squash 合并元数据 PR 时,将提交标题设置为恰好
chore(release): sync v{version} metadata,仅带有 GitHub 的可选 PR 编号后缀,并将release-metadata-boundary: v{version}保留在 squash 提交正文中的单独一行,以便下一个版本可以可靠地找到边界。
约束
- 修改
electron-builder.yml前,始终先阅读它,以了解当前格式。 - 切勿保留
package.json、electron-builder.yml、resources/cherry-studio/release-history.json以及生成的resources/builtin-agents/cherry-assistant/product-manifest.json之外的更改。 - 切勿直接推送到
main。 - 在 GitHub Release 发布之前,切勿创建发布元数据 PR;该步骤由
post-release.yml负责。 - 在创建发布分支之前,始终向用户展示生成的发布说明(除非在无交互用户的 CI 中运行)。
评论
支持 Markdown