Skip to content

Add branch protection rules to main #97

Description

@PAMulligan

Description

Configure GitHub branch protection on main so direct pushes, force-pushes, and accidental deletions are blocked, and so PRs cannot merge until CI is green.

Why

While shipping v0.6.0 a malformed release tag (v0.5.1) had to be rolled back with git push --force-with-lease +<sha>:main. That was only possible because main is currently unprotected — and would have been irrecoverable if anyone else had already pulled the bad state. As contributor count grows (or once external PRs start landing) the cost of an accidentally-broken main rises sharply. The stable-release contract advertised by the v1.0.0 milestone needs guarantees behind it, so this should land before v1.0 ships.

Acceptance Criteria

Configure the main branch (Settings → Branches, or via gh api -X PUT repos/PMDevSolutions/Nerva/branches/main/protection):

  • Block force-pushes (allow_force_pushes: false)
  • Block deletion (allow_deletions: false)
  • Require a pull request before merging
  • Require status checks to pass before merging, with these checks required (names from .github/workflows/ci.yml):
    • Validate Structure
    • Node.js 20 Compatibility
    • Node.js 22 Compatibility
    • Type-check Embedded Templates
    • Check Markdown Links
  • Require branches to be up to date before merging
  • Require linear history (matches the current squash-merge workflow used by gh pr merge --squash)
  • Allow admin/maintainer bypass so urgent rollbacks remain possible
  • Document the protection rules in CONTRIBUTING.md so contributors know what CI must pass before review

Release-workflow compatibility

The current .github/workflows/release.yml pushes directly to main (git push --follow-tags origin ${{ github.ref_name }}) after commit-and-tag-version creates a bump commit and tag. Once "require PR before merge" is enabled this push will fail unless we make a deliberate choice:

  • Option A — bypass: Add the github-actions[bot] actor (or a dedicated release PAT) to the branch-protection bypass list. Smallest change; keeps current release.yml working as-is.
  • Option B — release PR: Switch to a release-PR flow (e.g. release-please, or have release.yml open a PR from a release/vX.Y.Z branch and auto-merge once CI is green). Cleaner audit trail, more moving parts.
  • Option C — unprotected release branch: Run the release on a separate branch (e.g. release) that doesn't have PR-required protection, then merge to main. Mostly a worse hybrid.

Decide between these as part of this issue. A is the pragmatic default for a small team; B is the right end state if/when the project grows beyond solo maintenance.

Out of scope

  • CODEOWNERS configuration
  • Signed-commit enforcement (required_signatures) — probably premature pre-1.0
  • Required reviews from specific reviewers (single-maintainer project for now)
  • Protecting non-main branches (release/*, staging, etc. — only relevant if Option B/C is chosen)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions