Skip to content

feat: enforce Copilot code review ruleset via sync script#9

Merged
gamaware merged 2 commits intomainfrom
feat/copilot-ruleset-sync
Mar 11, 2026
Merged

feat: enforce Copilot code review ruleset via sync script#9
gamaware merged 2 commits intomainfrom
feat/copilot-ruleset-sync

Conversation

@gamaware
Copy link
Owner

@gamaware gamaware commented Mar 11, 2026

Summary

  • Add sync_rulesets() function to the sync script that creates and verifies the Copilot code review ruleset on all repositories
  • Add ruleset configuration to config/baseline.json (deletion protection, non-fast-forward, Copilot review)
  • Add CodeRabbit manual setup note to README (no API available for automation)
  • Add rulesets to baseline schema validation in quality-checks.yml

Test plan

  • Trigger dry-run workflow to verify ruleset drift detection works
  • Verify existing repos with the ruleset show as compliant
  • Test on a repo without the ruleset to confirm creation works
  • Confirm quality-checks pass with new rulesets schema section

Summary by CodeRabbit

  • New Features

    • Added Rulesets support to manage branch protection rules and enforce Copilot code review on the default branch.
    • Implemented automatic Copilot code review enforcement through configurable rulesets.
  • Documentation

    • Updated configuration documentation to detail the new Rulesets feature and available rules.
    • Clarified code review behavior for both Copilot and CodeRabbit, including manual setup requirements for CodeRabbit.

- Add sync_rulesets() to create and verify the Copilot code review
  ruleset on all repositories' default branches
- Add rulesets config to baseline.json with deletion protection,
  non-fast-forward protection, and Copilot review rules
- Add rulesets section to README with enforcement details
- Add CodeRabbit manual setup note (no API for automation)
- Add rulesets to baseline schema validation in quality-checks.yml
Copilot AI review requested due to automatic review settings March 11, 2026 04:45
@coderabbitai
Copy link

coderabbitai bot commented Mar 11, 2026

Warning

Rate limit exceeded

@gamaware has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 6 minutes and 13 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b4fce93c-e9d5-4cb0-9a00-68987d95b884

📥 Commits

Reviewing files that changed from the base of the PR and between 4fa8b71 and 41ce4fd.

📒 Files selected for processing (1)
  • scripts/sync-repo-settings.sh
📝 Walkthrough

Walkthrough

This PR introduces GitHub branch protection rulesets for enforcing Copilot code reviews on default branches. Changes include new baseline configuration, a sync_rulesets() function to apply and validate these rulesets, workflow validation updates, and documentation clarifications.

Changes

Cohort / File(s) Summary
Workflow & Schema Validation
.github/workflows/quality-checks.yml
Updated baseline.json schema validation to include "rulesets" as a required section alongside existing sections.
Configuration
config/baseline.json
Added new "Copilot review for default branch" ruleset targeting the default branch with three enforcement rules: deletion, non_fast_forward, and copilot_code_review (review_on_push and review_draft_pull_requests disabled).
Documentation
CLAUDE.md, README.md
Added Rulesets item to Settings Sync overview; expanded README with new Rulesets section detailing Copilot code review enforcement, rules table, and clarified Code Review section with notes on CodeRabbit manual enablement via dashboard.
Implementation
scripts/sync-repo-settings.sh
New sync_rulesets() function that checks for existing Copilot code review rulesets, validates enforcement state, creates or updates rulesets in apply mode, and reports drift detection alongside per-repo drift reporting.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 Hops with delight
Rulesets now guard the branches bright,
Copilot's wisdom on default's height! ✨
Three rules aligned, enforcement in sight,
From baseline to sync—everything's right! 🎯

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely describes the main change: adding enforcement of Copilot code review rulesets through the sync script, which is the primary focus across all modified files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/copilot-ruleset-sync

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds organization-wide enforcement of a Copilot code review ruleset by extending the repo settings sync tooling, plus baseline configuration, documentation, and CI validation updates.

Changes:

  • Add sync_rulesets() to create/verify a Copilot review ruleset during repo sync runs.
  • Add a rulesets section to config/baseline.json defining the Copilot ruleset (and related protections).
  • Document ruleset behavior + CodeRabbit manual setup, and extend baseline schema checks to require rulesets.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
scripts/sync-repo-settings.sh Adds ruleset sync logic and includes it in the per-repo drift report.
config/baseline.json Introduces baseline configuration for the Copilot code review ruleset.
README.md Documents rulesets and CodeRabbit manual enablement.
CLAUDE.md Updates project instructions to include rulesets in enforced settings.
.github/workflows/quality-checks.yml Ensures baseline schema validation requires a rulesets section.


# Check if the ruleset already exists
local existing
existing=$(gh api "repos/$OWNER/$repo/rulesets" --jq ".[] | select(.name == \"$ruleset_name\") | .id" 2>/dev/null || echo "")
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ruleset lookup builds a jq filter by interpolating ruleset_name into the --jq string. If the name contains quotes/backslashes (or other jq-significant chars), the filter can break and return the wrong ID. Prefer fetching the list JSON and piping to jq -r --arg name "$ruleset_name" 'map(select(.name == $name)) | .[0].id // empty' (or similar) to avoid injection/escaping issues and to guarantee a single ID.

Suggested change
existing=$(gh api "repos/$OWNER/$repo/rulesets" --jq ".[] | select(.name == \"$ruleset_name\") | .id" 2>/dev/null || echo "")
existing=$(gh api "repos/$OWNER/$repo/rulesets" 2>/dev/null | jq -r --arg name "$ruleset_name" 'map(select(.name == $name)) | .[0].id // empty' 2>/dev/null || echo "")

Copilot uses AI. Check for mistakes.
Comment on lines +394 to +410
if [ -n "$existing" ]; then
# Verify enforcement is active
local current_enforcement
current_enforcement=$(gh api "repos/$OWNER/$repo/rulesets/$existing" --jq '.enforcement' 2>/dev/null || echo "")
if [ "$current_enforcement" != "active" ]; then
changes="- Copilot review ruleset: enforcement \`$current_enforcement\` -> \`active\`\n"
if [ "$MODE" = "--apply" ]; then
gh api -X PUT "repos/$OWNER/$repo/rulesets/$existing" \
--input <(echo "$effective" | jq '.rulesets.copilot_code_review') \
> /dev/null 2>&1 || log "WARN: Could not update ruleset for $repo"
log "APPLIED ruleset enforcement for $repo"
else
log "DRIFT detected in ruleset enforcement for $repo"
fi
else
log "OK: Copilot review ruleset for $repo"
fi
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sync_rulesets only checks whether enforcement is active, but it doesn’t validate the rest of the ruleset (rules/conditions/target). That means a repo can be reported as OK even if protections like deletion / non_fast_forward / copilot_code_review were changed or removed. Consider diffing the current ruleset JSON against the desired config (after normalizing away server-managed fields like id, timestamps, etc.) and treating any mismatch as drift (and applying an update in --apply mode).

Copilot uses AI. Check for mistakes.

# Check if the ruleset already exists
local existing
existing=$(gh api "repos/$OWNER/$repo/rulesets" --jq ".[] | select(.name == \"$ruleset_name\") | .id" 2>/dev/null || echo "")
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ruleset list call (gh api repos/$OWNER/$repo/rulesets) is likely paginated. Without --paginate, repos with enough rulesets may not return the target ruleset in the first page, causing false “missing” drift and potentially creating duplicates. Use gh api --paginate (and/or increase per_page) when listing rulesets.

Suggested change
existing=$(gh api "repos/$OWNER/$repo/rulesets" --jq ".[] | select(.name == \"$ruleset_name\") | .id" 2>/dev/null || echo "")
existing=$(gh api --paginate "repos/$OWNER/$repo/rulesets?per_page=100" --jq '.[] | select(.name == $ruleset_name) | .id' --arg ruleset_name "$ruleset_name" 2>/dev/null || echo "")

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/quality-checks.yml (1)

90-100: ⚠️ Potential issue | 🟠 Major

Validate the ruleset schema, not just the top-level key.

This only proves .rulesets exists. A broken rulesets.copilot_code_review object would still pass CI, and scripts/sync-repo-settings.sh Lines 383-388 then short-circuit ruleset sync when .name is empty.

🧪 Proposed fix
           for section in repo_settings security branch_protection rulesets labels required_files; do
             if ! jq -e ".$section" config/baseline.json > /dev/null 2>&1; then
               echo "ERROR: Missing section '$section' in baseline.json"
               ERRORS=$((ERRORS + 1))
             else
               echo "OK: section '$section' present"
             fi
           done
+          if ! jq -e '
+            .rulesets.copilot_code_review
+            | type == "object"
+            and (.name | type == "string")
+            and (.enforcement | type == "string")
+            and (.target | type == "string")
+            and (.conditions.ref_name.include | type == "array")
+            and (.rules | type == "array" and length > 0)
+          ' config/baseline.json > /dev/null 2>&1; then
+            echo "ERROR: rulesets.copilot_code_review is missing required fields"
+            ERRORS=$((ERRORS + 1))
+          fi
           # Validate label structure
As per coding guidelines, "If baseline.json is updated, ensure the rulesets section is present and correctly structured to pass CI."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/quality-checks.yml around lines 90 - 100, The current
check only verifies the top-level .rulesets key; change the validation to assert
the ruleset object structure (e.g., .rulesets.copilot_code_review and its .name
field is present and non-empty) so broken or empty ruleset objects fail CI;
update the workflow loop to use jq checks like ensuring
".rulesets.copilot_code_review | has(\"name\") and .name != \"\" " (or
equivalent) and add similar assertions for any other required nested keys
referenced by sync logic (see rulesets.copilot_code_review and the code path in
scripts/sync-repo-settings.sh that short-circuits when .name is empty) so the
job errors when the nested schema is missing or malformed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/sync-repo-settings.sh`:
- Around line 394-410: Currently the script only checks current_enforcement and
ignores other ruleset fields; instead fetch the full existing ruleset JSON via
gh api (the same endpoint used for $existing) and compare it to the desired
payload (the effective payload produced by jq '.rulesets.copilot_code_review'),
and treat any diff as drift. Replace the current_enforcement check (variables
current_enforcement/existing) with a deep compare between the existing ruleset
JSON and the effective ruleset JSON, set changes to reflect differences, and
when MODE="--apply" PUT the full effective payload to
"repos/$OWNER/$repo/rulesets/$existing" (using the same --input <(echo
"$effective" | jq '.rulesets.copilot_code_review')) while preserving the
existing logging behavior (log WARN on failure, log APPLIED on success, log
DRIFT when not applying).
- Around line 391-417: The code treats any gh api failure as a "missing" ruleset
and always logs "APPLIED" even when POST/PUT fail; fix by checking gh api exit
statuses and outputs rather than swallowing errors. For the initial lookup
(existing) run gh api capturing both output and exit code (e.g., capture output
into a var and check $?), if the call failed log a specific warning and skip
treating the repo as "missing"; for the PUT/POST calls (the gh api -X PUT/POST
lines) capture their exit codes and only set changes/log "APPLIED ..." when the
gh api call returns success; on failure log a clear WARN with the gh output and
do not mark as applied. Use the existing variable names (existing,
current_enforcement, MODE, effective, log) and the same gh api invocation points
to implement these checks.

---

Outside diff comments:
In @.github/workflows/quality-checks.yml:
- Around line 90-100: The current check only verifies the top-level .rulesets
key; change the validation to assert the ruleset object structure (e.g.,
.rulesets.copilot_code_review and its .name field is present and non-empty) so
broken or empty ruleset objects fail CI; update the workflow loop to use jq
checks like ensuring ".rulesets.copilot_code_review | has(\"name\") and .name !=
\"\" " (or equivalent) and add similar assertions for any other required nested
keys referenced by sync logic (see rulesets.copilot_code_review and the code
path in scripts/sync-repo-settings.sh that short-circuits when .name is empty)
so the job errors when the nested schema is missing or malformed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c0de2fab-dfc6-4f57-a0c4-6d7d26bcdfc1

📥 Commits

Reviewing files that changed from the base of the PR and between 74ee2cc and 4fa8b71.

📒 Files selected for processing (5)
  • .github/workflows/quality-checks.yml
  • CLAUDE.md
  • README.md
  • config/baseline.json
  • scripts/sync-repo-settings.sh

- Use jq --arg for safe ruleset name matching (no injection risk)
- Bail early if rulesets API call fails (don't treat errors as missing)
- Compare full ruleset config (name, enforcement, target, conditions,
  rules) instead of just enforcement field
- Only log APPLIED on successful API calls
@gamaware gamaware merged commit 83a8534 into main Mar 11, 2026
8 checks passed
@gamaware gamaware deleted the feat/copilot-ruleset-sync branch March 11, 2026 05:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants