Skip to content

branch-protection.json

Branch protection ruleset configuration for GitHub repository protection.

Overview

The branch protection configuration file defines the ruleset that protects your repository's main branch. This file is automatically generated by pyrig and used by the protect-repo command to create or update GitHub branch protection rules.

The protect-repo command loads this file and applies it to GitHub via the API. It passes the JSON as a dict to the API, so any adjustments must align with GitHub's REST API schema. To customize, manually configure settings in GitHub, export the ruleset, and use that structure when subclassing RepoProtectionConfigFile.

Inheritance

graph TD A[ConfigFile] --> B[JsonConfigFile] B --> C[RepoProtectionConfigFile] style A fill:#a8dadc,stroke:#333,stroke-width:2px,color:#000 style B fill:#f4a261,stroke:#333,stroke-width:2px,color:#000 style C fill:#9d84b7,stroke:#333,stroke-width:2px,color:#000

Inherits from: JsonConfigFile

What this means:

  • Uses JSON format for configuration
  • Loads/dumps with Python's json module
  • Validation checks if configuration matches GitHub's ruleset schema
  • File can be manually uploaded to GitHub
  • Configuration is passed directly to GitHub's REST API

File Location

Path: branch-protection.json (project root)

Extension: .json - JSON configuration file.

Purpose

This configuration file serves as a declarative definition of your repository's branch protection rules. Instead of hardcoding protection settings in Python, pyrig generates a JSON file that:

  • Matches GitHub's ruleset export/import format
  • Can be manually uploaded to GitHub if needed
  • Provides transparency into protection rules

Configuration Structure

The file contains a complete GitHub ruleset definition:

{
  "name": "main-protection",
  "target": "branch",
  "enforcement": "active",
  "conditions": {
    "ref_name": {
      "exclude": [],
      "include": ["~DEFAULT_BRANCH"]
    }
  },
  "rules": [
    { "type": "creation" },
    { "type": "update" },
    { "type": "deletion" },
    { "type": "required_linear_history" },
    { "type": "required_signatures" },
    {
      "type": "pull_request",
      "parameters": {
        "required_approving_review_count": 1,
        "dismiss_stale_reviews_on_push": true,
        "required_reviewers": [],
        "require_code_owner_review": true,
        "require_last_push_approval": true,
        "required_review_thread_resolution": true,
        "allowed_merge_methods": ["squash", "rebase"]
      }
    },
    {
      "type": "required_status_checks",
      "parameters": {
        "strict_required_status_checks_policy": true,
        "do_not_enforce_on_create": true,
        "required_status_checks": [{ "context": "health_check" }]
      }
    },
    { "type": "non_fast_forward" }
  ],
  "bypass_actors": [
    {
      "actor_id": 5,
      "actor_type": "RepositoryRole",
      "bypass_mode": "always"
    }
  ]
}

Key Configuration Elements

Ruleset Name

  • Value: "main-protection"
  • Purpose: Identifies the ruleset in GitHub

Target

  • Value: "branch"
  • Purpose: Applies rules to branches (not tags or pushes)

Enforcement

  • Value: "active"
  • Purpose: Rules are enforced (alternatives: "disabled", "evaluate")

Conditions

  • Include: ["~DEFAULT_BRANCH"] (applies to main branch)
  • Exclude: [] (no exclusions)

Rules

Creation/Update/Deletion Protection:

  • Prevents unauthorized branch operations

Required Linear History:

  • Enforces linear commit history (no merge commits)

Required Signatures:

  • Requires signed commits for security

Pull Request Requirements:

  • 1 approving review required
  • Stale reviews dismissed on new pushes
  • Code owner approval required
  • Last push must be approved
  • All review threads must be resolved
  • Only squash and rebase merges allowed

Required Status Checks:

  • health_check job must pass
  • Branch must be up to date before merging and pass tests and other checks
  • Not enforced on branch creation

Non-Fast-Forward Protection:

  • Prevents force pushes

Bypass Actors

  • Actor ID 5: Repository admins
  • Bypass Mode: Always allowed to bypass rules

How It Works

Automatic Generation

When initialized via uv run pyrig mkroot, the branch-protection.json file is created by:

  1. Generating ruleset configuration: RepoProtectionConfigFile.get_configs() creates the complete ruleset
  2. Setting required status checks: Uses health check workflow job IDs
  3. Configuring bypass actors: Adds repository admin bypass permissions
  4. Applying security defaults: Enforces pyrig's opinionated protection rules

The class programmatically generates the configuration using:

  • Health check workflow job IDs for required status checks
  • Standard GitHub actor IDs for bypass permissions (actor_id: 5 = Repository admins)
  • Pyrig's opinionated security defaults

Application to GitHub

The protect-repo command loads this file and applies it to GitHub:

uv run pyrig protect-repo

This command:

  1. Loads branch-protection.json using RepoProtectionConfigFile.load()
  2. Checks if a ruleset with the same name exists
  3. Creates or updates the ruleset via GitHub API
  4. Applies all protection rules to the main branch

The configuration is passed directly to GitHub's REST API as a dictionary, so any manual modifications must align with GitHub's ruleset schema.

Usage

Automatic Creation

The file is automatically created when you initialize your project:

uv run pyrig mkroot

This generates branch-protection.json with pyrig's default protection rules.

Applying to GitHub

The protection rules are automatically applied by the health check workflow. To manually apply:

uv run pyrig protect-repo

This requires the REPO_TOKEN secret to be configured in your repository.

Manual Upload to GitHub

You can also manually upload this file to GitHub:

  1. Go to repository Settings → Rules → Rulesets
  2. Click "New ruleset" → "Import a ruleset"
  3. Upload branch-protection.json
  4. Review and create

You probably will not need this because the CI/CD pipeline will handle it for you via the protect-repo command.

Customization

To customize branch protection rules, subclass RepoProtectionConfigFile:

from typing import Any
from pyrig.dev.configs.git.branch_protection import RepoProtectionConfigFile

class MyRepoProtectionConfigFile(RepoProtectionConfigFile):
    @classmethod
    def _get_configs(cls) -> dict[str, Any]:
        """Custom branch protection configuration."""
        config = super()._get_configs()
        # Require 2 approvals instead of 1
        for rule in config["rules"]:
            if rule.get("type") == "pull_request":
                rule["parameters"]["required_approving_review_count"] = 2
        return config

Place in myapp/dev/configs/git/branch_protection.py and it will override the default configuration.

Verifying Configuration

After applying, verify the ruleset in GitHub after the CI/CD pipeline runs and activated it with the command uv run pyrig protect-repo:

  1. Go to repository Settings → Rules → Rulesets
  2. Find the main-protection ruleset
  3. Review the applied rules

Or export the current ruleset from GitHub and compare with your local file.

Best Practices

  1. Don't modify the file directly: Use subclassing to customize rules instead of editing the generated file
  2. Align with GitHub's schema: If you manually edit the JSON, ensure it matches GitHub's ruleset API schema
  3. Export from GitHub for reference: Manually configure rules in GitHub UI, export the ruleset, and use that structure for customization
  4. Test in a separate repository: Test custom protection rules in a test repository before applying to production
  5. Keep bypass actors minimal: Only allow repository admins to bypass rules
  6. Use the CI/CD pipeline: Let the health check workflow apply protection automatically instead of manual uploads