gh-bootstrap
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesegh-bootstrap
gh-bootstrap
One-shot repository configuration for the merge-queue + squash-merge + auto-release-notes pattern.
This is one-time repo policy, distinct from per-task GitHub operations: how should this repo be configured in the first place?
为 merge-queue + squash-merge + auto-release-notes 模式提供一次性仓库配置。
这是一次性仓库策略,与逐任务 GitHub 操作不同:首先要决定这个仓库应如何配置?
What gets configured
将配置哪些内容
| Concern | Result | Endpoint |
|---|---|---|
| Repo merge defaults | Squash-only; PR title → commit subject; delete branches on merge | |
| Default branch protection | Ruleset with PR and required checks, no force-push, no deletion; optional queue | |
| Release notes | | File commit |
| Release workflow (optional) | Tag push → | File commit |
All policy endpoints (repo settings, rulesets, branch protection) run through the CLI — for the JSON endpoints, / where there's a first-class subcommand. The skill writes files (, the optional release workflow) directly to the working tree.
ghgh apigh repogh release.github/release.yml| 配置项 | 结果 | 端点 |
|---|---|---|
| 仓库合并默认值 | 仅允许 Squash;PR 标题 → commit subject;合并后删除分支 | |
| 默认分支保护 | 包含 PR 和必需检查的 Ruleset;禁止 force-push 和删除;可选 queue | |
| Release notes | | 文件提交 |
| Release workflow(可选) | 推送 Tag → 在探测到的默认分支上执行 | 文件提交 |
所有策略端点(仓库设置、rulesets、branch protection)都通过 CLI 执行——JSON 端点使用 ,存在原生子命令的则使用 /。该 skill 会将文件(、可选的 release workflow)直接写入工作树。
ghgh apigh repogh release.github/release.ymlProtocol
协议
1. Confirm scope and inspect current state
1. 确认范围并检查当前状态
Before changing anything, read the runway and confirm with the user:
bash
gh auth status
REPO="$(gh repo view --json nameWithOwner --jq '.nameWithOwner')"
gh repo view "$REPO" --json nameWithOwner,visibility,defaultBranchRef,isPrivate
DEFAULT_BRANCH="$(gh repo view "$REPO" --json defaultBranchRef --jq '.defaultBranchRef.name')"
gh api "repos/$REPO" --jq '{
squash: .allow_squash_merge,
merge: .allow_merge_commit,
rebase: .allow_rebase_merge,
delete: .delete_branch_on_merge,
title: .squash_merge_commit_title,
body: .squash_merge_commit_message,
auto: .allow_auto_merge
}'
gh api "repos/$REPO/rulesets" # any rulesets already?在更改任何内容前,先读取 runway 并与用户确认:
bash
gh auth status
REPO="$(gh repo view --json nameWithOwner --jq '.nameWithOwner')"
gh repo view "$REPO" --json nameWithOwner,visibility,defaultBranchRef,isPrivate
DEFAULT_BRANCH="$(gh repo view "$REPO" --json defaultBranchRef --jq '.defaultBranchRef.name')"
gh api "repos/$REPO" --jq '{
squash: .allow_squash_merge,
merge: .allow_merge_commit,
rebase: .allow_rebase_merge,
delete: .delete_branch_on_merge,
title: .squash_merge_commit_title,
body: .squash_merge_commit_message,
auto: .allow_auto_merge
}'
gh api "repos/$REPO/rulesets" # any rulesets already?Legacy branch protection: a 404 with "Branch not protected" means none
Legacy branch protection: a 404 with "Branch not protected" means none
exists. Any other error must stop the protocol before rulesets are applied.
exists. Any other error must stop the protocol before rulesets are applied.
if legacy_protection=$(gh api "repos/$REPO/branches/$DEFAULT_BRANCH/protection" 2>&1); then
echo "$legacy_protection"
elif [[ "$legacy_protection" == "HTTP 404" && "$legacy_protection" == "Branch not protected" ]]; then
echo "No legacy branch protection on $DEFAULT_BRANCH."
else
echo "::error::Could not check legacy branch protection: $legacy_protection" >&2
exit 1
fi
Ask the user:
- Default branch (assume the repo's `defaultBranchRef`, usually `main`)
- Required status check **names** (e.g. `validate / validate skills`, `ci`, `test`) — these must match what CI emits
- Required approving review count (default `1`; `0` is fine for solo repos)
- Whether to scaffold the optional release workflow, or just `.github/release.yml`
**Merge queue availability.** GitHub supports merge queues on public organization-owned repositories and on private organization-owned repositories with GitHub Enterprise Cloud. A user-owned repository, including a public user-owned repository, is not eligible. Skip the `merge_queue` rule unless the repository and plan meet those requirements.if legacy_protection=$(gh api "repos/$REPO/branches/$DEFAULT_BRANCH/protection" 2>&1); then
echo "$legacy_protection"
elif [[ "$legacy_protection" == "HTTP 404" && "$legacy_protection" == "Branch not protected" ]]; then
echo "No legacy branch protection on $DEFAULT_BRANCH."
else
echo "::error::Could not check legacy branch protection: $legacy_protection" >&2
exit 1
fi
向用户询问:
- 默认分支(假设为仓库的 `defaultBranchRef`,通常是 `main`)
- 必需 status check 的**名称**(例如 `validate / validate skills`、`ci`、`test`)——这些名称必须与 CI 输出的名称一致
- 必需的 approving review 数量(默认 `1`;个人仓库使用 `0` 也可以)
- 是生成可选的 release workflow,还是只生成 `.github/release.yml`
**Merge queue 可用性。** GitHub 支持在组织拥有的公共仓库,以及使用 GitHub Enterprise Cloud 的组织拥有的私有仓库中使用 merge queue。用户拥有的仓库(包括公共的用户仓库)不符合条件。除非仓库和套餐满足这些要求,否则请跳过 `merge_queue` 规则。2. Set repo merge defaults
2. 设置仓库合并默认值
Single lands all the merge-button settings atomically:
PATCHbash
gh api -X PATCH "repos/$REPO" \
-F allow_squash_merge=true \
-F allow_merge_commit=false \
-F allow_rebase_merge=false \
-F allow_auto_merge=true \
-F delete_branch_on_merge=true \
-F squash_merge_commit_title=PR_TITLE \
-F squash_merge_commit_message=PR_BODYWhy each flag:
- ,
allow_squash_merge=true,allow_merge_commit=false— squash is the only button on a merged PR, soallow_rebase_merge=falsehistory is one commit per merged PR.main - — the branch is gone anyway once it's been collapsed into one commit; no point keeping the ref.
delete_branch_on_merge=true - — the squash commit's subject line is the PR title verbatim. This makes
squash_merge_commit_title=PR_TITLEongit log --onelineread as a list of merged PRs.main - — the PR body becomes the commit body, so the rationale for each change survives in
squash_merge_commit_message=PR_BODYwithout needing GitHub.git log - — required for the merge queue button to enqueue PRs without human babysitting.
allow_auto_merge=true
PATCH单次 会原子性地应用所有合并按钮设置:
PATCHbash
gh api -X PATCH "repos/$REPO" \
-F allow_squash_merge=true \
-F allow_merge_commit=false \
-F allow_rebase_merge=false \
-F allow_auto_merge=true \
-F delete_branch_on_merge=true \
-F squash_merge_commit_title=PR_TITLE \
-F squash_merge_commit_message=PR_BODY各参数的作用:
- 、
allow_squash_merge=true、allow_merge_commit=false——合并 PR 时只保留 squash 这一个按钮,因此allow_rebase_merge=false的历史记录中,每个合并的 PR 只对应一个 commit。main - ——分支在折叠成一个 commit 后本来就会消失,没有保留引用的意义。
delete_branch_on_merge=true - ——squash commit 的 subject line 原样使用 PR 标题。这样,
squash_merge_commit_title=PR_TITLE上的main看起来就像已合并 PR 的列表。git log --oneline - ——PR 正文成为 commit 正文,因此每次变更的依据都会保留在
squash_merge_commit_message=PR_BODY中,无需依赖 GitHub。git log - ——merge queue 按钮需要此设置,才能在无人值守的情况下将 PR 加入队列。
allow_auto_merge=true
PATCH3. Lock the default branch with a ruleset
3. 使用 ruleset 锁定默认分支
Rulesets are GitHub's modern replacement for branch protection — they support merge queues as a first-class rule, layer cleanly with org rulesets, and are queryable via the API. Default to a ruleset; only fall back to classic branch protection if the user explicitly asks (e.g. they are pinned to an older GHE version).
The full payloads, the asset templates, and the create-vs-update logic live in — read that file before this step. The no-queue variant ships as an asset:
references/ruleset.md| Variant | Asset | Pick when |
|---|---|---|
| | Solo or small repo. Squash-only via |
| inline JSON in | Team repo with concurrent PR throughput. Adds the |
Both variants share the same shape:
- ,
target: "branch"conditions.ref_name.include: ["~DEFAULT_BRANCH"] enforcement: "active"- Rules: ,
deletion,non_fast_forward,pull_requestrequired_status_checks
Default to the variant unless the user explicitly asks for the merge queue. It's the simpler shape and the one that matches everyday solo work.
main: PR + CIIdempotency: list rulesets and look for one targeting the default branch whose name matches either bootstrap variant ( or ). If found, it, diff its , , and against the rendered payload, show the diff, and ask the user before -ing. When the user switches variants, reuse that ruleset's id for the instead of creating a second one, and ask before disabling or retiring anything from the prior variant. If absent, a new one. Never create a duplicate ruleset for the default branch.
main: PR + CImain-protectionGETrulesbypass_actorsconditionsPUTPUTPOSTRulesets 是 GitHub 对 branch protection 的现代替代方案——它们将 merge queue 作为一等规则提供支持,能够与组织级 rulesets 干净地配合,并且可通过 API 查询。默认使用 ruleset;仅当用户明确要求时才回退到 classic branch protection(例如,他们被固定在较旧的 GHE 版本上)。
完整 payload、asset 模板以及创建或更新的逻辑位于 ——执行此步骤前先阅读该文件。无需 queue 的变体以 asset 形式提供:
references/ruleset.md| 变体 | Asset | 选择时机 |
|---|---|---|
| | 个人或小型仓库。通过 |
| | 需要并发 PR 处理能力的团队仓库。增加 |
两个变体采用相同的结构:
- 、
target: "branch"conditions.ref_name.include: ["~DEFAULT_BRANCH"] enforcement: "active"- 规则:、
deletion、non_fast_forward、pull_requestrequired_status_checks
除非用户明确要求使用 merge queue,否则默认采用 变体。它更简单,也符合日常个人工作的需要。
main: PR + CI**幂等性:**列出 rulesets,并查找名称与任一 bootstrap 变体( 或 )匹配且以默认分支为目标的 ruleset。如果找到,则通过 获取它,将其 、 和 与渲染后的 payload 进行比较,展示差异,并在执行 前征求用户同意。当用户切换变体时,复用该 ruleset 的 id 执行 ,而不是创建第二个 ruleset;停用或移除先前变集中的任何内容之前,必须征求用户同意。如果不存在,则通过 创建一个。绝不为默认分支创建重复的 ruleset。
main: PR + CImain-protectionGETrulesbypass_actorsconditionsPUTPUTPOST4. Scaffold the release-notes config
4. 生成 Release notes 配置
Copy to . This file is consumed by GitHub's auto-generated release notes (the "Generate release notes" button on the New Release page, and ). It groups merged PRs into Breaking / Features / Fixes / Docs / Internal / Other buckets driven by PR labels.
assets/release.yml.github/release.ymlgh release create --generate-notesIf already exists, diff it against the template and ask before overwriting. Don't silently clobber the user's existing categories.
.github/release.yml将 复制到 。GitHub 的自动生成 release notes 会使用此文件(New Release 页面上的 "Generate release notes" 按钮,以及 )。它会根据 PR labels 将已合并的 PR 分为 Breaking / Features / Fixes / Docs / Internal / Other 等分类 buckets。
assets/release.yml.github/release.ymlgh release create --generate-notes如果 已存在,将其与模板进行比较,并在覆盖前征求用户同意。不要擅自覆盖用户现有的分类配置。
.github/release.yml5. Scaffold the release workflow (optional)
5. 生成 Release workflow(可选)
If the user opted in, copy to . The workflow:
assets/release-workflow.yml.github/workflows/release.yml- Triggers on to
pushtagsv* - Runs a job (
verify,contents: read) that checks out the repo, discovers the default branch, and refuses to continue if the tag is not its ancestor — so unreviewed branches can't ship a release. Add the caller's setup and quality gate to this job.persist-credentials: false - Runs a job (
publish,contents: write, no checkout) that callsneeds: verify, addinggh release create "$TAG" --generate-notes --verify-tagwhen the tag contains a--prerelease-
Splitting from keeps the job that checks out and gates repository code from ever holding . Action SHAs are pinned (not floating tags) for supply-chain hygiene. documents which actions are pinned and how to refresh them when a new version comes out.
verifypublishcontents: writereferences/release.md如果用户选择启用,则将 复制到 。该 workflow:
assets/release-workflow.yml.github/workflows/release.yml- 在推送 tags 时触发
v* - 运行 job(
verify、contents: read),检出仓库、探测默认分支,并在 tag 不是默认分支的祖先时拒绝继续——从而防止未经 review 的分支发布 release。将调用方的 setup 和 quality gate 加入此 job。persist-credentials: false - 运行 job(
publish、contents: write,不执行 checkout),调用needs: verify;当 tag 包含gh release create "$TAG" --generate-notes --verify-tag时添加---prerelease
将 与 分开,可以确保负责检出仓库并执行代码质量门禁的 job 永远不会持有 权限。为了保障供应链安全,Action SHAs 均固定到具体提交(而非浮动 tags)。 记录了哪些 actions 已固定,以及发布新版本时如何刷新它们。
verifypublishcontents: writereferences/release.md6. Verify and report
6. 验证并报告
Re-read everything and print a small summary table to the user. Don't claim success without reading the post-state back from the API.
bash
gh api "repos/$REPO" --jq '{
squash: .allow_squash_merge, merge: .allow_merge_commit, rebase: .allow_rebase_merge,
delete: .delete_branch_on_merge, title: .squash_merge_commit_title, body: .squash_merge_commit_message
}'
gh api "repos/$REPO/rulesets" --jq '.[] | {id, name, enforcement, target, reviews: ([.rules[] | select(.type=="pull_request") | .parameters.required_approving_review_count][0])}'
test -f .github/release.yml && echo "release.yml present"
test -f .github/workflows/release.yml && echo "release workflow present"The summary should call out:
- What was changed
- What was already correct (no-op)
- What was skipped (e.g. merge queue on a private free-tier repo) and why
重新读取所有内容,并向用户打印一个简短的汇总表。未从 API 读回变更后的状态之前,不要声称成功。
bash
gh api "repos/$REPO" --jq '{
squash: .allow_squash_merge, merge: .allow_merge_commit, rebase: .allow_rebase_merge,
delete: .delete_branch_on_merge, title: .squash_merge_commit_title, body: .squash_merge_commit_message
}'
gh api "repos/$REPO/rulesets" --jq '.[] | {id, name, enforcement, target, reviews: ([.rules[] | select(.type=="pull_request") | .parameters.required_approving_review_count][0])}'
test -f .github/release.yml && echo "release.yml present"
test -f .github/workflows/release.yml && echo "release workflow present"汇总信息应说明:
- 变更了什么
- 哪些内容原本已经正确(no-op)
- 跳过了什么(例如,免费套餐私有仓库上的 merge queue)及其原因
Idempotency rules
幂等性规则
The skill is designed to be re-run safely on a partially-configured repo:
- Repo settings: is idempotent — re-applying the same body is a no-op response.
PATCH - Rulesets: lookup by either bootstrap variant name targeting the default branch. If found, diff and ask before ; reuse its id when switching variants.
PUTif absent. Never duplicate.POST - : diff before overwriting; ask if the content differs.
.github/release.yml - Release workflow: same — diff and ask.
- Never delete: if a previous run left an artifact (e.g. a legacy classic branch-protection rule alongside the new ruleset), surface it for the user to decide. Don't quietly remove it.
该 skill 旨在支持在部分配置完成的仓库上安全地重新运行:
- 仓库设置:是幂等的——重新应用相同的 body 不会产生变化。
PATCH - **Rulesets:**按目标为默认分支的 bootstrap 变体名称进行查找。如果找到,则比较差异并在执行 前征求用户同意;切换变体时复用其 id。如果不存在,则执行
PUT。绝不创建重复项。POST - **:**覆盖前先比较差异;如果内容不同,则征求用户同意。
.github/release.yml - **Release workflow:**同样先比较差异,再征求用户同意。
- **绝不删除:**如果之前的运行遗留了某个 artifact(例如,新 ruleset 与旧版 classic branch-protection rule 并存),将其呈现给用户决定。不要擅自移除。
What this skill is NOT for
本 skill 不适用于
- Per-task GitHub ops (PRs, issues, CI status, releases for a specific tag) — out of scope
- Local git operations — out of scope
- Pre-commit hooks — use
/prek - Project task running — out of scope
- Designing a CI pipeline — out of scope; this skill writes one specific release workflow if asked, no more
- Backfilling status check names into a repo whose CI doesn't emit them yet — it tells you what names to use, you wire them in your CI
- 逐任务 GitHub 操作(PR、issues、CI status、针对特定 tag 的 release)——不在范围内
- 本地 git 操作——不在范围内
- Pre-commit hooks——请使用
/prek - 执行项目任务——不在范围内
- 设计 CI pipeline——不在范围内;本 skill 只会在用户要求时写入一个特定的 release workflow,不会做其他事情
- 向尚未输出相应 status check 名称的仓库回填 status check 名称——本 skill 会告诉你应使用哪些名称,但需要你在 CI 中接入它们
Gotchas
易错点
- Required status check names must match exactly what your CI emits in the Checks API. After a green run on a PR, run to see the real names — silent typos will fail to gate merges.
gh api repos/$REPO/commits/<sha>/check-runs --jq '.check_runs[].name' - Squash merge with puts the entire PR description into the commit body. Encourage clean PR bodies; sloppy ones become permanent commit history.
squash_merge_commit_message=PR_BODY - Rulesets require admin permission on the repo (or org-level rulesets if applied at org scope). A token with only push/PR scopes will get a 403 here.
- Auto-generated release notes only categorize merged PRs, not direct commits. The default-branch ruleset blocks direct pushes, which is what makes release notes complete in the first place — these features reinforce each other.
- Use a merge queue only when GitHub eligibility is confirmed. A user-owned repository cannot use this rule even when it is public. Keep merge commits disabled at the repo level.
- If the "Merge when ready" button is missing from a PR view after this skill runs, double-check that both (repo-level) and the ruleset's
allow_auto_merge=truerule landed.merge_queue
- 必需 status check 的名称必须与 CI 在 Checks API 中输出的名称完全一致。在 PR 上完成一次成功运行后,执行 查看真实名称——细微的拼写错误会导致无法阻止合并。
gh api repos/$REPO/commits/<sha>/check-runs --jq '.check_runs[].name' - 使用 进行 Squash merge,会将整个 PR 描述放入 commit 正文。应鼓励编写简洁的 PR 正文;内容草率的正文会永久保留在 commit 历史中。
squash_merge_commit_message=PR_BODY - Rulesets 要求拥有仓库的管理员权限,或者可以在组织范围内应用组织级 rulesets。仅拥有 push/PR scopes 的 token 在此处会收到 403 错误。
- 自动生成的 release notes 只会分类已合并的 PR,不会分类直接提交的 commit。默认分支 ruleset 会阻止直接 push,这正是 release notes 从一开始就保持完整的原因——这些功能会相互配合。
- 仅在确认 GitHub 资格后使用 merge queue。即使是公共仓库,用户拥有的仓库也无法使用此规则。请保持仓库级别的 merge commits 处于禁用状态。
- 如果运行此 skill 后,PR 视图中仍缺少 "Merge when ready" 按钮,请再次确认 (仓库级别)和 ruleset 的
allow_auto_merge=true规则均已成功应用。merge_queue