diff --git a/AGENTS.md b/AGENTS.md index 93528898..19cc279b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,6 +19,7 @@ Read the relevant standard *before* making changes that touch CI, repo settings, | **Workflow templates** | [`standards/workflows/`](https://github.com/petry-projects/.github/tree/main/standards/workflows) | Copy-paste-ready templates: `agent-shield.yml`, `dev-lead.yml`, `dependabot-automerge.yml`, `dependabot-rebase.yml`, `dependency-audit.yml`, `feature-ideation.yml` | | **Agent configuration** | [`standards/agent-standards.md`](https://github.com/petry-projects/.github/blob/main/standards/agent-standards.md) | CLAUDE.md / AGENTS.md / SKILL.md required structure, frontmatter rules, cross-references | | **Repo settings + labels** | [`standards/github-settings.md`](https://github.com/petry-projects/.github/blob/main/standards/github-settings.md) | Required settings, label set with exact colors, code-quality ruleset, branch protection | +| **Rulesets** | [`standards/rulesets/`](https://github.com/petry-projects/.github/tree/main/standards/rulesets) | Codified source-of-truth JSON for the org-wide `pr-quality` + `code-quality` rulesets (applied by `apply-rulesets.sh`). `release-channel-tags` stays repo-local in `.github-private` | | **Dependabot config** | [`standards/dependabot-policy.md`](https://github.com/petry-projects/.github/blob/main/standards/dependabot-policy.md) and [`standards/dependabot/`](https://github.com/petry-projects/.github/tree/main/standards/dependabot) | Per-ecosystem dependabot.yml templates and policy | | **Advanced Security (GHAS)** | [`standards/advanced-security.md`](https://github.com/petry-projects/.github/blob/main/standards/advanced-security.md) | Org-wide GHAS enablement via the "GitHub recommended" code security configuration, licensing model, and how to verify push protection actually enforces | | **Push protection** | [`standards/push-protection.md`](https://github.com/petry-projects/.github/blob/main/standards/push-protection.md) | Secret scanning + push protection, local gitleaks hooks, CI secret-scan job, incident response | diff --git a/standards/github-settings.md b/standards/github-settings.md index 33f55654..eb19efb8 100644 --- a/standards/github-settings.md +++ b/standards/github-settings.md @@ -152,6 +152,15 @@ silently drops a merge gate. `check_legacy_rulesets()` in legacy ruleset and reports the exact migration delta (checks to move into `code-quality` first, or "safe to delete"). +**Source of truth.** The codified `pr-quality` and `code-quality` ruleset JSONs +live in this repo at [`standards/rulesets/`](rulesets/) — `.github` owns org-wide +compliance policy (see [`AGENTS.md`](../AGENTS.md#organization-standards) for the +repo-boundary rule, codified in #576). Run `apply-rulesets.sh` to converge each +repo's live ruleset to the desired state documented here. The one ruleset that stays in +`petry-projects/.github-private` is `release-channel-tags` — it protects that +repo's own `pr-review/**` and `dev-lead/**` agent-release tags and is therefore +correctly repo-local. + > **Remediating ruleset findings is a manual, admin-token procedure** — > `compliance-remediate.sh` skips the `rulesets` category. Follow the > [Ruleset Remediation Runbook](ruleset-remediation-runbook.md) (snapshot → diff --git a/standards/rulesets/README.md b/standards/rulesets/README.md new file mode 100644 index 00000000..770b743d --- /dev/null +++ b/standards/rulesets/README.md @@ -0,0 +1,43 @@ +# Repository Rulesets — codified source of truth + +This directory holds the **org-wide compliance ruleset JSONs** for `petry-projects`. +`.github` owns org-wide standards and compliance policy; its canonical home is +`standards/`. See [`AGENTS.md`](../../AGENTS.md#organization-standards) for the +repo-boundary rule (codified in petry-projects/.github#576) and +[`standards/github-settings.md`](../github-settings.md#repository-rulesets) for how +these rulesets are enforced. + +| File | Ruleset | Target | Enforces | +|------|---------|--------|----------| +| [`pr-quality.json`](pr-quality.json) | `pr-quality` | `~DEFAULT_BRANCH` | 1 approval, code-owner review, thread resolution, dismiss-stale, squash-only merge | +| [`code-quality.json`](code-quality.json) | `code-quality` | `~DEFAULT_BRANCH` | Required status checks (SonarCloud, CodeQL, agent-shield, dependency-audit) | + +Both carry the two mandatory bypass actors — `OrganizationAdmin` and the +`dependabot-automerge-petry` Integration app (id `3167543`), both `bypass_mode: always`. + +## Applying + +These files are the **desired-state source of truth**. `scripts/apply-rulesets.sh` +creates/updates the named ruleset on a target repo to match — re-running is a no-op +when already in sync. Rulesets live **on each repo**; editing a file here changes the +desired state, not any live ruleset, until the applier runs. + +```bash +# Preview the payload for one repo (no writes): +GH_TOKEN= bash scripts/apply-rulesets.sh --dry-run +``` + +## Scope boundary + +- **Fleet-wide → here.** `pr-quality` / `code-quality` are org-wide policy. +- **Protects an agent/skill's own assets → stays in `.github-private`.** + `release-channel-tags` lives in `petry-projects/.github-private` because it + protects that repo's own `pr-review/**` and `dev-lead/**` release tags. + +## Changing the required-check set + +Adding a required status-check context to `code-quality.json` makes it a **merge +gate on every repo the ruleset targets**. A repo that does not *produce* that check +is bricked (the required check never reports). Sequence any additions safely: ship +the producing CI job fleet-wide first, then add the context here. Keep stricter sets +scoped to the template / new repos until the fleet backfill lands. diff --git a/standards/rulesets/code-quality.json b/standards/rulesets/code-quality.json new file mode 100644 index 00000000..d64cb211 --- /dev/null +++ b/standards/rulesets/code-quality.json @@ -0,0 +1,37 @@ +{ + "name": "code-quality", + "target": "branch", + "enforcement": "active", + "bypass_actors": [ + { + "actor_type": "OrganizationAdmin", + "bypass_mode": "always" + }, + { + "actor_id": 3167543, + "actor_type": "Integration", + "bypass_mode": "always" + } + ], + "conditions": { + "ref_name": { + "include": ["~DEFAULT_BRANCH"], + "exclude": [] + } + }, + "rules": [ + { + "type": "required_status_checks", + "parameters": { + "required_status_checks": [ + {"context": "SonarCloud"}, + {"context": "CodeQL"}, + {"context": "agent-shield / AgentShield"}, + {"context": "dependency-audit / Detect ecosystems"} + ], + "strict_required_status_checks_policy": true, + "do_not_enforce_on_create": false + } + } + ] +} diff --git a/standards/rulesets/pr-quality.json b/standards/rulesets/pr-quality.json new file mode 100644 index 00000000..93ce6652 --- /dev/null +++ b/standards/rulesets/pr-quality.json @@ -0,0 +1,36 @@ +{ + "name": "pr-quality", + "target": "branch", + "enforcement": "active", + "bypass_actors": [ + { + "actor_type": "OrganizationAdmin", + "bypass_mode": "always" + }, + { + "actor_id": 3167543, + "actor_type": "Integration", + "bypass_mode": "always" + } + ], + "conditions": { + "ref_name": { + "include": ["~DEFAULT_BRANCH"], + "exclude": [] + } + }, + "rules": [ + { + "type": "pull_request", + "parameters": { + "required_approving_review_count": 1, + "require_code_owner_review": true, + "required_review_thread_resolution": true, + "dismiss_stale_reviews_on_push": true, + "require_last_push_approval": true, + "automatic_copilot_code_review_enabled": false, + "allowed_merge_methods": ["squash"] + } + } + ] +}