Rulesets
Configure GitHub repository rulesets — the modern replacement for classic branch protection rules, with richer controls and enforcement modes.
Example
Section titled “Example”reconcile: rulesets: additive
spec: rulesets: - name: protect-main target: branch enforcement: active bypass_actors: - role: admin bypass_mode: always conditions: ref_name: include: - "refs/heads/main" - "refs/heads/release/*" exclude: - "refs/heads/sandbox/*" rules: pull_request: required_approving_review_count: 1 dismiss_stale_reviews_on_push: true require_code_owner_review: false require_last_push_approval: false required_review_thread_resolution: false required_status_checks: strict_required_status_checks_policy: true contexts: - context: "ci/test" app: github-actions non_fast_forward: true deletion: true creation: false required_linear_history: false required_signatures: false
- name: protect-tags target: tag enforcement: evaluate conditions: ref_name: include: ["refs/tags/v*"] rules: deletion: true non_fast_forward: trueReconcile Mode
Section titled “Reconcile Mode”Rulesets are additive by default. gh-infra creates and updates rulesets listed in spec.rulesets, but it does not delete rulesets that exist on GitHub and are missing from YAML.
Set reconcile.rulesets: authoritative to make GitHub rulesets match YAML exactly:
reconcile: rulesets: authoritativespec: rulesets: - name: protect-main target: branch rules: non_fast_forward: trueWith authoritative, any repository ruleset not declared in spec.rulesets is planned for deletion. To delete all repository rulesets:
reconcile: rulesets: authoritativespec: rulesets: []rulesets: null and rulesets: are invalid. Use [] for an explicitly empty managed collection.
Top-level Fields
Section titled “Top-level Fields”| Field | Type | Default | Description |
|---|---|---|---|
name | string | required | Ruleset name (must be unique within the repository) |
target | string | branch | What the ruleset applies to: branch or tag |
enforcement | string | active | active (enforced), evaluate (dry-run, logged but not enforced), or disabled |
bypass_actors | list | [] | Actors who can bypass this ruleset |
conditions | object | — | Which branches or tags the ruleset applies to |
rules | object | required | The rules to enforce |
Bypass Actors
Section titled “Bypass Actors”Each entry allows a specific actor to bypass the ruleset. Specify the actor type by field name:
bypass_actors: - role: admin # Built-in role: admin, write, or maintain bypass_mode: always - team: maintainers # Organization team by slug bypass_mode: pull_request - app: github-actions # GitHub App by slug bypass_mode: always - org-admin: true # Organization administrators - custom-role: reviewer # Enterprise Cloud custom role by name bypass_mode: pull_request| Field | Description |
|---|---|
role | Built-in repository role: admin, write, or maintain |
team | Organization team slug (resolved via API) |
app | GitHub App slug (resolved via API). Private apps that cannot be resolved are exported as id:N by import, which is accepted as-is by plan and apply |
org-admin | Set to true for organization administrators |
custom-role | Enterprise Cloud custom role name (resolved via API) |
bypass_mode | always (pushes and PRs) or pull_request (PRs only) |
Exactly one actor type field must be specified per entry. Names are automatically resolved to numeric IDs — see Ruleset Identity Resolution for details.
Conditions
Section titled “Conditions”Specify which branches or tags the ruleset targets using fnmatch-style patterns:
conditions: ref_name: include: - "refs/heads/main" - "refs/heads/release/*" exclude: - "refs/heads/sandbox/*"Special values: ~DEFAULT_BRANCH matches the repository’s default branch, ~ALL matches all branches/tags.
pull_request
Section titled “pull_request”Require pull request reviews before merging:
| Field | Type | Default | Description |
|---|---|---|---|
required_approving_review_count | int | 0 | Number of required approving reviews (0–10) |
dismiss_stale_reviews_on_push | bool | false | Dismiss approvals when new commits are pushed |
require_code_owner_review | bool | false | Require review from code owners |
require_last_push_approval | bool | false | Last pusher cannot self-approve |
required_review_thread_resolution | bool | false | All review threads must be resolved |
required_status_checks
Section titled “required_status_checks”Require specific CI checks to pass:
rules: required_status_checks: strict_required_status_checks_policy: true contexts: - context: "ci/test" app: github-actions - context: "ci/lint"| Field | Type | Description |
|---|---|---|
strict_required_status_checks_policy | bool | Require branch to be up to date before merging |
contexts[].context | string | Name of the required status check |
contexts[].app | string | (optional) GitHub App slug that must provide this check. Omit to accept any provider |
Toggle Rules
Section titled “Toggle Rules”Simple on/off rules — set to true to enable:
| Field | Description |
|---|---|
non_fast_forward | Block force pushes to matching refs |
deletion | Block deletion of matching refs |
creation | Block creation of matching refs |
required_linear_history | Require linear commit history (no merge commits) |
required_signatures | Require signed commits |
Rulesets vs Classic Branch Protection
Section titled “Rulesets vs Classic Branch Protection”| Feature | Classic branch_protection | rulesets |
|---|---|---|
| Multiple rules per branch | No (one rule per pattern) | Yes (rules stack) |
| Enforcement modes | On/off only | Active, evaluate (dry-run), disabled |
| Bypass control | Admin override only | Granular per role/team/app |
| Tag protection | No | Yes (target: tag) |
| Audit trail | No | Built-in version history |
| Merge queue | Separate config | Integrated as a rule type (Phase 2) |
| Metadata rules | No | Commit message patterns, etc. (Phase 2) |