releasekit
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesereleasekit
releasekit
A repo that writes Conventional Commits has already answered the two questions a release asks: what changed, and how much did it break. releasekit reads that answer back out. It resolves the commit range since the last release, derives the next semver version and a Keep a Changelog section from the commits in it, and cuts the release: a entry, a version bump in the ecosystem's manifest, an annotated tag, and a GitHub release.
CHANGELOG.mdEverything it does is previewed before anything mutates, because a version number is spent permanently. A tag that ships broken code cannot be reused, only superseded.
releasekit stops at the tag. It does not run , , or . The tag it creates is precisely the trigger a publish workflow already listens for, and credentials plus irreversibility are two good reasons to leave that where it is.
npm publishtwine uploadcargo publish对于遵循Conventional Commits规范的仓库,版本发布所需的两个核心问题——变更内容和破坏性程度——已经通过提交信息给出了答案。releasekit会读取这些信息,解析出自上次发布以来的提交范围,从中推导符合semver规范的下一个版本号,以及符合Keep a Changelog格式的变更日志章节,然后完成版本发布流程:添加条目、更新对应生态的清单文件版本、创建带注释的标签,以及发布GitHub版本。
CHANGELOG.md所有操作在修改任何内容前都会先进行预览,因为版本号一旦使用便无法撤回。如果发布的标签对应的代码存在问题,无法重复使用该标签,只能用新版本号替代。
releasekit的操作止步于打标签环节。它不会执行、或等命令。它创建的标签正是现有发布工作流监听的触发条件,而凭证管理和操作不可逆性是将发布环节留在原有工作流中的两大合理原因。
npm publishtwine uploadcargo publishWhen this fires
触发场景
The user wants to turn merged work into a released version: "cut a release", "release this", "tag a version", "what would the next version be", "generate the changelog", "ship v2", "/releasekit".
Three boundaries matter:
- It does not write the commits it reads. Authoring Conventional Commits belongs to the commit step; releasekit only ever parses what is already in the log.
- It is not a publisher. The registry upload is CI's job, triggered by the tag.
- It is interactive only. The confirmation below is a real gate with no exemption, so releasekit never runs inside an unattended pipeline. A run with nobody to answer stops at the preview rather than assuming a yes.
当用户希望将合并后的代码转为正式发布版本时,比如提及“创建版本发布”“发布此版本”“打版本标签”“下一个版本会是什么”“生成变更日志”“发布v2版本”或“/releasekit”时,该工具会触发。
需要明确三个边界:
- 它不会生成提交信息。编写符合Conventional Commits规范的提交信息属于提交阶段的工作;releasekit仅解析已存在于提交日志中的内容。
- 它不是发布工具。包注册表的上传操作属于CI的职责,由标签触发执行。
- 它仅支持交互式操作。下方的确认步骤是不可跳过的真实校验环节,因此releasekit永远不会在无人值守的流水线中运行。如果运行时没有用户确认,会在预览阶段停止,而非默认执行操作。
Procedure
操作流程
1. Preflight
1. 预检
Every check runs before anything mutates, and each failure names itself rather than falling through to a later one.
Environment. Confirm git, a remote, and a usable . Without , releasekit still writes the changelog and cuts the tag, so say once that the GitHub release and the CI check are both skipped, and why. Do not fail wholesale for a missing enrichment.
ghghBase ref. Never assume ; repos default to , , and in the wild. When is installed, ask it, because it owns this answer. Otherwise resolve it directly:
maindeveloptrunkmastergitkitsh
gh repo view --json defaultBranchRef --jq .defaultBranchRef.namefalling back to , and asking the user rather than guessing past that.
git symbolic-ref --short refs/remotes/origin/HEADRepo state. Refuse, and say which one failed, when:
- the working tree is dirty, because a release must describe committed code;
- HEAD is not the base ref, because releasing a feature branch tags code nobody merged;
- the branch is behind its upstream, because the changelog would omit commits that are already public.
Competing release tooling. Look for , , , or a dependency. Any of them owns versioning end to end, and a second writer corrupts its state. Stop before mutating anything, name the tool, and name its command instead. This is a refusal, not a warning.
.changeset/.releaserc*release-please-config.jsonsemantic-releaseWorkspaces. Look for in , a , or in . A marker alone is not a monorepo, because plenty of repos have a directory and one published artifact. Refuse only when a marker is present and more than one manifest carries a version, and list every versioned manifest you found. A workspace where only the root is versioned has one version and releases normally.
workspacespackage.jsonpnpm-workspace.yaml[workspace]Cargo.tomlpackages/Branch protection. Probe the base branch once:
sh
gh api "repos/{owner}/{repo}/branches/$BASE/protection"A 404 means unprotected and selects the direct path. Any other answer means protected and selects the release-PR path. Getting this wrong is not cosmetic: the direct path pushes a commit straight to the base, and a protected repo rejects it after the changelog has already been written.
Phase. On the release-PR path only, work out which half of the release this is from the repo, never from state you keep. A merged commit on the base with no tag pointing at it means finish. Anything else means prepare.
chore(release): vX.Y.Z所有检查会在修改任何内容前执行,每一项检查失败都会明确告知原因,而非延续到后续步骤再报错。
环境检查:确认git、远程仓库和可用的工具已配置。如果没有,releasekit仍会生成变更日志并打标签,但会一次性告知用户GitHub版本发布和CI检查将被跳过,以及原因。不会因缺少增强功能而直接执行失败。
ghgh基准分支:绝不默认使用分支;实际场景中仓库的默认分支可能是、或。如果已安装,则询问获取基准分支,因为它负责管理该信息。否则直接通过以下命令解析:
maindeveloptrunkmastergitkitgitkitsh
gh repo view --json defaultBranchRef --jq .defaultBranchRef.name如果上述命令失败,则回退到,若仍无法获取,则询问用户,而非自行猜测。
git symbolic-ref --short refs/remotes/origin/HEAD仓库状态:当出现以下情况时,拒绝执行操作并告知具体失败项:
- 工作区未干净(存在未提交的修改),因为版本发布必须基于已提交的代码;
- 当前HEAD指向的不是基准分支,因为发布功能分支会给未合并的代码打标签;
- 当前分支落后于上游分支,因为变更日志会遗漏已公开的提交。
冲突的发布工具:检查是否存在、、文件,或是否依赖。这些工具会全权负责版本管理流程,若同时使用releasekit会破坏其状态。在修改任何内容前停止操作,告知用户存在的工具及其对应的命令。这是直接拒绝,而非警告。
.changeset/.releaserc*release-please-config.jsonsemantic-release工作区检查:检查中的字段、文件或中的配置。仅存在标记并不代表是单仓多包仓库,因为很多仓库虽有目录但仅发布一个产物。仅当存在标记且多个清单文件带有版本号时才拒绝执行,并列出所有找到的带版本号的清单文件。如果工作区仅根目录有版本号,则按正常流程发布。
package.jsonworkspacespnpm-workspace.yamlCargo.toml[workspace]packages/分支保护:探测基准分支一次:
sh
gh api "repos/{owner}/{repo}/branches/$BASE/protection"返回404表示分支未受保护,选择直接发布流程。其他返回结果表示分支受保护,选择发布PR流程。此判断错误会导致严重问题:直接发布流程会将提交直接推送到基准分支,而受保护的仓库会在变更日志已生成后拒绝该推送。
阶段判断:仅在发布PR流程中,从仓库状态判断当前处于发布流程的哪个阶段绝不要依赖自身保存的状态。如果基准分支上存在已合并的提交但未对应标签,则表示处于完成阶段。其他情况则表示处于准备阶段。
chore(release): vX.Y.Z2. Derive the version
2. 推导版本号
Resolve the last release. Take the nearest semver ancestor of HEAD, not the highest version in the repo:
sh
git describe --tags --abbrev=0 --match 'v[0-9]*.[0-9]*.[0-9]*'Filtering to the semver shape stops a tag being mistaken for a release, and taking an ancestor rather than a maximum is what lets a repo running alongside read its own lineage. Skip prereleases, so a tag is passed over and the commits it shipped still appear in the changelog. Cut them at the resolver, so nothing downstream has to know prereleases exist.
nightly-2026-08-01v1.xv2.xv1.3.0-rc.1v1.3.0Read the range , or the whole history when no tag exists. Parse each commit's subject and body, because a footer lives in the body, so bodies cannot be skipped.
<lasttag>..HEADBREAKING CHANGE:Bump from what the range contains, taking the highest that applies:
| in the range | bump on | bump on |
|---|---|---|
a | major | minor |
any | minor | patch |
any | patch | patch |
only | none | none |
The column is not a typo, and it is the rule most likely to surprise someone. Bumping major on a pre-1.0 repo takes to , which declares the API stable off the back of a single commit footer. This follows the release-please and Cargo convention instead. State the rule and its arithmetic in words in the preview, not just the resulting number.
0.x0.3.01.0.0A first release asks. With no tag in the repo, prompt once with prefilled. This happens exactly once per repo and cannot be corrected afterwards, and is a public declaration of API stability that must never be guessed on the user's behalf. Skip the prompt when a manifest already carries a version, because that version is the user's answer.
0.1.01.0.0The tag prefix is inherited, not chosen. gives , gives none. Default to on a first release.
v1.2.0v1.2.0vTwo refusals come out of this step, both with a named override so the refusal is discoverable rather than a dead end:
- Nothing user-visible. The range holds no commit that forces a bump. Say so concretely ("nothing user-visible since v1.2.0") and name for a user who has a reason. A patch bump for every CI tweak makes the version meaningless; a prompt every time trains people to dismiss it.
--allow-empty - Nothing parses. No commit in the range is a Conventional Commit, so any bump would be a guess. Refuse the same way.
Commits that do not parse otherwise proceed. Collect them for an Other section and count them in the preview. Squash-merge repos build subjects from PR titles, and any repo predating the convention has a mixed log, so refusing until every commit parses would make releasekit unusable on both. Nothing is silently dropped; the floor above is what stops the bump becoming fiction.
解析上次发布版本:获取HEAD的最近的semver祖先标签,而非仓库中最高的版本号:
sh
git describe --tags --abbrev=0 --match 'v[0-9]*.[0-9]*.[0-9]*'过滤semver格式的标签可避免将这类标签误认为正式发布版本;选择祖先标签而非最高版本号,可让同时维护和分支的仓库正确读取自身版本谱系。跳过预发布版本,因此这类标签会被忽略,其包含的提交仍会出现在的变更日志中。在解析阶段就过滤预发布版本,下游流程无需处理预发布版本相关逻辑。
nightly-2026-08-01v1.xv2.xv1.3.0-rc.1v1.3.0读取提交范围:,如果没有标签则读取全部提交历史。解析每个提交的标题和正文,因为脚注位于正文中,因此不能跳过正文。
<lasttag>..HEADBREAKING CHANGE:根据提交范围确定版本升级幅度,取适用的最高级别:
| 提交范围包含内容 | 1.x及以上版本的升级幅度 | 0.x版本的升级幅度 |
|---|---|---|
带有 | major(主版本) | minor(次版本) |
任何 | minor(次版本) | patch(修订版本) |
任何 | patch(修订版本) | patch(修订版本) |
仅包含 | 不升级 | 不升级 |
0.x列的规则并非笔误,也是最可能让用户意外的规则。在1.0之前的仓库中升级主版本会将升级到,这意味着仅通过一个提交脚注就声明API稳定。releasekit遵循release-please和Cargo的约定。在预览阶段需用文字说明该规则及其计算方式,而非仅展示最终版本号。
0.3.01.0.0首次发布需确认:如果仓库中没有标签,会以为预设值提示用户确认。此操作每个仓库仅执行一次,且无法事后修改;是API稳定的公开声明,绝不能替用户猜测。如果清单文件已带有版本号,则跳过确认步骤,因为该版本号就是用户的选择。
0.1.01.0.0标签前缀继承自现有标签,而非自行选择。如果现有标签是则前缀为,如果是则无前缀。首次发布时默认使用作为前缀。
v1.2.0v1.2.0v此步骤会触发两种拒绝操作,均提供明确的覆盖选项,让用户可发现解决方式而非陷入死胡同:
- 无用户可见变更:提交范围中没有需要触发版本升级的提交。需明确说明(比如“自v1.2.0以来无用户可见变更”),并告知用户可使用选项。若每次CI调整都触发修订版本升级会让版本号失去意义;频繁提示会让用户养成忽略提示的习惯。
--allow-empty - 无符合规范的提交:提交范围中没有符合Conventional Commits规范的提交,此时任何版本升级都是猜测。同样拒绝执行。
无法解析的提交仍会被处理:将这些提交归类到其他章节,并在预览阶段统计数量。使用合并提交的仓库会从PR标题生成提交标题,任何在引入规范前的仓库都会有混合的提交日志,因此如果要求所有提交都能解析才能运行,releasekit将无法在这两类仓库中使用。不会静默丢弃任何提交;上述的拒绝规则可避免版本升级成为无意义的操作。
3. Render the changelog
3. 生成变更日志
Group the parsed commits into Keep a Changelog sections: Added, Changed, Fixed, Removed, plus Other for the unparsed.
- Breaking changes come first, always in their own section, carrying the migration note from the footer. A reader scanning a release wants the thing that will break them above the thing that delights them.
- Keep the scope, drop the type. Under an Added heading, reads better than
auth: add token refresh retry, because the heading already saidfeat(auth): add token refresh retry.feat - Link each entry to its commit, and to its PR when the merge commit names one.
- Render exactly once. The section and the GitHub release body are the same string. Two renders are two texts that can disagree, and the disagreement always surfaces after the release is public.
CHANGELOG.md
将解析后的提交归类到Keep a Changelog的章节中:新增、变更、修复、移除,以及用于存放无法解析提交的其他章节。
- 破坏性变更始终排在首位,且单独成节,并包含脚注中的迁移说明。用户浏览版本发布信息时,最关心的是可能导致自身代码崩溃的内容,而非新增的功能。
- 保留范围,移除类型。在新增标题下,比
auth: 添加令牌刷新重试机制更易读,因为标题已表明这是feat(auth): 添加令牌刷新重试机制类型的变更。feat - 为每个条目添加对应的提交链接,如果合并提交中包含PR编号,则同时添加PR链接。
- 仅生成一次。中的章节内容与GitHub版本发布的正文完全一致。生成两次会导致内容不一致,且这种不一致往往在版本发布后才会被发现。
CHANGELOG.md
4. Preview and confirm
4. 预览与确认
The preview is mandatory and always renders. ends here having mutated nothing; a bare waits for a confirmation here and mutates nothing until it gets one.
releasekit previewreleasekitLead with the path, then the facts:
- first line. The path (direct or release-PR) and, on the release-PR path, the phase. A misdetected phase is the one failure that costs a duplicate release, so it has to be visible before it is actionable.
- the computed version, and the rule that produced it stated in words;
- the rendered changelog in full;
- the manifest file and its old → new version, or that no manifest was found;
- the tag name;
- the CI check rollup result on the base's head;
- the count of unparsed commits;
- every command about to run.
预览是强制环节,始终会执行。命令会在此阶段结束,不会修改任何内容;不带参数的命令会在此阶段等待用户确认,在获得确认前不会修改任何内容。
releasekit previewreleasekit预览内容先展示流程路径,再展示具体信息:
- 第一行:流程路径(直接发布或发布PR),若为发布PR流程则同时展示阶段。阶段检测错误是唯一可能导致重复发布的故障,因此必须在用户可操作前清晰展示。
- 计算得出的版本号,以及生成该版本号的规则说明;
- 完整的变更日志内容;
- 清单文件及其旧版本→新版本的变化,或提示未找到清单文件;
- 标签名称;
- 基准分支最新提交的CI检查汇总结果;
- 无法解析的提交数量;
- 即将执行的所有命令。
5. Cut the release
5. 完成版本发布
Verify CI first, on either path. Read the check rollup for the base's head commit and refuse to tag a failing one, because a tag on red code spends a version number on something nobody can use. overrides it for a known-flaky required check. When is unusable there is no rollup, so say the check was skipped; never let a missing signal read as green.
--allow-redgh无论采用哪种流程,都先验证CI状态。读取基准分支最新提交的检查汇总结果,拒绝为失败的提交打标签,因为为失败代码打标签会浪费一个版本号在无法使用的代码上。选项可覆盖此规则,适用于已知不稳定的必填检查。如果无法使用工具获取检查汇总结果,则告知用户CI检查已跳过;绝不能将缺失的信号视为检查通过。
--allow-redghThe direct path
直接发布流程
Unprotected base. In this order, because each step depends on the last landing:
- Bump the detected manifest (,
package.json,Cargo.toml, a gemspec). No manifest found is fine and is not an error.pyproject.toml - Prepend the rendered section to , creating the file with a header when it does not exist.
CHANGELOG.md - Commit both together as . One commit, because the bump and the changelog describe the same event.
chore(release): v<version> - Create an annotated tag whose message is the release title.
- Push the commit and the tag.
- Create the GitHub release with , using the rendered body.
gh release create
适用于未受保护的基准分支。按以下顺序执行,因为每个步骤都依赖上一步完成:
- 更新检测到的清单文件(、
package.json、Cargo.toml、gemspec文件)。未找到清单文件属于正常情况,不会报错。pyproject.toml - 将生成的变更日志章节添加到的开头,如果文件不存在则创建并添加头部内容。
CHANGELOG.md - 将清单文件更新和变更日志更新合并为一个提交,提交信息为。使用单个提交,因为版本更新和变更日志描述的是同一个事件。
chore(release): v<version> - 创建带注释的标签,标签信息为版本发布标题。
- 推送提交和标签。
- 使用命令创建GitHub版本发布,使用生成的变更日志作为正文。
gh release create
The release-PR path
发布PR流程
Protected base. Two invocations, and the phase resolved in Preflight decides which one runs.
Prepare. Do the manifest bump, the changelog, and the commit on a branch. Push it and open a PR whose body is the rendered changelog. Stop there. The user reviews and merges it.
release-v<version>Finish. The merged, untagged commit is the trigger. Verify the rollup, tag that commit, push the tag, and create the GitHub release. The changelog is already in the repo from the merge, so nothing is rewritten and nothing is committed.
chore(release):If any step on either path fails, stop at that step and report exactly what landed and what did not. Never roll back a pushed tag; see the refusals in Notes.
适用于受保护的基准分支。分为两次调用,预检阶段确定的阶段决定执行哪一次操作。
准备阶段:在分支上执行清单文件更新、变更日志生成和提交操作。推送该分支并创建PR,PR正文为生成的变更日志。到此为止。由用户审核并合并该PR。
release-v<version>完成阶段:已合并的、未打标签的提交是触发条件。验证CI检查汇总结果,为该提交打标签,推送标签,并创建GitHub版本发布。变更日志已通过PR合并到仓库中,因此无需重新生成或提交。
chore(release):无论哪种流程,若任何步骤失败,则在该步骤停止,并准确告知用户哪些操作已完成,哪些未完成。绝不回滚已推送的标签;具体说明请参考注意事项。
6. Hand off
6. 交接
Write this section in the procedural register: one instruction per sentence, active voice, present tense, no metaphor.
The hand-off differs by path, so report the one that ran.
Direct path, and release-PR finish. This is terminal.
- What changed. The version, the files bumped, and the commit created.
- Where it landed. The tag name, the release URL, and the branch pushed.
- Next. There is no next kit. Say that pushing the tag may have started a publish workflow, and that releasekit does not own or watch it. Name for a typo in the notes.
gh release edit
Release-PR path, prepare. This is not terminal.
- What changed. The release branch, the commit on it, and the PR opened. Say plainly that no tag exists yet.
- Where it landed. The branch name and the PR URL.
- Next. Review and merge the PR. Use mergekit when it is installed, otherwise . Then run releasekit again to tag the merged commit.
gh pr merge
此部分采用流程化表述:每句一个指令,主动语态,现在时,无隐喻。
交接内容因流程而异,需告知用户实际执行的流程。
直接发布流程和发布PR流程的完成阶段:此为终端操作。
- 变更内容:版本号、已更新的文件、已创建的提交。
- 结果位置:标签名称、版本发布URL、已推送的分支。
- 后续操作:没有后续工具操作。告知用户推送标签可能已触发发布工作流,releasekit不负责或监控该工作流。若版本发布说明存在拼写错误,可使用命令修改。
gh release edit
发布PR流程的准备阶段:此为非终端操作。
- 变更内容:发布分支、分支上的提交、已创建的PR。明确告知用户尚未创建标签。
- 结果位置:分支名称、PR URL。
- 后续操作:审核并合并PR。如果已安装mergekit则使用它,否则使用。之后再次运行releasekit为合并后的提交打标签。
gh pr merge
Notes
注意事项
- Four refusals, and each one is a refusal rather than a caution. releasekit stands down when another release tool owns the repo. It refuses a workspace with several versioned packages. It refuses to tag a red base. It never moves or deletes a published tag, because re-tagging breaks every consumer that already resolved it. A bad release is fixed by cutting the next one.
- Fixing a released typo is out of scope. The notes on a published release are editable by nature, but adding an edit path would make a third entry point out of a skill with one procedure and two exits. Run instead.
gh release edit - Prereleases are read, never written. releasekit passes over an existing tag so a repo already using them can still cut a stable release. It does not create one.
-rc.N - One version per repo. Per-package versioning in a monorepo is a different skill with a different data model, and guessing at it silently is worse than refusing.
- It never runs unattended. The confirmation in Preview and confirm is a consent gate with no exemption. An orchestrator that reaches it with nobody to answer must stop and escalate, not proceed.
- Follow the repo over these defaults. A repo with its own changelog format, a at a different path, or a documented release procedure gets that, and say you followed it.
CHANGELOG - is declared for one call only, which is asking
Skillfor the base ref. releasekit invokes no other skill; naming mergekit in the hand-off is routing, not launching.gitkit - No filesystem or shell (a browser-based agent, say)? Then you cannot read the log or cut anything. Instead print the derived version, the rendered changelog as a codeblock, and the exact and
git tagcommands for the user to run.gh release create
- 四种拒绝场景,均为直接拒绝而非警告:当其他发布工具管理仓库时,releasekit会停止操作。它会拒绝包含多个带版本号包的工作区。它会拒绝为CI失败的基准分支打标签。它绝不会移动或删除已发布的标签,因为重新打标签会破坏所有已解析该标签的消费者。错误的版本发布需通过创建下一个版本来修复。
- 修复已发布版本的拼写错误不在范围内:已发布版本的说明本质上是可编辑的,但添加编辑流程会让这个仅有一个流程和两个出口的工具增加第三个入口。请使用命令修改。
gh release edit - 仅读取预发布版本,不生成预发布版本:releasekit会跳过已存在的标签,因此已使用预发布版本的仓库仍可创建稳定版本。它不会生成预发布版本。
-rc.N - 每个仓库一个版本号:单仓多包仓库的按包版本管理是不同的功能,有不同的数据模型,静默猜测该逻辑比直接拒绝更糟糕。
- 绝不无人值守运行:预览与确认阶段的确认步骤是不可跳过的同意环节。无人应答的编排工具必须停止并上报,而非继续执行。
- 优先遵循仓库自身配置而非默认规则:如果仓库有自己的变更日志格式、文件位于其他路径或有文档化的发布流程,则遵循仓库的配置,并告知用户已按仓库配置执行。
CHANGELOG - 仅在调用获取基准分支时声明
gitkit:releasekit不会调用其他工具;在交接部分提及mergekit是路由指引,而非启动该工具。Skill - 无文件系统或Shell环境(比如基于浏览器的Agent)?此时无法读取提交日志或执行任何操作。取而代之的是,输出推导的版本号、以代码块形式展示生成的变更日志,以及供用户执行的精确和
git tag命令。gh release create