release
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chineserelease
release
Cuts a versioned release: pick the next semantic version, write proper release notes, tag the commit, and publish the GitHub release.
This sits next to and answers a different question. wires the release plumbing once (, the tag-driven workflow). This skill is the ceremony you run each time you ship: decide the bump, draft the notes, tag, publish. Use the CLI directly for one-off release commands outside this ceremony.
/gh-bootstrap/gh-bootstrap.github/release.ymlgh执行带版本号的 Release:选择下一个语义化版本,编写规范的发布说明,为 commit 打 tag,并发布 GitHub Release。
本 skill 与 相邻,但解决的问题不同。 负责一次性配置 Release 所需的基础设施(、由 tag 驱动的 workflow)。本 skill 是每次发布时执行的流程:决定版本号、起草发布说明、打 tag、发布。对于此类正式发布流程之外的一次性 Release 命令,直接使用 CLI。
/gh-bootstrap/gh-bootstrap.github/release.ymlghWhere this skill fits
本 skill 的定位
| Concern | Skill |
|---|---|
| One-time release-notes config + tag-driven workflow | |
| Deciding the version, drafting notes, tagging, publishing | |
If the repo has a tag-driven release workflow (from ), pushing the tag is enough — the workflow publishes the release. This skill still decides the version and (optionally) drafts curated notes. After the tag push, it does not run . It waits for the workflow, then applies approved curated notes. Otherwise it publishes the release itself with .
/gh-bootstrapgh release creategh release create| 关注事项 | Skill |
|---|---|
| 一次性发布说明配置及 tag 驱动的 workflow | |
| 决定版本、起草说明、打 tag、发布 | |
如果仓库已有由 tag 驱动的 Release workflow(来自 ),那么仅推送 tag 即可——workflow 会负责发布 Release。本 skill 仍会决定版本号,并可选地起草人工整理的发布说明。推送 tag 后,本 skill 不会运行 ,而是等待 workflow 完成,再应用已批准的人工整理内容。否则,本 skill 会自行通过 发布 Release。
/gh-bootstrapgh release creategh release createProtocol
执行协议
1. Pre-flight
1. 发布前检查
Refuse to release from a dirty or diverged state — a release tag is permanent and public.
bash
git fetch --tags origin
DEFAULT=$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name)
git diff-index --quiet HEAD || { echo "uncommitted changes — commit or stash first"; exit 1; }
[ "$(git rev-parse HEAD)" = "$(git rev-parse "origin/$DEFAULT")" ] || { echo "HEAD is not the tip of origin/$DEFAULT — check out and update it first"; exit 1; }
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")Confirm with the user:
- The commit to tag. Only the tip of is taggable.
origin/$DEFAULT - The last release tag () — the baseline for the version bump and the notes range. If the repo has no tags yet, this is the first release.
$LAST_TAG
Stop and tell the user — don't tag — if the tree is dirty or is not the tip of .
HEADorigin/$DEFAULT拒绝从工作区不干净或已发生分叉的状态执行发布——Release tag 是永久且公开的。
bash
git fetch --tags origin
DEFAULT=$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name)
git diff-index --quiet HEAD || { echo "uncommitted changes — commit or stash first"; exit 1; }
[ "$(git rev-parse HEAD)" = "$(git rev-parse "origin/$DEFAULT")" ] || { echo "HEAD is not the tip of origin/$DEFAULT — check out and update it first"; exit 1; }
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")向用户确认:
- 要打 tag 的 commit。只有 的最新提交可以打 tag。
origin/$DEFAULT - 上一个 Release tag()——它是版本递增和发布说明范围的基准。如果仓库尚无 tag,则这是首次发布。
$LAST_TAG
如果工作区不干净,或者 不是 的最新提交,请停止并告知用户,不要打 tag。
HEADorigin/$DEFAULT2. Decide the version
2. 决定版本号
Read for the full decision rules. The short version: scan the Conventional Commits since the last tag and map the highest-impact change to a bump.
references/semver.mdbash
git log "${LAST_TAG:+$LAST_TAG..}HEAD" --no-merges --pretty='%s'| Highest-impact change since last tag | Bump (≥ 1.0.0) | Bump (0.x pre-1.0) |
|---|---|---|
| major | minor |
| minor | patch |
| patch | patch |
only | patch (or skip — ask) | patch |
Match the repo's existing tag scheme — if is , the next is , not . Detect and reuse the prefix (or its absence). Detect prerelease identifiers (, ) and keep the user's convention.
$LAST_TAGv1.2.3v1.3.01.3.0v-rc.1-beta.2Propose the computed version and the one-line reason ("3 feats, no breaking → minor → v1.3.0"). Let the user confirm or override. Never tag a version the user hasn't seen.
阅读 以了解完整的版本决策规则。简而言之:扫描上一个 tag 以来的 Conventional Commits,并将影响最大的变更映射为一次版本递增。
references/semver.mdbash
git log "${LAST_TAG:+$LAST_TAG..}HEAD" --no-merges --pretty='%s'| 上一个 tag 以来影响最大的变更 | 递增(≥ 1.0.0) | 递增(0.x,尚未进入 1.0) |
|---|---|---|
| major | minor |
| minor | patch |
| patch | patch |
仅有 | patch(或跳过——询问用户) | patch |
匹配仓库现有的 tag 命名方案——如果 是 ,下一个版本就是 ,而不是 。检测并复用 前缀(或确认没有该前缀)。检测预发布标识符(如 、),并遵循用户现有的命名约定。
$LAST_TAGv1.2.3v1.3.01.3.0v-rc.1-beta.2给出计算出的版本号及一句话理由(“3 个 feat,无 breaking → minor → v1.3.0”)。让用户确认或覆盖该结果。绝不要在用户尚未看到版本号前为其打 tag。
3. Draft the release notes
3. 起草发布说明
Read for the three strategies and how to choose. Summary:
references/release-notes.md- Auto-generated () — best when the repo merges via PRs and has
gh release create --generate-notes. GitHub groups merged PRs into the configured category buckets by label. Cheapest, and complete as long as every change landed as a labelled PR..github/release.yml - Hand-curated — best when there's no , when commits (not PRs) are the unit of change, or when the release needs editorial highlights. Group by Conventional Commit type; lead with a Highlights section and, for any breaking change, an Upgrade notes section with the concrete migration step.
.github/release.ymlis the starting structure.assets/release-notes-template.md - Hybrid — generate, then hand-edit the top to add highlights and upgrade notes. The most common choice for a release that matters.
When curating, derive the entries from the commit log, not from imagination — every bullet must trace to a real commit. Link PRs/commits where the audience would want the detail.
阅读 ,了解三种策略及其选择方式。摘要如下:
references/release-notes.md- 自动生成()——最适合通过 PR 合并且拥有
gh release create --generate-notes的仓库。GitHub 会根据 label 将已合并的 PR 归入配置好的类别。成本最低;只要每项变更都以带 label 的 PR 形式合入,就能完整覆盖所有变更。.github/release.yml - 人工整理——最适合没有 、以 commit(而非 PR)作为变更单位,或需要编辑性重点内容的 Release。按 Conventional Commit 类型分组;以 Highlights 部分开头,对于任何 breaking change,还应添加 Upgrade notes 部分并给出具体的迁移步骤。
.github/release.yml可作为起始结构。assets/release-notes-template.md - 混合——先生成内容,再手工编辑顶部以添加重点内容和升级说明。对于重要 Release,这是最常见的选择。
人工整理时,应从 commit 日志推导条目,而不是凭空编写——每一个列表项都必须能追溯到真实 commit。在受众需要了解细节的地方链接对应的 PR 或 commit。
4. Update CHANGELOG.md (if the repo keeps one)
4. 更新 CHANGELOG.md(如果仓库维护此文件)
If exists, keep it in sync. Most repos follow Keep a Changelog: move the entries under a new heading, leave a fresh empty , and update the comparison links at the bottom. If there's no changelog and the user doesn't want one, skip this — don't introduce a changelog they didn't ask for.
CHANGELOG.md## [Unreleased]## [VERSION] - YYYY-MM-DDUnreleasedThe tag must include the changelog entry. Commit the changelog change and land it on the default branch through the repository's normal merge flow. Push only with explicit user approval. Then repeat steps 1-3 on the new tip of the default branch. A squash merge gives a new SHA, so tag the tip that step 1 checks, not your local commit. Compare with the drafted notes. Stop and tell the user if commits other than the changelog commit landed.
git log "${LAST_TAG:+$LAST_TAG..}HEAD"如果存在 ,请保持其同步。大多数仓库遵循 Keep a Changelog:将 下的条目移动到新的 标题下,保留一个全新的空 部分,并更新底部的比较链接。如果仓库没有 changelog,且用户也不需要,则跳过——不要擅自引入 changelog。
CHANGELOG.md## [Unreleased]## [VERSION] - YYYY-MM-DDUnreleasedtag 所指向的提交必须包含 changelog 修改。提交 changelog 变更,并通过仓库正常的 merge 流程将其合入默认分支。仅在获得用户明确批准后推送。然后在默认分支的新最新提交上重复步骤 1-3。Squash merge 会生成新的 SHA,因此应给步骤 1 所检查的最新提交打 tag,而不是本地提交。对比 与起草的发布说明。如果除 changelog 提交外还有其他 commit 合入,请停止并告知用户。
git log "${LAST_TAG:+$LAST_TAG..}HEAD"5. Tag and push
5. 打 tag 并推送
Annotated tag (carries a message, author, and date — lightweight tags don't, and treats them differently). Tag the agreed commit, then push only the tag.
git describeIf a tag-driven release workflow exists (for example ), read its arguments before you assume generated notes. The workflow publishes generated notes only (). If the workflow publishes generated notes and step 3 chose curated or hybrid notes, tell the user before you push the tag.
.github/workflows/release.ymlgh release create/gh-bootstrap--generate-notesAsk for explicit user approval before you push the tag.
bash
VERSION=v1.3.0
git tag -a "$VERSION" -m "$VERSION" # add -s to GPG-sign if the repo signs tags
git push origin "$VERSION"If a tag-driven release workflow exists, skip step 6. Wait for the workflow, then apply approved curated notes with . Apply the notes only with user approval. Report the workflow run URL.
gh release edit "$VERSION" --notes-file NOTES.md使用 annotated tag(包含 message、author 和 date;lightweight tag 不包含这些信息,而且 对两者的处理方式不同)。为双方约定的 commit 打 tag,然后只推送该 tag。
git describe如果存在由 tag 驱动的 Release workflow(例如 ),请先读取其中的 参数,不要想当然地认为它会生成发布说明。 workflow 仅发布自动生成的说明()。如果 workflow 会发布自动生成的说明,而步骤 3 选择的是人工整理或混合策略,请在推送 tag 前告知用户。
.github/workflows/release.ymlgh release create/gh-bootstrap--generate-notes推送 tag 前必须获得用户明确批准。
bash
VERSION=v1.3.0
git tag -a "$VERSION" -m "$VERSION" # add -s to GPG-sign if the repo signs tags
git push origin "$VERSION"如果存在由 tag 驱动的 Release workflow,请跳过步骤 6。等待 workflow 完成,然后使用 应用已批准的人工整理内容。仅在获得用户批准后应用这些说明。报告 workflow run URL。
gh release edit "$VERSION" --notes-file NOTES.md6. Publish the GitHub release
6. 发布 GitHub Release
Only when there's no tag-driven workflow doing it for you.
Ask for explicit user approval before you run .
gh release createbash
undefined仅在没有由 tag 驱动的 workflow 代为发布时执行此步骤。
运行 前必须获得用户明确批准。
gh release createbash
undefinedAuto-generated notes:
Auto-generated notes:
gh release create "$VERSION" --title "$VERSION" --generate-notes --latest
gh release create "$VERSION" --title "$VERSION" --generate-notes --latest
Curated notes from a file:
Curated notes from a file:
gh release create "$VERSION" --title "$VERSION" --notes-file NOTES.md --latest
gh release create "$VERSION" --title "$VERSION" --notes-file NOTES.md --latest
Prerelease (rc/beta/alpha) — do NOT mark --latest:
Prerelease (rc/beta/alpha) — do NOT mark --latest:
gh release create "$VERSION" --title "$VERSION" --notes-file NOTES.md --prerelease
gh release create "$VERSION" --title "$VERSION" --notes-file NOTES.md --prerelease
Draft for review before going public:
Draft for review before going public:
gh release create "$VERSION" --title "$VERSION" --notes-file NOTES.md --draft
Attach build artifacts by listing them after the tag (`gh release create "$VERSION" ... dist/*.tar.gz`) when the project ships binaries.gh release create "$VERSION" --title "$VERSION" --notes-file NOTES.md --draft
当项目分发二进制文件时,可以在 tag 后列出构建产物以附加到 Release(`gh release create "$VERSION" ... dist/*.tar.gz`)。Immutable releases
不可变 Release
GitHub immutable releases became generally available on 2025-10-28. When the
repository enables them, a published release keeps its assets and tag fixed
and carries signed Sigstore attestations. You can still edit the title and
notes of a published immutable release, but you cannot change its assets or
move its tag. Verify the attached assets and the tagged commit before you
publish. See for the citations.
references/release-notes.mdGitHub 不可变 Release 于 2025-10-28 正式全面可用。仓库启用此功能后,已发布的 Release 会固定其 assets 和 tag,并附带已签名的 Sigstore attestations。你仍然可以编辑已发布不可变 Release 的标题和说明,但不能更改其 assets 或移动其 tag。发布前请核对已附加的 assets 和被打 tag 的 commit。有关引用来源,请参阅 。
references/release-notes.md7. Verify and report
7. 验证并报告
Read the published state back — don't claim success from the create command's exit code alone.
bash
gh release view "$VERSION" --json tagName,isDraft,isPrerelease,url --jq .
gh release list --json tagName,isLatest --jq '.[] | select(.isLatest) | .tagName'
git ls-remote --tags origin "$VERSION"Report to the user: the version and why, the notes strategy used, the release URL, and whether it's draft/prerelease/latest. Call out anything skipped (e.g. "workflow is publishing — release not yet visible") and why.
回读已发布状态——不要仅凭 create 命令的退出代码就宣称成功。
bash
gh release view "$VERSION" --json tagName,isDraft,isPrerelease,url --jq .
gh release list --json tagName,isLatest --jq '.[] | select(.isLatest) | .tagName'
git ls-remote --tags origin "$VERSION"向用户报告:版本号及其原因、采用的发布说明策略、Release URL,以及它是 draft、prerelease 还是 latest。明确说明任何被跳过的步骤(例如“workflow 正在发布——Release 尚不可见”)及其原因。
What this skill is NOT for
本 skill 不适用于
- Scaffolding or the release workflow — that's
.github/release.yml(run once, first)./gh-bootstrap - Looking up raw command flags outside a release ceremony — use the
gh releaseCLI reference directly.gh - Committing or pushing branch code before the release ceremony starts.
- Publishing to a package registry (npm / crates.io / PyPI). This skill tags and creates the GitHub release; registry publishing is the project's own /CI step.
just - Per-package versioning in a monorepo with independent release lines — out of scope; this skill cuts one tag for one version line. Say so if the repo is a multi-package monorepo.
- 搭建 或 Release workflow——那属于
.github/release.yml(首次执行)。/gh-bootstrap - 在正式发布流程之外查阅原始 命令参数——请直接查阅
gh releaseCLI 参考文档。gh - 在发布流程开始前提交或推送分支代码。
- 发布到软件包 registry(npm / crates.io / PyPI)。本 skill 负责打 tag 并创建 GitHub Release;registry 发布由项目自身的 /CI 步骤完成。
just - monorepo 中各 package 拥有独立 Release line 时的 per-package 版本管理——这不在本 skill 的范围内;本 skill 每次只为一个版本 line 创建一个 tag。如果仓库属于多 package monorepo,请明确告知用户。
Gotchas
常见陷阱
- Never re-point an existing tag. +
git tag -fto move a published tag breaks every consumer who already fetched it. If you tagged the wrong commit, cut a new patch version instead.git push -f - Auto-generated notes only see merged PRs. Direct pushes to the default branch don't appear. A repo without PR-based merges (no branch protection) should curate notes, not rely on .
--generate-notes - needs
--generate-notesto group nicely; without it you get a flat "What's Changed" list. If the grouping looks wrong, the categories/labels in.github/release.ymlare the lever — fix them viarelease.yml, not here./gh-bootstrap - Prereleases must not be . Marking an
--latestbuild as latest makes it the default download and the target of-rc. Usereleases/latestand omit--prerelease.--latest - Pre-1.0 semver is different. Under , breaking changes bump the minor, not the major. See
0.x.references/semver.md - Tag must be an ancestor of the default branch. Tagging a feature-branch commit ships unreviewed code. Confirm the commit is on (or merged into) the default branch before tagging.
- Signed tags — if the repo signs releases (or a project convention), use
git config tag.gpgSign true; an unsigned tag on a signed-release repo fails verification downstream.git tag -s
- 绝不能重新指向已有 tag。 使用 配合
git tag -f移动已发布的 tag,会破坏所有已经获取该 tag 的消费者。如果给错误的 commit 打了 tag,应改为发布一个新的 patch 版本。git push -f - 自动生成的说明只能看到已合并的 PR。 直接推送到默认分支的变更不会出现。没有基于 PR 的合并方式(也没有分支保护)的仓库应人工整理说明,而不是依赖 。
--generate-notes - 需要
--generate-notes才能进行良好分组。 如果没有该文件,只会得到扁平的 “What's Changed” 列表。如果分组不正确,应调整.github/release.yml中的类别或 label——通过release.yml修复,而不是在此处处理。/gh-bootstrap - 预发布版本绝不能标记为 。 将
--latest构建标记为 latest 会使其成为默认下载版本,并成为-rc的目标。应使用releases/latest,并省略--prerelease。--latest - 1.0 之前的 SemVer 规则有所不同。 在 版本下,breaking changes 会递增 minor,而不是 major。参阅
0.x。references/semver.md - tag 必须是默认分支的祖先。 给 feature branch 上的 commit 打 tag 会发布未经审查的代码。打 tag 前请确认该 commit 位于默认分支上或已合入默认分支。
- 签名 tag——如果仓库会签署 Release(或项目有相应约定),请使用
git config tag.gpgSign true;在要求签名 Release 的仓库中使用未签名 tag,会导致后续验证失败。git tag -s