Skip to content

ci(branch-protection): self-heal + protect release tags#105

Merged
CameronBrooks11 merged 1 commit into
mainfrom
ci/branch-protection-heal
Jul 4, 2026
Merged

ci(branch-protection): self-heal + protect release tags#105
CameronBrooks11 merged 1 commit into
mainfrom
ci/branch-protection-heal

Conversation

@CameronBrooks11

Copy link
Copy Markdown
Member

Delivers the self-healing branch protection + release-tag protection from #94.

What

  • branch-protection-heal.yml — weekly cron (0 6 * * 3) + workflow_dispatch. Runs apply-branch-protection.sh with an admin PAT so drift is re-asserted, not just reported. Fails fast with an actionable message if the secret is missing.
  • Tag protection — a per-repo repository ruleset (protect-version-tags). The classic tag-protection REST API was retired by GitHub (404), so rulesets are the vehicle (free on public repos). Blocks deletion + force-push (non_fast_forward); creation of new release tags stays allowed.
  • Robust per-repo classificationOK / SKIPPED (archived) / FAILED; exits non-zero only on a genuine failure, so an archived repo no longer breaks the run (the operator-ui breakage from chore: drop archived anolis-operator-ui from branch-protection list #95).
  • Reconciliation report — enumerates non-archived public org repos and loudly flags any not in REPOS.

Design decisions (called out for review)

  • Tag pattern is refs/tags/v*.* (dotted), not v*. The shared-actions release flow re-points the moving bare-major aliases v1/v2 with git tag -f; a v* + non_fast_forward rule would block that and break org-wide action pinning. Immutable release tags always carry a dot (v2.4, v0.2.7); the aliases never do — so v*.* protects exactly the tags that must never move and leaves the aliases free. This also means no bypass actor is needed.
  • Hybrid enrollment (owner-confirmed). Explicit REPOS allowlist for enforcement; dynamic gh repo list for reporting only. Full dynamic enrollment would risk permanently blocking PRs on a repo whose CI doesn't yet expose the required ok check.
  • Heal-not-audit (scope note). The run re-asserts protection every time (idempotent) rather than GET+diffing to report setting-level drift. Enrollment drift is surfaced; setting-level drift-diff can be a follow-up if wanted.

Validation

  • shellcheck clean; bash -n OK; --dry-run verified.
  • Ruleset create → lookup-by-name → idempotent PUT update → delete cycle exercised live against GitHub's API with the exact v*.* payload (test rulesets cleaned up).
  • Adversarial correctness + design/acceptance reviews run; findings addressed.

Remaining owner step (activation)

This ships the mechanism. It activates once you provision the BRANCH_PROTECTION_PAT secret (fine-grained PAT, Administration: read/write on all anolishq repos) — see the README. After that I can trigger the first workflow_dispatch run and monitor it. Leaving #94 open until activated.

Refs #94

Add a scheduled self-healing workflow that re-asserts main branch
protection across the org, and extend the script to protect immutable
release tags.

- branch-protection-heal.yml: weekly cron + workflow_dispatch, runs the
  script with an admin PAT (BRANCH_PROTECTION_PAT), fails fast with an
  actionable message if the secret is unset.
- Tag protection via a repository ruleset (the classic tag-protection API
  was retired). Targets dotted tags (refs/tags/v*.*, e.g. v2.4, v0.2.7)
  so the moving bare-major aliases (v1/v2) the shared-actions release
  flow re-points stay mutable; blocks deletion + non-fast-forward.
- Per-repo OK / SKIPPED(archived) / FAILED classification; exits non-zero
  only on genuine failure so an archived repo no longer breaks the run.
- Reconciliation report enumerates non-archived public org repos and
  flags any not enrolled (dynamic discovery, explicit enrollment).

Refs #94
@CameronBrooks11
CameronBrooks11 enabled auto-merge (squash) July 4, 2026 03:20
@CameronBrooks11
CameronBrooks11 merged commit 3601c96 into main Jul 4, 2026
9 checks passed
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