GitHub Repository Rules

1. Introduction

This guide describes how to configure GitHub repository rules to enforce the GitFlow development workflow. Branch protection rulesets ensure that:

  • Direct pushes to main and develop are blocked

  • All changes require pull request review

  • Code owners are automatically requested for review

2. Branch Protection Ruleset

Branch rulesets define protection rules for specific branches. We protect both main and develop branches to enforce the GitFlow model.

2.1. Ruleset Configuration

Use the GitHub CLI to create or update the branch protection ruleset:

2.1.1. Creating a New Ruleset

For repositories without an existing ruleset:

gh api repos/OWNER/REPO/rulesets --method POST --input - << 'EOF'
{
  "name": "Protect main and develop",
  "target": "branch",
  "enforcement": "active",
  "conditions": {
    "ref_name": {
      "include": ["refs/heads/main", "refs/heads/develop"],
      "exclude": []
    }
  },
  "rules": [
    {
      "type": "pull_request",
      "parameters": {
        "required_approving_review_count": 1,
        "dismiss_stale_reviews_on_push": false,
        "require_code_owner_review": true,
        "require_last_push_approval": false,
        "required_review_thread_resolution": false
      }
    }
  ],
  "bypass_actors": [
    {
      "actor_id": 5,
      "actor_type": "RepositoryRole",
      "bypass_mode": "always"
    }
  ]
}
EOF

2.1.2. Updating an Existing Ruleset

For repositories with an existing ruleset that needs modification:

# Step 1: List rulesets to find the ID
gh api repos/OWNER/REPO/rulesets

This returns JSON with ruleset details. Find the id field for the ruleset you want to update.

# Step 2: Update by ID (replace {ruleset_id} with actual ID)
gh api repos/OWNER/REPO/rulesets/{ruleset_id} --method PUT --input - << 'EOF'
{
  "name": "Protect main and develop",
  "target": "branch",
  "enforcement": "active",
  "conditions": {
    "ref_name": {
      "include": ["refs/heads/main", "refs/heads/develop"],
      "exclude": []
    }
  },
  "rules": [
    {
      "type": "pull_request",
      "parameters": {
        "required_approving_review_count": 1,
        "dismiss_stale_reviews_on_push": false,
        "require_code_owner_review": true,
        "require_last_push_approval": false,
        "required_review_thread_resolution": false
      }
    }
  ],
  "bypass_actors": [
    {
      "actor_id": 5,
      "actor_type": "RepositoryRole",
      "bypass_mode": "always"
    }
  ]
}
EOF

2.2. Ruleset Parameters

Parameter Description Value

required_approving_review_count

Minimum number of approving reviews before merge

1

dismiss_stale_reviews_on_push

Dismiss existing approvals when new commits are pushed

false

require_code_owner_review

Require approval from designated code owners

true

require_last_push_approval

Require approval of the most recent push

false

required_review_thread_resolution

Require all review threads to be resolved

false

2.3. Bypass Actors

The bypass_actors section defines who can bypass the protection rules:

Actor ID Role Description

5

Repository Admin

Users with admin access can bypass rules when necessary

Use bypass sparingly. It’s intended for emergency situations where normal workflow cannot be followed.

3. Code Owners

GitHub CODEOWNERS files automatically assign reviewers to pull requests based on which files are modified.

3.1. File Location

Create a CODEOWNERS file in the .github directory of your repository:

.github/CODEOWNERS

3.2. Creating CODEOWNERS

mkdir -p .github
echo "* @christhonie" > .github/CODEOWNERS
git add .github/CODEOWNERS
git commit -m "Add CODEOWNERS with default reviewer"
git push

3.3. CODEOWNERS Syntax

The CODEOWNERS file uses pattern matching similar to .gitignore:

# Default owner for all files (catch-all)
* @christhonie

# Specific file patterns
*.java @christhonie
*.ts @frontend-team

# Directory patterns
/docs/ @docs-team
/src/main/helm/ @devops-team

# Multiple owners (all are requested)
/security/ @christhonie @security-team

3.4. Pattern Matching Rules

Pattern Matches

*

All files (catch-all, lowest priority)

*.java

All Java files

/docs/

Files in the root docs directory

docs/

Files in any docs directory

/src/main/helm/**

All files under src/main/helm

Later patterns take precedence over earlier ones. Place specific patterns after general ones.

3.5. Interaction with Rulesets

When require_code_owner_review: true is set in the branch ruleset:

  • PRs cannot be merged until a code owner approves

  • Code owners are automatically requested for review

  • If no CODEOWNERS file exists, any reviewer can approve

4. Applying to Repositories

4.1. New Repository Setup

Follow this checklist when configuring a new repository:

  1. [ ] Create the CODEOWNERS file

    mkdir -p .github
    echo "* @christhonie" > .github/CODEOWNERS
    git add .github/CODEOWNERS
    git commit -m "Add CODEOWNERS with default reviewer"
    git push
  2. [ ] Apply the branch protection ruleset

    gh api repos/OWNER/REPO/rulesets --method POST --input - << 'EOF'
    { ... ruleset JSON ... }
    EOF
  3. [ ] Verify by creating a test PR to develop

    1. Confirm code owner is automatically requested

    2. Confirm merge is blocked until approved

4.2. Updating Existing Repositories

For repositories with an existing ruleset:

  1. [ ] Add CODEOWNERS file (if not present)

  2. [ ] List existing rulesets: gh api repos/OWNER/REPO/rulesets

  3. [ ] Note the ruleset ID from the response

  4. [ ] Update the ruleset using PUT with the ID

  5. [ ] Verify the update: gh api repos/OWNER/REPO/rulesets/{id}

5. Troubleshooting

5.1. Code Owner Not Requested

  • Verify CODEOWNERS file is in .github/ directory

  • Check file patterns match the modified files

  • Ensure require_code_owner_review: true in ruleset

5.2. Cannot Merge Despite Approval

  • Check if the approver is listed in CODEOWNERS for the modified files

  • Verify the correct number of approvals (check required_approving_review_count)

  • Check GitHub Actions status checks are passing

5.3. Ruleset Not Taking Effect

  • Verify branch names in conditions.ref_name.include

  • Check enforcement is set to active

  • Confirm no conflicting legacy branch protection rules