Skip to content

Commit 83a8534

Browse files
authored
feat: enforce Copilot code review ruleset via sync script (#9)
## 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 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## 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. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 74ee2cc commit 83a8534

File tree

5 files changed

+122
-6
lines changed

5 files changed

+122
-6
lines changed

.github/workflows/quality-checks.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ jobs:
9090
- name: Validate baseline schema
9191
run: |
9292
ERRORS=0
93-
for section in repo_settings security branch_protection labels required_files; do
93+
for section in repo_settings security branch_protection rulesets labels required_files; do
9494
if ! jq -e ".$section" config/baseline.json > /dev/null 2>&1; then
9595
echo "ERROR: Missing section '$section' in baseline.json"
9696
ERRORS=$((ERRORS + 1))

CLAUDE.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,11 @@ The sync script (`scripts/sync-repo-settings.sh`) enforces:
100100
alerts
101101
3. **Branch protection**: reviews, CODEOWNERS, linear history,
102102
conversation resolution
103-
4. **Labels**: standard issue labels across all repos
104-
5. **Default branch**: ensures all repos use `main`
105-
6. **Metadata**: flags missing descriptions and topics (advisory)
106-
7. **Required files**: LICENSE, README, CODEOWNERS, etc.
103+
4. **Rulesets**: Copilot code review ruleset on default branch
104+
5. **Labels**: standard issue labels across all repos
105+
6. **Default branch**: ensures all repos use `main`
106+
7. **Metadata**: flags missing descriptions and topics (advisory)
107+
8. **Required files**: LICENSE, README, CODEOWNERS, etc.
107108

108109
Configuration lives in `config/baseline.json` with per-repo
109110
overrides in `config/overrides.json`.

README.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,18 @@ ensures clean `git log` and bisectability.
9898
| Required conversation resolution | `true` | All comments must be resolved |
9999
| Enforce admins | `false` | Admins can bypass when needed |
100100

101+
### Rulesets
102+
103+
The Copilot code review ruleset is enforced on every repository's
104+
default branch. The sync script creates it if missing and verifies
105+
enforcement is active.
106+
107+
| Rule | Purpose |
108+
| --- | --- |
109+
| `deletion` | Prevent branch deletion |
110+
| `non_fast_forward` | Prevent force pushes |
111+
| `copilot_code_review` | Require Copilot review on PRs |
112+
101113
### Labels
102114

103115
Standard labels are created on every repo for consistent issue
@@ -272,8 +284,15 @@ Add repo names to the `excluded` array in `config/overrides.json`:
272284

273285
## Code Review
274286

287+
- **GitHub Copilot**: auto-review via ruleset (enforced automatically
288+
by the sync script)
275289
- **CodeRabbit**: auto-review on PRs via `.coderabbit.yaml`
276-
- **GitHub Copilot**: auto-review via ruleset with custom instructions
290+
291+
> **Note:** CodeRabbit must be enabled manually per repository through
292+
> the [CodeRabbit dashboard](https://app.coderabbit.ai). There is no
293+
> API to automate this. After installing the GitHub App, select "All
294+
> repositories" to cover new repos automatically, or add repos
295+
> individually through the dashboard.
277296
278297
## CI/CD Pipelines
279298

config/baseline.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,30 @@
6666
"description": "New repository discovered"
6767
}
6868
],
69+
"rulesets": {
70+
"copilot_code_review": {
71+
"name": "Copilot review for default branch",
72+
"enforcement": "active",
73+
"target": "branch",
74+
"conditions": {
75+
"ref_name": {
76+
"include": ["~DEFAULT_BRANCH"],
77+
"exclude": []
78+
}
79+
},
80+
"rules": [
81+
{"type": "deletion"},
82+
{"type": "non_fast_forward"},
83+
{
84+
"type": "copilot_code_review",
85+
"parameters": {
86+
"review_on_push": false,
87+
"review_draft_pull_requests": false
88+
}
89+
}
90+
]
91+
}
92+
},
6993
"required_files": [
7094
"LICENSE",
7195
"README.md",

scripts/sync-repo-settings.sh

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,71 @@ sync_labels() {
373373
echo -e "$changes"
374374
}
375375

376+
# Ensure Copilot code review ruleset exists
377+
sync_rulesets() {
378+
local repo="$1"
379+
local effective
380+
effective=$(get_effective_settings "$repo")
381+
local changes=""
382+
383+
local ruleset_name
384+
ruleset_name=$(echo "$effective" | jq -r '.rulesets.copilot_code_review.name // empty')
385+
if [ -z "$ruleset_name" ]; then
386+
echo ""
387+
return
388+
fi
389+
390+
# List rulesets — bail if the API call itself fails
391+
local rulesets_json existing
392+
if ! rulesets_json=$(gh api "repos/$OWNER/$repo/rulesets" 2>/dev/null); then
393+
log "WARN: Could not list rulesets for $repo"
394+
echo ""
395+
return
396+
fi
397+
existing=$(echo "$rulesets_json" | jq -r --arg name "$ruleset_name" '.[] | select(.name == $name) | .id' | head -n1)
398+
399+
local desired_ruleset
400+
desired_ruleset=$(echo "$effective" | jq '.rulesets.copilot_code_review')
401+
402+
if [ -n "$existing" ]; then
403+
# Compare full ruleset config, not just enforcement
404+
local current_ruleset desired_normalized current_normalized
405+
current_ruleset=$(gh api "repos/$OWNER/$repo/rulesets/$existing" 2>/dev/null || echo "")
406+
desired_normalized=$(echo "$desired_ruleset" | jq -cS '{name, enforcement, target, conditions, rules}')
407+
current_normalized=$(echo "$current_ruleset" | jq -cS '{name, enforcement, target, conditions, rules}')
408+
if [ "$current_normalized" != "$desired_normalized" ]; then
409+
changes="- Copilot review ruleset: configuration drift detected\n"
410+
if [ "$MODE" = "--apply" ]; then
411+
if gh api -X PUT "repos/$OWNER/$repo/rulesets/$existing" \
412+
--input <(echo "$desired_ruleset") \
413+
> /dev/null 2>&1; then
414+
log "APPLIED Copilot review ruleset for $repo"
415+
else
416+
log "WARN: Could not update ruleset for $repo"
417+
fi
418+
else
419+
log "DRIFT detected in Copilot review ruleset for $repo"
420+
fi
421+
else
422+
log "OK: Copilot review ruleset for $repo"
423+
fi
424+
else
425+
changes="- Copilot review ruleset: **missing** -> will be created\n"
426+
if [ "$MODE" = "--apply" ]; then
427+
if gh api -X POST "repos/$OWNER/$repo/rulesets" \
428+
--input <(echo "$desired_ruleset") \
429+
> /dev/null 2>&1; then
430+
log "APPLIED Copilot review ruleset for $repo"
431+
else
432+
log "WARN: Could not create ruleset for $repo"
433+
fi
434+
else
435+
log "DRIFT detected: missing Copilot review ruleset for $repo"
436+
fi
437+
fi
438+
echo -e "$changes"
439+
}
440+
376441
# Check default branch matches config
377442
check_default_branch() {
378443
local repo="$1"
@@ -513,6 +578,13 @@ REPORT_HEADER
513578
repo_drift="${repo_drift}### Branch Protection\n\n${protection_changes}\n"
514579
fi
515580

581+
# Rulesets (Copilot code review)
582+
local ruleset_changes
583+
ruleset_changes=$(sync_rulesets "$repo")
584+
if [ -n "$ruleset_changes" ]; then
585+
repo_drift="${repo_drift}### Rulesets\n\n${ruleset_changes}\n"
586+
fi
587+
516588
# Labels
517589
local label_changes
518590
label_changes=$(sync_labels "$repo")

0 commit comments

Comments
 (0)