Rulesets reference
Repository rulesets — branch or tag protection rules — are declared under a config's rulesets: section. Each entry is matched to a live ruleset by name and updated to exactly the declared spec.
Ruleset shape
| Field | Type | Default | Notes |
|---|---|---|---|
name |
string | required | Match key |
target |
branch | tag | push |
branch |
|
enforcement |
active | evaluate | disabled |
active |
|
bypass_actors |
list | [] |
See Bypass actors |
conditions |
object | empty ref_name |
See Conditions |
rules |
list | [] |
See Rule types |
rulesets:
- name: main-branch-protection
target: branch
enforcement: active
conditions:
ref_name:
include: ["~DEFAULT_BRANCH"]
exclude: []
bypass_actors:
- actor_type: OrganizationAdmin
bypass_mode: always
rules:
- type: pull_request
required_approving_review_count: 1
dismiss_stale_reviews_on_push: true
- type: required_status_checks
required_checks: [ci, lint]
strict_required_status_checks_policy: true
Rulesets merge across extends: layers the same way as the config's other keyed lists — by name, same-key item replaces in place, new items append. See composing with extends in the config reference.
Tag rulesets
Setting target: tag protects tags instead of branches — the same rule types, conditions, and bypass actors apply, just matched against tag names. A common case is locking down release tags so only an automated release process can create them, closing a publish trust boundary: if a CI workflow trusts "pushing a v* tag" as the signal to publish, anyone who can push that tag can trigger a publish from an arbitrary tree.
rulesets:
- name: protect release tags
target: tag
enforcement: active
conditions:
ref_name:
include: ["v*"]
exclude: []
rules:
- type: creation
- type: update
- type: deletion
bypass_actors:
- actor_type: RepositoryRole
actor_id: 5 # Repository Admins
bypass_mode: always
- actor_type: Integration
actor_id: 123456
bypass_mode: always
The bypass_actors entries are what let the automated process (here, a GitHub App) still create/move/delete the tag while everyone else is blocked by the three rules above. Give admins a bypass too, not just the automation: without one, a broken App installation or key locks every repo owner out of their own tags until the ruleset itself is edited.
Push rulesets
GitHub partitions rule types by target. A push ruleset accepts only the four file-restriction rule types — file_path_restriction, max_file_path_length, file_extension_restriction, and max_file_size — and every other rule type is branch/tag-only. This tool enforces the partition at config-load time (a load error), not at apply (a 422): the four file rules require target: push, any other rule type on a push target is rejected, and the four file rules are rejected on a branch/tag target.
A push ruleset selects no refs, so it must not carry a conditions.ref_name include/exclude — declaring one is rejected at load.
rulesets:
- name: block-binaries-and-large-files
target: push
enforcement: active
rules:
- type: max_file_size
max_file_size: 100
- type: file_extension_restriction
restricted_file_extensions: [".exe", ".dll"]
Matching and drift
Note
A ruleset is matched to a live one by name. Once matched, every declared list — rules, bypass_actors, and the ref-name include/exclude patterns in conditions — must equal the live list exactly, not just be a subset. A rule someone added by hand in the GitHub UI counts as drift and triggers an update that removes it, same as any other mismatch.
Server-supplied metadata that the config never sets — a check's integration_id, a bypass actor's resolved actor_id, timestamps — is ignored when comparing, so values GitHub fills in on its own can't cause spurious churn on every plan.
Rulesets inherited from an organization or enterprise are never matched or deleted: the listing call uses includes_parents=false, so only rulesets defined directly on the repo are ever in scope.
Warning
A declared rulesets: section is authoritative for the repo's own rulesets: any repo-level ruleset whose name isn't in the config gets deleted, and an explicit empty list (rulesets: []) deletes every one of them. Omitting the rulesets: key entirely, by contrast, leaves existing rulesets untouched — None and [] are not the same thing here.
Conditions
Conditions select which refs a ruleset applies to, via ref_name.include and ref_name.exclude pattern lists.
| Field | Type | Default | Notes |
|---|---|---|---|
ref_name.include |
list[string] | [] |
Two special tokens: ~DEFAULT_BRANCH, ~ALL |
ref_name.exclude |
list[string] | [] |
Bypass actors
| Field | Type | Default | Notes |
|---|---|---|---|
actor_type |
Integration | OrganizationAdmin | RepositoryRole | Team | DeployKey |
required | |
actor_id |
int | unset | Required for Integration, RepositoryRole, and Team; must be omitted for DeployKey; ignored for OrganizationAdmin (set it or not — no effect) |
bypass_mode |
always | pull_request |
always |
Note
The actor_type/actor_id coherence is validated at config load, following GitHub's own
rule: "Required for Integration, RepositoryRole, Team, and User actor types. If actor_type
is OrganizationAdmin, actor_id is ignored. If actor_type is DeployKey, this should be null."
This model does not include the User actor type.
Rule types
Each entry in rules: has a type discriminator plus that type's own parameters. The groups below cover every supported type.
Parameterless
These take no parameters — just the type:
rules:
- type: creation
- type: deletion
- type: non_fast_forward
- type: required_linear_history
- type: required_signatures
Pattern rules
commit_message_pattern, commit_author_email_pattern, committer_email_pattern, branch_name_pattern, and tag_name_pattern all share the same parameter shape:
| Field | Type | Default | Notes |
|---|---|---|---|
operator |
starts_with | ends_with | contains | regex |
required | |
pattern |
string | required | |
name |
string | unset | Label shown for the pattern in the GitHub UI |
negate |
bool | false |
rules:
- type: commit_message_pattern
operator: regex
pattern: '^(feat|fix|docs|refactor|chore)(\(.+\))?: .+'
name: conventional-commits
update
| Field | Type | Default | Notes |
|---|---|---|---|
update_allows_fetch_and_merge |
bool | false |
pull_request
| Field | Type | Default | Notes |
|---|---|---|---|
required_approving_review_count |
int | 0 |
|
dismiss_stale_reviews_on_push |
bool | false |
|
require_code_owner_review |
bool | false |
|
require_last_push_approval |
bool | false |
|
required_review_thread_resolution |
bool | false |
|
allowed_merge_methods |
list of merge | squash | rebase |
unset | Unset allows all methods |
required_status_checks
| Field | Type | Default | Notes |
|---|---|---|---|
required_checks |
list | [] |
Bare strings are coerced to {context: ...}; entries may also set integration_id |
strict_required_status_checks_policy |
bool | false |
|
do_not_enforce_on_create |
bool | false |
rules:
- type: required_status_checks
required_checks: [ci, lint] # shorthand for [{context: ci}, {context: lint}]
strict_required_status_checks_policy: true
required_deployments
| Field | Type | Default | Notes |
|---|---|---|---|
required_deployment_environments |
list[string] | [] |
Environment names |
merge_queue
| Field | Type | Default | Notes |
|---|---|---|---|
check_response_timeout_minutes |
int | 60 |
|
grouping_strategy |
ALLGREEN | HEADGREEN |
ALLGREEN |
|
max_entries_to_build |
int | 5 |
|
max_entries_to_merge |
int | 5 |
|
merge_method |
MERGE | SQUASH | REBASE |
MERGE |
|
min_entries_to_merge |
int | 1 |
|
min_entries_to_merge_wait_minutes |
int | 5 |
File restrictions
Four separate rule types, each gating one kind of file-level change. These are valid only on a push ruleset (see Push rulesets above):
| Rule type | Field | Default | Notes |
|---|---|---|---|
file_path_restriction |
restricted_file_paths |
[] |
Paths that cannot be added or modified |
max_file_path_length |
max_file_path_length |
required | Maximum path length, in characters |
file_extension_restriction |
restricted_file_extensions |
[] |
Extensions that cannot be added or modified |
max_file_size |
max_file_size |
required | Maximum blob size, in MB |
workflows
| Field | Type | Default | Notes |
|---|---|---|---|
workflows |
list of {repository_id, path, ref?, sha?} |
[] |
repository_id and path required per entry |
do_not_enforce_on_create |
bool | false |
rules:
- type: workflows
workflows:
- repository_id: 123456789
path: .github/workflows/ci.yml
ref: refs/heads/main
code_scanning
| Field | Type | Default | Notes |
|---|---|---|---|
code_scanning_tools |
list of {tool, security_alerts_threshold, alerts_threshold} |
[] |
security_alerts_threshold defaults to high_or_higher, alerts_threshold to errors |
copilot_code_review
A branch-target rule that requests an automatic Copilot review on the PR flow. It supersedes the deprecated automatic_copilot_code_review_enabled pull_request parameter.
| Field | Type | Default | Notes |
|---|---|---|---|
review_on_push |
bool | false |
Re-review when new commits are pushed |
review_draft_pull_requests |
bool | false |
Also review draft PRs |