activescott / activescott/auth

Require CI checks before merging PRs without breaking the simple-release push to main

Aperta
#63 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub
Lingua principale
TypeScript
Stelle
0
Fork
1
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Descrizione

## Problem

`main` has **no branch protection** today (`GET /repos/activescott/auth/branches/main/protection` returns empty). Any PR can be merged with red, missing, or never-started checks, and the release workflow then runs against whatever landed. This came up during the 2026-08-06 GitHub Actions outage, when PR #62's `validate` and `example-e2e` jobs were cancelled by the platform without ever being assigned a runner — and the PR was still mergeable.

We want CI (`validate`, `example-e2e`) to be **required** before a PR can merge.

## Why it isn't a one-liner

The release job pushes directly to `main`. `scripts/release.ts:46` calls `simple-release`'s `GithubHosting.push()`, which commits the version bumps and pushes the commit + tags as `github-actions[bot]` (e.g. commit `857dde2`, committer `github-actions[bot]`).

Rulesets and classic branch protection gate **direct pushes** as well as PR merges. So naively requiring a PR and/or status checks on `main` rejects the release bot's push, and **publishing silently stops**.

The usual escape hatch — exempting the GitHub Actions app as a bypass actor — is unavailable here. Attempting it returns:

```
422 Validation Failed
Actor GitHub Actions integration must be part of the ruleset source or owner organization
```

`activescott` is a personal account, not an org, so `actor_type: "Integration"` bypasses aren't allowed. The draft ruleset that failed is otherwise sound: PR required (0 approvals), required status checks `validate` + `example-e2e`, plus `deletion` and `non_fast_forward` rules.

## Constraint: no long-lived credentials on runners

The obvious workaround — issue a fine-grained PAT for the repo owner, store it as an Actions secret, and have the release job push with it (then bypass via the admin repository role) — is **explicitly not wanted**. Long-lived tokens sitting in CI were the exact target of the recent npm/GitHub credential-harvesting campaigns. A solution should avoid adding a durable push credential to the runner environment.

Note the repo already avoids a long-lived npm token by using OIDC trusted publishing for `npm publish`; the git push is the remaining gap.

## Options to evaluate

1. **Release opens a PR instead of pushing.** Ruleset enforced with no bypass at all; the version-bump commit goes through an auto-merging PR. No new credential. Costs a round trip per release and some rework of `scripts/release.ts` / the workflow — needs a look at whether `simple-release` supports a PR-based flow or whether we drive it ourselves.
2. **GitHub App with short-lived tokens.** Create an app owned by the account, install it on the repo, mint an installation token at job runtime (e.g. `actions/create-github-app-token`), push with that. Tokens are minted per run and expire in an hour, so nothing durable lives on the runner — but the app's private key does become an Actions secret, so this trades one long-lived secret for another (arguably better scoped, still worth weighing against option 1).
3. **Move the repo to an organization.** Unlocks `Integration` bypass actors, making the original ruleset work as drafted. Largest blast radius; probably only worth it if there are other reasons to want an org.
4. **Status quo + convention.** Checks still run and are visible on every PR; merging red is possible but not done. Zero risk to releases, no enforcement.

## Suggested next step

Try option 1 first — it's the only one that adds no secret at all. If `simple-release` can't be driven that way without significant surgery, evaluate option 2.

## Acceptance criteria

- Merging a PR into `main` is blocked until `validate` and `example-e2e` pass.
- `chore(release)` commits and package tags still reach `main` automatically, and npm publishing still works.
- No long-lived push credential is stored in Actions secrets or on runners.

## Context / references

- The draft ruleset JSON and the 422 above were produced on 2026-08-06 while investigating this.
- Same pattern exists in `activescott/ramblefeed` (its release job also pushes to `main`), so whatever we land here is probably worth repeating there.

Guida per i contributori

Nessuna guida per i contributori indicizzata per questo repository

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.