gh-bootstrap

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

gh-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

将配置哪些内容

ConcernResultEndpoint
Repo merge defaultsSquash-only; PR title → commit subject; delete branches on merge
PATCH /repos/{owner}/{repo}
Default branch protectionRuleset with PR and required checks, no force-push, no deletion; optional queue
POST /repos/{owner}/{repo}/rulesets
Release notes
.github/release.yml
with category buckets driven by PR labels
File commit
Release workflow (optional)Tag push →
gh release create --generate-notes
on the discovered default branch
File commit
All policy endpoints (repo settings, rulesets, branch protection) run through the
gh
CLI —
gh api
for the JSON endpoints,
gh repo
/
gh release
where there's a first-class subcommand. The skill writes files (
.github/release.yml
, the optional release workflow) directly to the working tree.
配置项结果端点
仓库合并默认值仅允许 Squash;PR 标题 → commit subject;合并后删除分支
PATCH /repos/{owner}/{repo}
默认分支保护包含 PR 和必需检查的 Ruleset;禁止 force-push 和删除;可选 queue
POST /repos/{owner}/{repo}/rulesets
Release notes
.github/release.yml
,包含由 PR labels 驱动的分类 buckets
文件提交
Release workflow(可选)推送 Tag → 在探测到的默认分支上执行
gh release create --generate-notes
文件提交
所有策略端点(仓库设置、rulesets、branch protection)都通过
gh
CLI 执行——JSON 端点使用
gh api
,存在原生子命令的则使用
gh repo
/
gh release
。该 skill 会将文件(
.github/release.yml
、可选的 release workflow)直接写入工作树。

Protocol

协议

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
PATCH
lands all the merge-button settings atomically:
bash
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
Why each flag:
  • allow_squash_merge=true
    ,
    allow_merge_commit=false
    ,
    allow_rebase_merge=false
    — squash is the only button on a merged PR, so
    main
    history is one commit per merged PR.
  • delete_branch_on_merge=true
    — the branch is gone anyway once it's been collapsed into one commit; no point keeping the ref.
  • squash_merge_commit_title=PR_TITLE
    — the squash commit's subject line is the PR title verbatim. This makes
    git log --oneline
    on
    main
    read as a list of merged PRs.
  • squash_merge_commit_message=PR_BODY
    — the PR body becomes the commit body, so the rationale for each change survives in
    git log
    without needing GitHub.
  • allow_auto_merge=true
    — required for the merge queue button to enqueue PRs without human babysitting.
PATCH
is idempotent: re-running with the same values is a no-op.
单次
PATCH
会原子性地应用所有合并按钮设置:
bash
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
    、
    allow_rebase_merge=false
    ——合并 PR 时只保留 squash 这一个按钮,因此
    main
    的历史记录中,每个合并的 PR 只对应一个 commit。
  • delete_branch_on_merge=true
    ——分支在折叠成一个 commit 后本来就会消失,没有保留引用的意义。
  • squash_merge_commit_title=PR_TITLE
    ——squash commit 的 subject line 原样使用 PR 标题。这样,
    main
    上的
    git log --oneline
    看起来就像已合并 PR 的列表。
  • squash_merge_commit_message=PR_BODY
    ——PR 正文成为 commit 正文,因此每次变更的依据都会保留在
    git log
    中,无需依赖 GitHub。
  • allow_auto_merge=true
    ——merge queue 按钮需要此设置,才能在无人值守的情况下将 PR 加入队列。
PATCH
是幂等的:使用相同值重新运行不会产生任何变化。

3. 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
references/ruleset.md
— read that file before this step. The no-queue variant ships as an asset:
VariantAssetPick when
main: PR + CI
assets/rulesets/main-pr-ci.json
Solo or small repo. Squash-only via
allowed_merge_methods: ["squash"]
, 0 required reviews, single CI check, no merge queue.
main-protection
(queue)
inline JSON in
references/ruleset.md
Team repo with concurrent PR throughput. Adds the
merge_queue
rule, ≥1 review, matrix of CI checks.
Both variants share the same shape:
  • target: "branch"
    ,
    conditions.ref_name.include: ["~DEFAULT_BRANCH"]
  • enforcement: "active"
  • Rules:
    deletion
    ,
    non_fast_forward
    ,
    pull_request
    ,
    required_status_checks
Default to the
main: PR + CI
variant unless the user explicitly asks for the merge queue. It's the simpler shape and the one that matches everyday solo work.
Idempotency: list rulesets and look for one targeting the default branch whose name matches either bootstrap variant (
main: PR + CI
or
main-protection
). If found,
GET
it, diff its
rules
,
bypass_actors
, and
conditions
against the rendered payload, show the diff, and ask the user before
PUT
-ing. When the user switches variants, reuse that ruleset's id for the
PUT
instead of creating a second one, and ask before disabling or retiring anything from the prior variant. If absent,
POST
a new one. Never create a duplicate ruleset for the default branch.
Rulesets 是 GitHub 对 branch protection 的现代替代方案——它们将 merge queue 作为一等规则提供支持,能够与组织级 rulesets 干净地配合,并且可通过 API 查询。默认使用 ruleset;仅当用户明确要求时才回退到 classic branch protection(例如,他们被固定在较旧的 GHE 版本上)。
完整 payload、asset 模板以及创建或更新的逻辑位于
references/ruleset.md
——执行此步骤前先阅读该文件。无需 queue 的变体以 asset 形式提供:
变体Asset选择时机
main: PR + CI
assets/rulesets/main-pr-ci.json
个人或小型仓库。通过
allowed_merge_methods: ["squash"]
实现仅允许 Squash,要求 0 个 reviews、单个 CI check,不使用 merge queue。
main-protection
(queue)
references/ruleset.md
中的内联 JSON
需要并发 PR 处理能力的团队仓库。增加
merge_queue
规则、至少 1 个 review,以及一组 CI checks。
两个变体采用相同的结构:
  • target: "branch"
    、
    conditions.ref_name.include: ["~DEFAULT_BRANCH"]
  • enforcement: "active"
  • 规则:
    deletion
    、
    non_fast_forward
    、
    pull_request
    、
    required_status_checks
除非用户明确要求使用 merge queue,否则默认采用
main: PR + CI
变体。它更简单,也符合日常个人工作的需要。
**幂等性:**列出 rulesets,并查找名称与任一 bootstrap 变体(
main: PR + CI
或
main-protection
)匹配且以默认分支为目标的 ruleset。如果找到,则通过
GET
获取它,将其
rules
、
bypass_actors
和
conditions
与渲染后的 payload 进行比较,展示差异,并在执行
PUT
前征求用户同意。当用户切换变体时,复用该 ruleset 的 id 执行
PUT
,而不是创建第二个 ruleset;停用或移除先前变集中的任何内容之前,必须征求用户同意。如果不存在,则通过
POST
创建一个。绝不为默认分支创建重复的 ruleset。

4. Scaffold the release-notes config

4. 生成 Release notes 配置

Copy
assets/release.yml
to
.github/release.yml
. This file is consumed by GitHub's auto-generated release notes (the "Generate release notes" button on the New Release page, and
gh release create --generate-notes
). It groups merged PRs into Breaking / Features / Fixes / Docs / Internal / Other buckets driven by PR labels.
If
.github/release.yml
already exists, diff it against the template and ask before overwriting. Don't silently clobber the user's existing categories.
将
assets/release.yml
复制到
.github/release.yml
。GitHub 的自动生成 release notes 会使用此文件(New Release 页面上的 "Generate release notes" 按钮,以及
gh release create --generate-notes
)。它会根据 PR labels 将已合并的 PR 分为 Breaking / Features / Fixes / Docs / Internal / Other 等分类 buckets。
如果
.github/release.yml
已存在,将其与模板进行比较,并在覆盖前征求用户同意。不要擅自覆盖用户现有的分类配置。

5. Scaffold the release workflow (optional)

5. 生成 Release workflow(可选)

If the user opted in, copy
assets/release-workflow.yml
to
.github/workflows/release.yml
. The workflow:
  • Triggers on
    push
    to
    v*
    tags
  • Runs a
    verify
    job (
    contents: read
    ,
    persist-credentials: false
    ) 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.
  • Runs a
    publish
    job (
    contents: write
    ,
    needs: verify
    , no checkout) that calls
    gh release create "$TAG" --generate-notes --verify-tag
    , adding
    --prerelease
    when the tag contains a
    -
Splitting
verify
from
publish
keeps the job that checks out and gates repository code from ever holding
contents: write
. Action SHAs are pinned (not floating tags) for supply-chain hygiene.
references/release.md
documents which actions are pinned and how to refresh them when a new version comes out.
如果用户选择启用,则将
assets/release-workflow.yml
复制到
.github/workflows/release.yml
。该 workflow:
  • 在推送
    v*
    tags 时触发
  • 运行
    verify
    job(
    contents: read
    、
    persist-credentials: false
    ),检出仓库、探测默认分支,并在 tag 不是默认分支的祖先时拒绝继续——从而防止未经 review 的分支发布 release。将调用方的 setup 和 quality gate 加入此 job。
  • 运行
    publish
    job(
    contents: write
    、
    needs: verify
    ,不执行 checkout),调用
    gh release create "$TAG" --generate-notes --verify-tag
    ;当 tag 包含
    -
    时添加
    --prerelease
将
verify
与
publish
分开,可以确保负责检出仓库并执行代码质量门禁的 job 永远不会持有
contents: write
权限。为了保障供应链安全,Action SHAs 均固定到具体提交(而非浮动 tags)。
references/release.md
记录了哪些 actions 已固定,以及发布新版本时如何刷新它们。

6. 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:
    PATCH
    is idempotent — re-applying the same body is a no-op response.
  • Rulesets: lookup by either bootstrap variant name targeting the default branch. If found, diff and ask before
    PUT
    ; reuse its id when switching variants.
    POST
    if absent. Never duplicate.
  • .github/release.yml
    :
    diff before overwriting; ask if the content differs.
  • 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 旨在支持在部分配置完成的仓库上安全地重新运行:
  • 仓库设置:
    PATCH
    是幂等的——重新应用相同的 body 不会产生变化。
  • **Rulesets:**按目标为默认分支的 bootstrap 变体名称进行查找。如果找到,则比较差异并在执行
    PUT
    前征求用户同意;切换变体时复用其 id。如果不存在,则执行
    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
    gh api repos/$REPO/commits/<sha>/check-runs --jq '.check_runs[].name'
    to see the real names — silent typos will fail to gate merges.
  • Squash merge with
    squash_merge_commit_message=PR_BODY
    puts the entire PR description into the commit body. Encourage clean PR bodies; sloppy ones become permanent commit history.
  • 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
    allow_auto_merge=true
    (repo-level) and the ruleset's
    merge_queue
    rule landed.
  • 必需 status check 的名称必须与 CI 在 Checks API 中输出的名称完全一致。在 PR 上完成一次成功运行后,执行
    gh api repos/$REPO/commits/<sha>/check-runs --jq '.check_runs[].name'
    查看真实名称——细微的拼写错误会导致无法阻止合并。
  • 使用
    squash_merge_commit_message=PR_BODY
    进行 Squash merge,会将整个 PR 描述放入 commit 正文。应鼓励编写简洁的 PR 正文;内容草率的正文会永久保留在 commit 历史中。
  • 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" 按钮,请再次确认
    allow_auto_merge=true
    (仓库级别)和 ruleset 的
    merge_queue
    规则均已成功应用。