mcpField
R
releasekit

releasekit

github.com/mimukit开发2203 次安装今天更新
代码仓库

关于 releasekit

releasekit 是将 Conventional Commits 转为版本发布的技能,适合维护使用该提交规范的仓库。它推导语义化版本并生成变更日志,预览确认后创建标签和 GitHub Release;不负责发布到软件包注册表。

主要功能

  1. 01推导下一语义化版本
  2. 02生成 Keep a Changelog 条目
  3. 03发布前预览并要求确认
  4. 04创建版本标签和 GitHub Release

适用场景

  1. 01根据合并提交准备版本发布
  2. 02查看下一版本及其变更日志
  3. 03为 CI 发布流程创建触发标签

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mimukit/skills --skill releasekit

安装后在 Claude Code 中输入/releasekit确认已加载。

详细介绍

releasekit

使用 Conventional Commits 的仓库已经回答了发布所需的两个问题:改了什么,以及造成了多大破坏。releasekit 会读取这些答案。它会解析自上次发布以来的提交范围,根据其中的提交推导下一个 semver 版本和 Keep a Changelog 格式的章节,并完成发布:添加一条 CHANGELOG.md 记录、更新生态系统清单中的版本、创建带注释的标签,以及创建 GitHub release。

releasekit 执行的所有操作都会先预览,再进行任何更改,因为版本号一经使用就无法收回。发布了有问题代码的标签无法重复使用,只能被取代。

releasekit 的操作到标签为止。 它不会运行 npm publish、twine upload 或 cargo publish。它创建的标签正是发布工作流已在监听的触发条件,而凭据与不可逆性都是将发布工作留给工作流处理的充分理由。

何时使用

用户想要将已合并的工作变成一个发布版本:「创建一个发布版本」「发布这个版本」「打一个版本标签」「下一个版本会是什么」「生成变更日志」「发布 v2」「/releasekit」。

以下三条边界很重要:

  • 它不会修改自己读取的提交。 编写 Conventional Commits 属于提交步骤;releasekit 只会解析日志中已有的内容。
  • 它不是发布工具。 上传到注册表是 CI 的工作,由标签触发。
  • 它仅以交互方式运行。 下方的确认是不可豁免的实际关卡,因此 releasekit 不会在无人值守的流水线中运行。若运行时没有人可以回答,它会停在预览阶段,而不会擅自假定用户同意。

流程

1. 预检

每项检查都在任何变更发生前运行,每次失败都会明确指出失败项,而不是拖到后续检查才报错。只有在以下每项检查都通过或以明确的拒绝原因终止运行后,预检才算完成。

环境。 确认存在 git、远程仓库和可用的 gh。如果没有 gh,releasekit 仍会写入变更日志并创建标签,因此只需说明一次:GitHub 发布和 CI 检查都会跳过,并说明原因。不要因为缺少增强功能而全面失败。

基准引用。 切勿假定是 main;实际仓库的默认分支可能是 develop、trunk 或 master。安装了 gitkit 时,请向它查询,因为它负责确定此项。否则,直接解析:

gh repo view --json defaultBranchRef --jq .defaultBranchRef.name

如果未能解析,则回退到 git symbolic-ref --short refs/remotes/origin/HEAD,再无法确定时应询问用户,而不是继续猜测。

仓库状态。 遇到以下任一情况时拒绝执行,并说明具体是哪项检查失败:

  • 工作区有未提交更改,因为发布必须描述已提交的代码;
  • HEAD 不是基准引用,因为发布功能分支会给无人合并的代码打标签;
  • 当前分支落后于其上游分支,因为变更日志会漏掉已公开的提交。

相互冲突的发布工具。 查找 .changeset/、.releaserc*、release-please-config.json,或 semantic-release 依赖。它们中的任何一个都负责端到端的版本管理,第二个写入工具会破坏其状态。在进行任何变更之前停止,并说明工具名称及其命令。这是拒绝执行,不是警告。

工作区。 查找 package.json 中的 workspaces、pnpm-workspace.yaml,或 Cargo.toml 中的 [workspace]。仅有标记并不代表是 monorepo,因为许多仓库都有 packages/ 目录,但只发布一个构件。仅当存在标记且多个清单文件都包含版本号时才拒绝执行,并列出找到的所有带版本号的清单文件。只有根目录包含版本号的工作区只有一个版本,可以正常发布。

分支保护。 对基准分支探测一次:

gh api "repos/{owner}/{repo}/branches/$BASE/protection"

404 表示未受保护,选择直接路径。其他任何响应都表示受保护,选择发布 PR 路径。弄错并非小事:直接路径会将提交直接推送到基准分支,而受保护的仓库会拒绝该操作,此时变更日志已经写入。

阶段。 根据仓库判断本次运行处于哪个阶段,绝不根据自行保存的状态判断。按以下顺序检查,并采用第一个匹配项:

  1. 恢复。 先前运行中途停止。证据是 HEAD 所在提交线上的最新 chore(release): vX.Y.Z 提交,加上以下任一情况:标签 vX.Y.Z 仅存在于本地(git ls-remote --tags origin vX.Y.Z 为空);标签已推送到远程,但 gh release view vX.Y.Z 找不到发布;或者在直接路径中,该提交尚未出现在上游且没有标签。恢复该版本,绝不重新推导版本号,因为再次推导会为同一份代码多占用一个版本号。从第一个尚未完成的步骤继续,并从 CHANGELOG.md 中该版本对应的章节获取发布说明。
  2. 完成发布,仅适用于发布 PR 路径。基准分支上已合并 chore(release): vX.Y.Z 提交,但没有指向该提交的标签。
  3. 准备发布,适用于发布 PR 路径;或者在直接路径中进行全新发布。其他情况均归入此项。

恢复和完成发布都会从提交主题读取版本号,并跳过推导版本号和生成变更日志。只有当预览能明确指出一个阶段,并在恢复时指出接下来要运行的第一个步骤,检查才算完成。

2. 推导版本号

确定上次发布版本。 取已合并到 HEAD 的最高稳定版 semver 标签,而不是仓库中版本号最高的标签:

tags=$(git tag --merged HEAD \
  | grep -E '^v?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$' \
  | awk -F. '{ m = $1; sub(/^v/, "", m); if (m + 0 < 1000) print }')
nv=$(printf '%s\n' "$tags" | grep -c '^v')
nb=$(printf '%s\n' "$tags" | grep -c '^[0-9]')
if [ "$nb" -gt "$nv" ]; then style='^[0-9]'; else style='^v'; fi
last=$(printf '%s\n' "$tags" | grep "$style" | sort -V | tail -n 1)

$last 为空表示没有此前的发布。每个筛选条件各有一个用途:

  • --merged HEAD 会保留版本沿革。一个同时运行 v1.x 和 v2.x 的仓库会读取自己的分支线,因为 v2 标签不会合并到 v1 维护分支中。
  • 锚定正则表达式只接受 MAJOR.MINOR.PATCH,可选带 v,且不允许前导零。它会拒绝预发布版本(v1.3.0-rc.1)、构建或备份后缀(v1.4.0.bak)、带零填充的 CalVer 日期(2026.08.01),以及 nightly-2026-08-01 这类名称。git describe --match 的 glob 无法做到这一点,因为末尾的 * 会接受任意后缀。
  • awk 过滤器会排除主版本号达到 1000 或更高的标签。这种形式属于 CalVer 日期,例如 2026.8.1,仅靠正则表达式无法将其排除。请在预览中列出所有被排除的标签,以便看出是否存在这么大的真实 SemVer 主版本号。
  • 前缀风格遵循剩余标签中的多数风格,平票时选择 v。使用 v1.2.3 打标签的仓库会忽略意外出现的裸标签 2.0.0,使用裸标签的仓库也会忽略意外出现的 v 标签。请在预览中将另一种风格的标签列为已跳过。
  • sort -V 会按版本排序,因此 v1.10.0 排在 v1.9.0 之后。在 sort 不支持 -V 的主机上,改为按三个数字字段排序。

此处会跳过预发布版本,因此 v1.3.0-rc.1 发布时包含的提交仍会出现在 v1.3.0 的变更日志中。请在解析器中截断预发布版本,这样下游就无需知道预发布版本的存在。

读取提交范围 <lasttag>..HEAD;如果没有标签,则读取全部历史记录。解析每个提交的主题和正文,因为 BREAKING CHANGE: 页脚位于正文中,所以不能跳过正文。

根据范围内的内容决定版本递增,并采用适用的最高级别:

范围内的内容1.x 及以上版本的递增0.x 版本的递增
! 标记或 BREAKING CHANGE: 页脚主版本次版本
任意 feat次版本修订版本
任意 fix、hotfix、perf 或 revert修订版本修订版本
仅有 docs、chore、style、test、build、ci、refactor不递增不递增

0.x 这一列并非笔误,而且最容易让人意外。 对尚未达到 1.0 的仓库递增主版本,会将 0.3.0 升到 1.0.0,仅凭一个提交页脚就宣告 API 稳定。这里改为遵循 release-please 和 Cargo 的惯例。请在预览中用文字说明规则及其计算方式,而不只是显示计算结果。

首次发布需要征求用户意见。 如果仓库中没有标签,请预填 0.1.0,并提示一次。每个仓库只会发生一次,而且之后无法更正;1.0.0 是公开宣告 API 稳定的版本,绝不能代替用户猜测。如果清单中已有版本号,则跳过提示,因为那个版本号就是用户的答案。

标签前缀沿用现有设置,不自行选择。 v1.2.0 表示使用 v,1.2.0 表示不使用前缀。首次发布时默认使用 v。

此步骤会有两种拒绝情况,两者都应给出明确命名的覆盖选项,让用户知道如何继续,而不是陷入死路:

  • 没有面向用户的变更。 范围内没有任何会触发版本递增的提交。请明确说明(例如“自 v1.2.0 以来没有面向用户的变更”),并告知有需要的用户可使用 --allow-empty。每次 CI 调整都递增修订版本会让版本号失去意义;每次都提示则会让人养成忽略提示的习惯。
  • 没有可解析的提交。 范围内没有任何提交符合 Conventional Commits,因此任何版本递增都是猜测。请同样拒绝。

无法解析的提交仍可继续处理。 将它们收集到 Other 部分,并在预览中计数。采用 Squash 合并的仓库会根据 PR 标题生成提交主题,而任何早于该约定的仓库都有混杂的提交记录;如果要求每个提交都能解析才继续,就会让 releasekit 在这两种仓库中都无法使用。不会静默丢弃任何内容;上面的最低要求可以防止版本递增变成无根据的猜测。

当预览能够说明上次发布的版本(或没有发布过)、解析器跳过的每个标签、下一个版本以及递增规则时,此步骤即告完成。

3. 渲染变更日志

按类型将解析出的提交归入 Keep a Changelog 各个章节:

提交类型章节
带有 ! 或 BREAKING CHANGE: 页脚的任何类型Breaking changes
featAdded
fix、hotfixFixed
perf、revertChanged
docs、refactor、style、test、build、ci、chore无;在预览中按类型统计
未解析Other

hotfix 提交是在紧急路径上发布的修复,因此对用户而言应显示为修复。被省略的类型不会改变该版本用户可见的内容,预览中的计数表明这些提交已被读取,而非遗漏。在 --allow-empty 发布中,将被省略的提交列在 Other 下,确保该章节不会为空。仅在章节中有条目时才渲染该章节。

  • Breaking changes 始终排在最前,并且独占一个章节,附上页脚中的迁移说明。读者浏览版本说明时,想先看到可能造成问题的内容,再看到令人欣喜的内容。
  • 保留作用域,省略类型。 在 Added 标题下,auth: add token refresh retry 比 feat(auth): add token refresh retry 更易读,因为标题已经说明了 feat。
  • 每个条目都链接到其提交;如果合并提交中提到了 PR,也链接到对应 PR。
  • 确保每个提交都有归属。 当范围内的每个提交都恰好归入一个章节或计入省略数量时,此步骤才算完成。
  • 只渲染一次。 CHANGELOG.md 章节和 GitHub 发布正文必须是同一段文本。渲染两次就会产生两份可能不一致的文本,而这种不一致总会在发布公开后显现。
4. 预览并确认

预览是必需的,并且始终会渲染。运行 releasekit preview 后,流程在此结束,不会更改任何内容;直接运行 releasekit 时,流程会在此等待确认,在收到确认前不会更改任何内容。

先说明路径,再列出事实:

  • 第一行。 路径(直接发布或发布 PR)和阶段(恢复、完成、准备或全新)。阶段误判是唯一会导致重复发布的故障,因此在可执行操作前必须让它清晰可见。
  • 计算出的版本,以及用文字说明得出该版本的规则;
  • 完整渲染后的变更日志;
  • 清单文件及其旧版本 → 新版本,或说明未找到清单;
  • 标签名称;
  • CI 检查汇总结果,以及其对应的 SHA;
  • 未解析提交的数量;
  • 即将运行的每条命令。
gh api "repos/{owner}/{repo}/commits/$SHA/check-runs" \
  --jq '.check_runs[] | [.name, .status, .conclusion] | @tsv'
gh api "repos/{owner}/{repo}/commits/$SHA/status" --jq .state
  • 完成发布,以及发布提交已在上游的恢复操作。 $SHA 是要打标签的发布提交。
  • 直接发布,以及发布提交不在上游的恢复操作。 CI 不可能在发布提交上运行,因此 $SHA 是其父提交:即发布提交之前的 HEAD。发布提交只会在其上添加变更日志并更新清单版本。推送前再次确认 origin/<base> 仍等于 $SHA;如果已移动则停止。
直接路径

未受保护的基准分支。按以下顺序执行,因为每一步都依赖上一步的结果:

  1. 更新检测到的清单(package.json、Cargo.toml、pyproject.toml、gemspec)。未找到清单也没关系,不算错误。
  2. 将渲染后的章节添加到 CHANGELOG.md 的开头;如果文件不存在,则创建文件并添加标题。
  3. 将两项更改一并提交,提交信息为 chore(release): v<version>。只创建一个提交,因为版本更新和变更日志描述的是同一件事。
  4. 创建注释标签,标签消息使用发布标题。
  5. 推送提交和标签。
  6. 使用渲染后的正文,通过 gh release create 创建 GitHub 发布。
发布 PR 路径

受保护的基准分支。分两次调用,具体运行哪一个阶段由 预检 中确定的阶段决定。

准备。 在 release-v<version> 分支上更新清单、处理变更日志并提交。推送该分支,并打开标题为 chore(release): v<version> 的 PR,正文使用渲染后的变更日志。标题至关重要:压缩合并会将 PR 标题作为提交主题,而完成阶段会根据该主题查找发布。到此停止。 由用户审核并合并 PR。

完成。 已合并且尚未打标签的 chore(release): 提交就是触发条件。根据该提交的 SHA 验证汇总内容,为该提交打标签,推送标签,并创建 GitHub 发布。合并后,变更日志已经在仓库中,因此无需重写或提交任何内容。

当预览列出的所有命令都已运行,或运行在某一步失败后停止时,此步骤即告完成。如果任一路径中的任何一步失败,都在该步停止,并准确报告哪些更改已落地、哪些没有。下次运行会将缺失项识别为恢复,并从那里继续。绝不回滚已推送的标签;请参阅备注中的拒绝处理情形。

6. 交接

本节使用程序性语体撰写:每句一条指令,使用主动语态和现在时,不使用比喻。

交接内容因路径而异,因此只报告实际运行的路径。

直接路径、发布 PR 完成阶段和恢复。 这是终止阶段。

  • 变更内容。 说明版本、更新的文件以及创建的提交。
  • 落地位置。 说明标签名称、发布 URL 和推送的分支。
  • 后续步骤。 说明没有后续工具包。指出推送标签可能已启动发布工作流,且 releasekit 不负责管理或监控该工作流。说明笔记中有拼写错误时使用 gh release edit。

发布 PR 路径,准备阶段。 这不是终止阶段。

  • 变更内容。 说明发布分支、该分支上的提交以及已打开的 PR。明确说明目前还没有标签。
  • 落地位置。 说明分支名称和 PR URL。
  • 后续步骤。 审核并合并 PR。安装了 mergekit 时使用它,否则使用 gh pr merge。然后再次运行 releasekit,为已合并的提交打标签。

备注

  • 有四种拒绝情形,且每种都是拒绝,而不是警告。 如果仓库由其他发布工具管理,releasekit 就会停下。它会拒绝处理包含多个带版本号软件包的工作区。它会拒绝为红色基准打标签。它绝不会移动或删除已发布的标签,因为重新打标签会破坏所有已经解析该标签的使用者。错误的发布应通过发布下一个版本来修正。
  • 修正已发布版本中的错字不在处理范围内。 已发布版本的说明本来就可以编辑,但增加一条编辑路径,会让原本只有一个流程、两个出口的技能变成三个入口。请改用 gh release edit。
  • 预发布版本只读取,不写入。 releasekit 会跳过已有的 -rc.N 标签,因此已在使用这类标签的仓库仍可发布稳定版本。它不会创建这类标签。
  • 每个仓库只有一个版本。 单体仓库中的按软件包版本管理属于另一项技能,使用不同的数据模型;对此自行猜测并默默处理,比直接拒绝更糟。
  • 它绝不会在无人值守的情况下运行。 预览并确认中的确认步骤是必须经过的同意关卡,没有例外。到达这一步却无人回应的编排器必须停止并升级处理,不得继续。
  • 遵循仓库的做法,而非这些默认设置。 如果仓库使用自己的变更日志格式、将 CHANGELOG 放在其他路径,或记录了发布流程,就应遵循该做法,并说明你已按此执行。
  • Skill 仅为一次调用而声明,用于向 gitkit 请求基准引用。releasekit 不会调用其他技能;在交接中提到 mergekit 是为了路由,而不是启动它。
  • 没有文件系统或 shell(例如基于浏览器的 Agent)?那你就无法读取日志或执行发布。此时请输出推导出的版本、以代码块形式呈现的变更日志,以及供用户运行的确切 git tag 和 gh release create 命令。

评论

支持 Markdown