Skip to content

Branch protection

Protect main so changes go through a reviewed PR and a green CI gate, while the auto-bump workflow can still push the version tag. Protection is optional — a fresh project works unprotected out of the box.

Generated projects ship scripts for this. From the project root:

bash scripts/setup_repo.sh          # guided: create repo → install App → protect
# or, on a repo that already exists:
bash scripts/setup_branch_protection.sh

setup_branch_protection.sh installs a main-protected ruleset via the GitHub API: require a PR + approving review + the CI status check on the default branch, block force-pushes and deletion, and bypass repository admins and the release-bot App. It is idempotent; tune with --reviews N / --check <job>.

The full setup — including the App, the exact permissions it needs, and the CI_BOT_APP_ID / CI_BOT_PRIVATE_KEY credentials — is documented in the generated project's own docs/repository-setup.md.

Why an App (not github-actions[bot])

A protected main blocks direct pushes, including versioning.yml's bump. A GitHub App is an identifiable actor the ruleset can add to its bypass list, and (unlike github-actions[bot]) an App-pushed tag triggers release.yml. versioning.yml is adaptive: it uses the App when CI_BOT_APP_ID is set, else falls back to github-actions[bot] on an unprotected repo. So the same repo works before and after you protect it — no file changes.

Required status check name

The aggregate job in ci.yml is named CI; that is the context the ruleset requires. It passes only when lint and test both succeed (gitleaks runs inside pre-commit, not as a separate job).

Manual alternative

If you prefer to click through it, Settings → Rules → Rulesets → New branch ruleset targeting the default branch, with the same rules and bypass actors as above. Classic Settings → Branches protection also works, but Rulesets are the modern path and are what the script uses.