chore: add branch protection ruleset specs (PR-1 of 3)
Checks in JSON specs for three rulesets (main-protection, release-branches, tag-immutability), a CODEOWNERS file (advisory), and scripts/sync-rulesets.sh to apply them. Enforcement is `disabled` in all three files — PR-2 will apply with `evaluate` for a 1-week dry-run, PR-3 will flip to `active`. See docs/branch-protection.md for the full rollout plan.
This commit is contained in:
68
docs/branch-protection.md
Normal file
68
docs/branch-protection.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# Branch Protection Rollout
|
||||
|
||||
## Rulesets
|
||||
|
||||
Three ruleset specs live under `.github/rulesets/`. All are committed with
|
||||
`enforcement: disabled` and activated in stages via the 3-PR rollout below.
|
||||
|
||||
### `main-protection`
|
||||
Targets `~DEFAULT_BRANCH` (main). Enforces:
|
||||
- No deletions or force pushes
|
||||
- Required linear history (no merge commits)
|
||||
- All changes via pull request (0 required approvals, stale-review dismissal, thread resolution required, squash/rebase only)
|
||||
- 10 required status checks: lint, Node 22/24 matrix (ubuntu/mac/windows), Changeset Required, require-issue-link, pr-template-format
|
||||
|
||||
### `release-branches`
|
||||
Targets `refs/heads/release/**` and `refs/heads/hotfix/**`. Same rules as
|
||||
`main-protection` except `required_linear_history` is omitted (merge commits
|
||||
are permitted on release/hotfix branches).
|
||||
|
||||
### `tag-immutability`
|
||||
Targets all tags (`~ALL`). Blocks tag updates and deletions — tags are
|
||||
immutable once created. Tag creation is unrestricted.
|
||||
|
||||
## 3-PR Rollout Plan
|
||||
|
||||
| PR | Branch | Action |
|
||||
|----|--------|--------|
|
||||
| PR-1 (this PR) | `chore/branch-protection-specs` | Check in spec files; `enforcement: disabled` — no effect on repo |
|
||||
| PR-2 | `chore/branch-protection-evaluate` | Run `sync-rulesets.sh` with `ENFORCEMENT=evaluate`; 1-week dry-run via rule-suite logs |
|
||||
| PR-3 | `chore/branch-protection-active` | Run `sync-rulesets.sh` with `ENFORCEMENT=active`; protection live |
|
||||
|
||||
## Running `sync-rulesets.sh`
|
||||
|
||||
**Prerequisites:** `gh` authenticated with repo-admin scope, `jq` installed.
|
||||
|
||||
```bash
|
||||
# Dry-run (evaluate mode — logs violations, does not block)
|
||||
REPO=GSD-redux/get-shit-done-redux ENFORCEMENT=evaluate bash scripts/sync-rulesets.sh
|
||||
|
||||
# Activate protection
|
||||
REPO=GSD-redux/get-shit-done-redux ENFORCEMENT=active bash scripts/sync-rulesets.sh
|
||||
|
||||
# Roll back to disabled
|
||||
REPO=GSD-redux/get-shit-done-redux ENFORCEMENT=disabled bash scripts/sync-rulesets.sh
|
||||
```
|
||||
|
||||
The script is idempotent: running it twice with the same `ENFORCEMENT` value
|
||||
is a no-op semantically (PUT with identical body).
|
||||
|
||||
## Reading evaluate-mode logs
|
||||
|
||||
After applying with `evaluate`, check which PRs/pushes would have been blocked:
|
||||
|
||||
```bash
|
||||
REPO=GSD-redux/get-shit-done-redux
|
||||
RULESET_ID=$(gh api repos/$REPO/rulesets --jq '.[] | select(.name=="main-protection") | .id')
|
||||
gh api repos/$REPO/rulesets/$RULESET_ID/rule-suites
|
||||
```
|
||||
|
||||
Each entry shows the actor, ref, result (`pass`/`fail`), and which rules
|
||||
triggered. Use this to validate no legitimate workflows are broken before
|
||||
flipping to `active` in PR-3.
|
||||
|
||||
## Phase-2 TODO
|
||||
|
||||
Enable the `required_signatures` rule (signed commits) once agent commits sign
|
||||
uniformly. As of PR-1 this rule is intentionally omitted — unsigned agent
|
||||
commits would be blocked by it. Track readiness in the issue linked to PR-3.
|
||||
Reference in New Issue
Block a user