Skip to content

Commit 4fa8b71

Browse files
committed
feat: enforce Copilot code review ruleset via sync script
- 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
1 parent 74ee2cc commit 4fa8b71

5 files changed

Lines changed: 106 additions & 6 deletions

File tree

.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: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,55 @@ 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+
# Check if the ruleset already exists
391+
local existing
392+
existing=$(gh api "repos/$OWNER/$repo/rulesets" --jq ".[] | select(.name == \"$ruleset_name\") | .id" 2>/dev/null || echo "")
393+
394+
if [ -n "$existing" ]; then
395+
# Verify enforcement is active
396+
local current_enforcement
397+
current_enforcement=$(gh api "repos/$OWNER/$repo/rulesets/$existing" --jq '.enforcement' 2>/dev/null || echo "")
398+
if [ "$current_enforcement" != "active" ]; then
399+
changes="- Copilot review ruleset: enforcement \`$current_enforcement\` -> \`active\`\n"
400+
if [ "$MODE" = "--apply" ]; then
401+
gh api -X PUT "repos/$OWNER/$repo/rulesets/$existing" \
402+
--input <(echo "$effective" | jq '.rulesets.copilot_code_review') \
403+
> /dev/null 2>&1 || log "WARN: Could not update ruleset for $repo"
404+
log "APPLIED ruleset enforcement for $repo"
405+
else
406+
log "DRIFT detected in ruleset enforcement for $repo"
407+
fi
408+
else
409+
log "OK: Copilot review ruleset for $repo"
410+
fi
411+
else
412+
changes="- Copilot review ruleset: **missing** -> will be created\n"
413+
if [ "$MODE" = "--apply" ]; then
414+
gh api -X POST "repos/$OWNER/$repo/rulesets" \
415+
--input <(echo "$effective" | jq '.rulesets.copilot_code_review') \
416+
> /dev/null 2>&1 || log "WARN: Could not create ruleset for $repo"
417+
log "APPLIED Copilot review ruleset for $repo"
418+
else
419+
log "DRIFT detected: missing Copilot review ruleset for $repo"
420+
fi
421+
fi
422+
echo -e "$changes"
423+
}
424+
376425
# Check default branch matches config
377426
check_default_branch() {
378427
local repo="$1"
@@ -513,6 +562,13 @@ REPORT_HEADER
513562
repo_drift="${repo_drift}### Branch Protection\n\n${protection_changes}\n"
514563
fi
515564

565+
# Rulesets (Copilot code review)
566+
local ruleset_changes
567+
ruleset_changes=$(sync_rulesets "$repo")
568+
if [ -n "$ruleset_changes" ]; then
569+
repo_drift="${repo_drift}### Rulesets\n\n${ruleset_changes}\n"
570+
fi
571+
516572
# Labels
517573
local label_changes
518574
label_changes=$(sync_labels "$repo")

0 commit comments

Comments
 (0)