Rampart v0.7: Push Allowlists for Protected Branches
Rampart v0.7 adds support for the one piece of GitHub branch protection it was missing: the push allowlist.
The Problem
The standard “require N approvals” pattern works great when there are at least N+1 humans on the team. For a lot of my repos there’s exactly one human, which is me, so that pattern just doesn’t work.
What I actually want is: nobody force-pushes to main by accident, and no bot can sneak a commit in. The “nobody force-pushes” part rampart could already enforce. The “no bot” part it couldn’t, because the only GitHub-native way to express that is the push restrictions list, and rampart hardcoded that to nil on every apply.
So an automation account that got hold of a repo token could happily push straight to main, and rampart would still call the repo “compliant” because the rules it knew about were all green. Not great.
What Changed
You can now put a restrictions block under rules in your rampart.yaml:
rules:
require_pull_request: true
required_approvals: 0
restrictions:
users: [wdm0006]
teams: []
apps: [renovate]
Anyone not on that list cannot push directly to the protected branch, even if they have write access to the repo. apply writes the allowlist via classic branch protection (which natively accepts user, team, and app names). audit reads it back and compares set-equality. If you set allow_stricter_rules: true and a repo’s actual allowlist is a subset of what you configured, that passes too – smaller allowlist is stricter.
Overrides work the same way as the other rules, with one wrinkle: an override’s restrictions block fully replaces the base, it doesn’t merge. Merging actor lists is almost never what you actually want. If you need an override that clears a base allowlist, set restrictions: null.
The Ruleset Caveat
GitHub has two branch protection systems now: the classic API and the newer rulesets API. Rampart’s apply writes to both. For restrictions specifically, only the classic side gets the allowlist, because the ruleset equivalent (bypass_actors) wants numeric actor IDs that I’d have to resolve from names with extra API calls. That’s a bigger piece of work and the issue flagged it as needing a design pass, so I split it out for later.
In practice this is fine for most repos – the classic restrictions list is what GitHub actually enforces. If you’re on a repo where only rulesets are configured and classic protection is fully off, the allowlist won’t take effect there yet. The rampart config help calls this out.
Upgrading
brew upgrade rampart
Or grab v0.7.0 from the releases page.
Stay in the loop
Get notified when I publish new posts. No spam, unsubscribe anytime.