Skip to content

Rulesets

Configure GitHub repository rulesets — the modern replacement for classic branch protection rules, with richer controls and enforcement modes.

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
update: false
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: true

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: authoritative
spec:
rulesets:
- name: protect-main
target: branch
rules:
non_fast_forward: true

With authoritative, any repository ruleset not declared in spec.rulesets is planned for deletion. To delete all repository rulesets:

reconcile:
rulesets: authoritative
spec:
rulesets: []

rulesets: null and rulesets: are invalid. Use [] for an explicitly empty managed collection.

Field Type Default Description
name string required Ruleset name (must be unique within the repository)
target string branch on create What the ruleset applies to: branch or tag
enforcement string active on create active (enforced), evaluate (dry-run, logged but not enforced), or disabled
bypass_actors list [] on create Actors who can bypass this ruleset. [] removes all actors from an existing ruleset
conditions object — Which branches or tags the ruleset applies to
rules object required The rules to enforce

The defaults apply when a ruleset is created. See Updating an existing ruleset for what happens to settings you leave out afterwards.

GitHub replaces the whole ruleset on update, so gh-infra reads the current ruleset and changes only what the manifest sets. Everything the manifest leaves out keeps its current value:

  • target, enforcement, bypass_actors, and conditions
  • rules the manifest does not mention, including rule types gh-infra does not model (such as merge_queue or commit_message_pattern)
  • parameters the manifest does not set, including ones gh-infra does not model (such as pull_request.allowed_merge_methods)

plan compares only what the manifest sets, which is what apply changes.

To remove something, say so in the manifest:

bypass_actors: [] # remove all bypass actors
rules:
deletion: false # toggle rules and update: false removes them
pull_request: false # remove the pull_request rule
required_status_checks: false

Because settings you leave out are kept, a change made on GitHub to one of them stays and does not show up in plan. For example, a ruleset switched to disabled in the GitHub UI stays disabled if the manifest does not set enforcement. Set enforcement (and anything else you want enforced) explicitly. reconcile.rulesets: authoritative controls which rulesets exist, not the rules inside a ruleset you declare.

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)

Teams and custom roles are looked up in the organization that owns the repository, so one directory of manifests can manage repositories of several organizations.

Exactly one actor type field must be specified per entry. Names are automatically resolved to numeric IDs — see Ruleset Identity Resolution for details.

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.

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

GitHub requires every parameter of this rule. When the rule is created, the ones you leave out are sent with the defaults above. On an existing rule, the ones you leave out keep their current values. Set pull_request: false to remove the rule.

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

GitHub requires both contexts and strict_required_status_checks_policy. When the rule is created, the ones you leave out are sent as no checks and false. On an existing rule, leaving out either keeps the current value. Set required_status_checks: false to remove the rule.

Simple on/off rules. Set to true to enable, false to remove; leaving one out keeps the current state:

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

Set update to true to block updates to matching refs:

update: true

For branch rulesets, use object form to control whether fork branches may pull changes from their upstream repository:

update:
allows_fetch_and_merge: true

allows_fetch_and_merge maps to the REST API update_allows_fetch_and_merge parameter and is not supported for tag rulesets. GitHub may omit this parameter from API responses (for example, on user-owned repositories). When it does, plan cannot detect drift for this field.

With the bool form (update: true), gh-infra does not manage allows_fetch_and_merge: it neither compares nor changes the parameter, and an existing value is kept.

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)