GitHub Rulesets¶
xfg can manage GitHub Rulesets declaratively using the settings command. Define rulesets in your config file, and xfg will create, update, or delete them to match your desired state.
GitHub-Only Feature
Rulesets are only available for GitHub repositories. Azure DevOps and GitLab repos will be skipped when running xfg settings.
Quick Start¶
id: my-config
files:
.prettierrc.json:
content:
semi: false
settings:
rulesets:
main-protection:
target: branch
enforcement: active
conditions:
refName:
include:
- refs/heads/main
rules:
- type: pull_request
parameters:
requiredApprovingReviewCount: 1
repos:
- git: git@github.com:your-org/your-repo.git
Why Rulesets?¶
GitHub Rulesets offer advantages over legacy branch protection rules:
- Pattern-based conditions - Apply rules to multiple branches with glob patterns
- Multiple rules per ruleset - Group related rules together
- Bypass actors - Fine-grained control over who can bypass rules
- Evaluate mode - Test rules without enforcing them
- Advanced rules - Code scanning, workflows, file restrictions
Ruleset Structure¶
settings:
rulesets:
ruleset-name: # Unique name for this ruleset
target: branch # "branch" or "tag"
enforcement: active # "active", "disabled", or "evaluate"
bypassActors: # Optional: who can bypass these rules
- actorId: 12345
actorType: Team # "Team", "User", or "Integration"
bypassMode: always # "always" or "pull_request"
conditions: # Which refs this applies to
refName:
include:
- refs/heads/main
- refs/heads/release/*
exclude:
- refs/heads/dev*
rules: # Array of rule objects
- type: pull_request
parameters:
requiredApprovingReviewCount: 2
Available Rule Types¶
Pull Request Rules¶
- type: pull_request
parameters:
requiredApprovingReviewCount: 2 # 0-10
dismissStaleReviewsOnPush: true
requireCodeOwnerReview: true
requireLastPushApproval: true
requiredReviewThreadResolution: true
allowedMergeMethods:
- squash
- rebase
Status Checks¶
- type: required_status_checks
parameters:
strictRequiredStatusChecksPolicy: true
doNotEnforceOnCreate: false
requiredStatusChecks:
- context: "ci/build"
- context: "ci/test"
integrationId: 12345
Simple Rules (No Parameters)¶
- type: required_signatures
- type: required_linear_history
- type: non_fast_forward
- type: creation
- type: deletion
Update Rule¶
Deployments¶
Code Scanning¶
- type: code_scanning
parameters:
codeScanningTools:
- tool: CodeQL
alertsThreshold: errors # none, errors, errors_and_warnings, all
securityAlertsThreshold: critical # none, critical, high_or_higher, medium_or_higher, all
Pattern Rules¶
- type: commit_message_pattern
parameters:
name: "Conventional commits"
operator: regex
pattern: "^(feat|fix|docs|style|refactor|test|chore)(\\(.+\\))?: .+"
negate: false
- type: branch_name_pattern
parameters:
operator: regex
pattern: "^(feature|bugfix|hotfix)/.+"
File Restrictions¶
- type: file_path_restriction
parameters:
restrictedFilePaths:
- ".github/workflows/*"
- "package-lock.json"
- type: max_file_size
parameters:
maxFileSize: 10485760 # 10MB in bytes
Inheritance and Opt-Out¶
Like files, rulesets support inheritance with options to opt out.
Default Inheritance¶
Define defaults at the root level and override per-repo:
# Root-level defaults for all repos
settings:
rulesets:
main-protection:
target: branch
enforcement: active
conditions:
refName:
include: [refs/heads/main]
rules:
- type: pull_request
parameters:
requiredApprovingReviewCount: 1
repos:
# Gets default ruleset
- git: git@github.com:your-org/standard-repo.git
# Overrides with stricter requirements
- git: git@github.com:your-org/critical-repo.git
settings:
rulesets:
main-protection:
rules:
- type: pull_request
parameters:
requiredApprovingReviewCount: 3 # Override
Single Ruleset Opt-Out¶
Set a ruleset to false to exclude it from a specific repo:
settings:
rulesets:
main-protection:
target: branch
enforcement: active
release-protection:
target: branch
enforcement: active
repos:
# Gets both rulesets
- git: git@github.com:your-org/standard-repo.git
# Skips release-protection only
- git: git@github.com:your-org/no-releases.git
settings:
rulesets:
release-protection: false
Skipping All Inherited Rulesets¶
Use inherit: false to skip all root-level rulesets. You can optionally add repo-specific rulesets:
settings:
rulesets:
main-protection:
target: branch
enforcement: active
repos:
# No rulesets at all
- git: git@github.com:your-org/experimental.git
settings:
rulesets:
inherit: false
# Skip inherited, add custom
- git: git@github.com:your-org/custom-rules.git
settings:
rulesets:
inherit: false
custom-ruleset:
target: tag
enforcement: active
conditions:
refName:
include: [refs/tags/v*]
rules:
- type: required_signatures
Bypass Actors¶
Allow specific users, teams, or integrations to bypass rules:
bypassActors:
# GitHub App (e.g., Renovate, Dependabot)
- actorId: 2719952
actorType: Integration
bypassMode: always
# Team
- actorId: 123456
actorType: Team
bypassMode: pull_request # Only bypass via PRs
# User
- actorId: 789012
actorType: User
bypassMode: always
Finding Actor IDs
Use the GitHub API to find actor IDs.
# Team ID
gh api orgs/{org}/teams/{team-slug} --jq '.id'
# User ID
gh api users/{username} --jq '.id'
# Integration ID (GitHub Apps)
gh api orgs/{org}/installations --jq '.installations[] | {name: .app_slug, id: .app_id}'
Orphan Deletion¶
When deleteOrphaned: true is set, xfg tracks which rulesets it manages and deletes any that are removed from the config:
id: my-config
deleteOrphaned: true
settings:
rulesets:
main-protection: # This ruleset is tracked
# ...
If you later remove main-protection from the config and run xfg settings, it will be deleted from the repository.
Use --no-delete to skip orphan deletion:
Dry Run¶
Preview changes without applying them:
Output shows planned changes:
Loading config from: ./config.yaml
Running in DRY RUN mode - no changes will be made
Found 2 repositories with rulesets
[1/2] your-org/repo1: Processing rulesets...
[1/2] ✓ your-org/repo1: [DRY RUN] 1 created, 0 updated, 0 deleted
[2/2] your-org/repo2: Processing rulesets...
[2/2] ✓ your-org/repo2: [DRY RUN] 0 created, 1 updated, 0 deleted
Combining with File Sync¶
The sync and settings commands are independent. Run them together or separately:
# Sync files and apply rulesets
xfg sync -c config.yaml && xfg settings -c config.yaml
# Or run separately
xfg sync -c config.yaml
xfg settings -c config.yaml --dry-run # Preview first
xfg settings -c config.yaml # Apply
Complete Example¶
id: org-standards
deleteOrphaned: true
prOptions:
merge: auto
mergeStrategy: squash
settings:
rulesets:
main-protection:
target: branch
enforcement: active
bypassActors:
- actorId: 2719952
actorType: Integration
bypassMode: always
conditions:
refName:
include:
- refs/heads/main
rules:
- type: pull_request
parameters:
requiredApprovingReviewCount: 1
dismissStaleReviewsOnPush: true
requireCodeOwnerReview: true
- type: required_status_checks
parameters:
strictRequiredStatusChecksPolicy: true
requiredStatusChecks:
- context: "ci/build"
- context: "ci/test"
- type: required_linear_history
release-protection:
target: branch
enforcement: active
conditions:
refName:
include:
- refs/heads/release/*
rules:
- type: pull_request
parameters:
requiredApprovingReviewCount: 2
- type: required_signatures
files:
.github/dependabot.yml:
content:
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
repos:
- git:
- git@github.com:your-org/frontend.git
- git@github.com:your-org/backend.git
- git@github.com:your-org/shared-lib.git