Skip to content

feat(rulesets): org_push_protection — native max_file_size + banned extensions (Step 1)#3

Merged
JacobPEvans-personal merged 1 commit into
mainfrom
feat/step-1-push-protection
May 31, 2026
Merged

feat(rulesets): org_push_protection — native max_file_size + banned extensions (Step 1)#3
JacobPEvans-personal merged 1 commit into
mainfrom
feat/step-1-push-protection

Conversation

@JacobPEvans-personal
Copy link
Copy Markdown
Member

Summary

First native org-wide push-protection ruleset. Replaces the historical pattern of per-repo file-size caller workflows with GitHub's own max_file_size and file_extension_restriction push rules — enforced at the git layer, no workflow runs, no per-repo wiring.

What lands

  • rulesets.tf — new github_organization_ruleset.org_push_protection, target = "push", ~ALL repos, two rule blocks (max_file_size, file_extension_restriction). Bypass actors deliberately not declared so manual exemptions in the GitHub UI persist across applies.
  • locals.tf — new. Decodes config/rulesets-defaults.yml into a named local so rulesets.tf references typed terraform values, not raw file reads inside each resource.
  • variables.tf — adds org_push_protection_enforcement (disabled / evaluate / active, default "active" per the convention; the variable still exists so a misbehaving rule can be disabled with -var without a code change).
  • config/rulesets-defaults.ymlbanned_file_extensions reshaped to fnmatch globs (*.env, *.pem, *.key, *.p12, *.pfx) matching the provider's restricted_file_extensions input format. max_file_size_mb: 1 unchanged.
  • README.md — "What it manages today" includes the new ruleset; Layout block adds locals.tf; the "next" line trims file-size limits (covered now) and adds labels + per-repo file content.

What this replaces

This is the answer to the PR #332 incident (README outgrew the custom workflow's 12 KB limit). The native push rule has a 1 MB ceiling that no markdown file in the workspace will hit organically; per-repo .file-size.yml overrides and per-repo caller workflows become obsolete. A follow-up cleanup PR deletes them across the fleet, along with _file-size.yml in JacobPEvans-personal/.github.

Apply

Requires the ORG_ADMIN token tier (gh-claude-org-admin). DRYVIST is read-only on org rulesets and will 403 on apply. The variable defaults to active, so:

```bash
gh-claude-org-admin # relaunch Claude or your shell with the elevated tier
tofu init -backend-config=...
tofu apply
```

The first apply creates the ruleset live and starts blocking any push that contains a > 1 MB file or a file matching one of the banned globs. Bypass adjustments happen in the GitHub UI after.

Test plan

  • tofu init -backend=false && tofu validate — green
  • pre-commit run --all-files — every hook passes
  • Commit GPG-signed
  • Apply with gh-claude-org-admin and confirm new ruleset appears at https://github.com/organizations/dryvist/settings/rules
  • Sacrificial test: push a 2 MB binary on a throwaway branch → rejected at the git layer
  • Sacrificial test: push a test.env file on a throwaway branch → rejected
  • Confirm an existing PR with a > 12 KB markdown file (e.g. dryvist/terraform-proxmox README, ~33 KB) is NOT rejected (well under the 1 MB ceiling — that's the whole point)

…xtensions

Adds the first native org-wide push-protection ruleset. Enforced at the
git layer by GitHub's own push rules; no workflow runs and no per-repo
caller. Replaces the historical per-repo file-size workflow callers
(those become deletable in a follow-up cleanup step).

What lands
----------
- rulesets.tf: new resource `github_organization_ruleset.org_push_protection`
  (target = "push", ~ALL repos). Two rule blocks: `max_file_size` (hard
  ceiling per file) and `file_extension_restriction` (banned globs).
  Bypass actors are intentionally not declared so manual exemptions in
  the GitHub UI persist across applies.
- locals.tf: new. Decodes config/rulesets-defaults.yml into a named
  `push_protection_defaults` local, so rulesets.tf references typed
  terraform values instead of raw file reads inside each resource.
- variables.tf: adds `org_push_protection_enforcement` (one of disabled
  / evaluate / active, default "active" per the new convention).
- config/rulesets-defaults.yml: `banned_file_extensions` reshaped to
  fnmatch glob form (`*.env`, `*.pem`, …) matching the
  integrations/github provider's `restricted_file_extensions` input
  format. `max_file_size_mb` unchanged (1 MB).
- README.md: "What it manages today" table now includes the new
  ruleset; the Layout block adds `locals.tf`; the "next" line trims
  file-size limits (covered now) and adds labels and per-repo file
  content.

Verification
------------
- tofu init -backend=false and tofu validate -> green
- pre-commit run --all-files -> all hooks pass

Apply
-----
Requires the ORG_ADMIN token tier (gh-claude-org-admin). DRYVIST is
read-only on org rulesets and will 403 on apply.

Assisted-by: Claude <noreply@anthropic.com>
@JacobPEvans-personal JacobPEvans-personal merged commit a0d5093 into main May 31, 2026
3 checks passed
JacobPEvans-personal added a commit that referenced this pull request May 31, 2026
…ate, conventional commits (#4)

Codifies the pre-Terraform live state of the org-level rulesets and applies
the directives from #2/#3 review. Single PR because the imports must land
together with the new resources to avoid duplicate ruleset creation on
next apply.

New rulesets
------------
- `github_organization_ruleset.org_branch_protection` — non-bypassable
  quality gate on every default branch. Reverse-engineered from the
  pre-Terraform "main" org ruleset (id 15555419) and extended with strict
  Conventional Commits enforcement. Rules:
    - required_linear_history = true
    - required_signatures     = true
    - branch_name_pattern     = starts_with (main|develop|feat|fix|hotfix|release|chore)
    - commit_message_pattern  = regex enforcing Conventional Commits v1.0.0
    - pull_request            = thread resolution required, 0 approvers
                                (the approver requirement lives in
                                org_review_gate with admin bypass)
  No bypass_actors: signed commits, linear history, and Conventional
  Commits apply to every actor including OrganizationAdmins.

- `github_organization_ruleset.org_review_gate` — separate ruleset so the
  admin bypass below doesn't accidentally weaken the quality gates above.
  Rules:
    - pull_request { required_approving_review_count = 1,
                     require_code_owner_review = true,
                     required_review_thread_resolution = true,
                     allowed_merge_methods = [squash, rebase] }
  bypass_actors: OrganizationAdmin (actor_id=1, the API constant) in
  pull_request mode. Any OrganizationAdmin merges their own PRs without
  external review; non-admin actors must obtain the review.

  GitHub rulesets do not support per-author conditional rules. The closest
  approximation — "everyone but an admin needs a reviewer" — is
  implemented via OrganizationAdmin bypass. Granting a new account the
  OrganizationAdmin role extends this bypass to them.

Existing ruleset reconciled
---------------------------
- `github_organization_ruleset.markdown_lint` updated to match live state
  for clean import: ref_name = ~ALL (was ~DEFAULT_BRANCH),
  do_not_enforce_on_create = true. Default enforcement stays "evaluate"
  (legacy default); pass `-var markdown_lint_enforcement=active` to flip
  on. Live is currently "disabled" — apply moves it to "evaluate".

Import blocks
-------------
Declared in rulesets.tf so `tofu apply` adopts pre-Terraform state in the
same run that adds the new resources:

  import { to = github_organization_ruleset.org_branch_protection
           id = "15555419" }
  import { to = github_organization_ruleset.markdown_lint
           id = "17062292" }

org_review_gate is new (no existing live ruleset), so no import needed.

Config — no magic numbers
-------------------------
- config/rulesets-defaults.yml gains a `branch_protection` block with the
  branch name pattern, the Conventional Commits regex, and the allowed
  merge methods. Patterns are tunable here without editing .tf.
- locals.tf decodes the full config file once and exposes per-section
  locals (push_protection_defaults, branch_protection_defaults).

Conventions — identities out of source tree
-------------------------------------------
- AGENTS.md adds a "No identities anywhere except providers.tf owner"
  rule. No usernames, account logins, or person-tied identifiers in .tf
  resource bodies, comments, variable descriptions, config/*.yml, or
  CODEOWNERS-style files committed in this repo. CODEOWNERS for managed
  repos materializes at apply time from a Terraform variable.
- The `.github/CODEOWNERS` file that an earlier commit attempt added is
  intentionally NOT in this PR — it would have hardcoded an owner login.
  CODEOWNERS for this repo and all dryvist repos lands in a follow-up PR
  via `github_repository_file` with the owner identity as a `-var` input.

Conventions — cost policy
-------------------------
- AGENTS.md adds a "Cost policy" section documenting GitHub's pricing
  model and the rule "never apply a policy or enable a feature that
  costs money unless the PR body declares the cost and the operator
  approves it." Includes a matrix of free / GHAS-gated / metered /
  subscription features as of 2026-05, with sources, and a per-PR
  cost-impact checklist for any change that touches Actions, GHAS,
  Codespaces, Copilot, or org/repo settings affecting those.

Variables
---------
- New: org_branch_protection_enforcement (default "active")
- New: org_review_gate_enforcement (default "active")

Apply
-----
Requires the ORG_ADMIN token tier (gh-claude-org-admin). Apply will:
  1. Adopt rulesets 15555419 and 17062292 into Terraform state
  2. Rename the imported rulesets to org-branch-protection /
     org-markdown-lint
  3. Add commit_message_pattern rule to the imported branch-protection
     ruleset
  4. Create new org-review-gate ruleset
  5. Move markdown_lint enforcement from disabled -> evaluate

Cost impact
-----------
Free — all ruleset rules are native GitHub features at zero cost on any
plan. The markdown_lint workflow runs on Actions; public repos are free,
private repos count against the Team-plan free-tier minutes (3000/month
GitHub-hosted, $0.002/min self-hosted as of 2026-03-01). No GHAS feature
flipped on by this PR.

Verification
------------
- tofu init -backend=false and tofu validate -> green
- pre-commit run --all-files -> all hooks pass

Assisted-by: Claude <noreply@anthropic.com>
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.

1 participant