Renovate: auto-merge non-major npm updates once CI is green (#56) #82

Merged
claude-bot merged 2 commits from chore/renovate-automerge-npm into main 2026-08-31 04:23:50 +00:00
Contributor

Third checkbox on #56, and the first change to land through the new branch protection.

renovate.json already auto-merged github-actions minor/patch/digest bumps. This extends the same treatment to the batched npm (non-major) group.

Why this is safe: Renovate's automerge waits for a green branch, so it cannot land anything CI rejects. The eslint bump that broke lint earlier today would have sat red and unmerged rather than auto-merging itself in. Majors are untouched — they stay behind major.dependencyDashboardApproval and get merged by hand.

Why it pairs with protection: gating merges on green checks is precisely what makes unattended merging of routine dependency churn safe. Six weeks of drift produced 5 stale PRs and 6 advisories; this stops the queue rotting again.


This PR doubles as the end-to-end test of the protection rule enabled for #56:

  • direct pushes to main are blocked — verified, Forgejo: Not allowed to push to protected branch main
  • status_check_contexts: ["CI / *"] (the pattern the sibling repos on this instance use), so the CI workflow's jobs must pass
  • the new Dependency audit workflow is deliberately not matched by that glob, so advisories report without gating merges
  • required_approvals: 0, since requiring one on a single-maintainer repo would deadlock on self-approval
Third checkbox on #56, and the first change to land through the new branch protection. `renovate.json` already auto-merged `github-actions` minor/patch/digest bumps. This extends the same treatment to the batched `npm (non-major)` group. **Why this is safe:** Renovate's automerge waits for a green branch, so it cannot land anything CI rejects. The eslint bump that broke lint earlier today would have sat red and unmerged rather than auto-merging itself in. Majors are untouched — they stay behind `major.dependencyDashboardApproval` and get merged by hand. **Why it pairs with protection:** gating merges on green checks is precisely what makes unattended merging of routine dependency churn safe. Six weeks of drift produced 5 stale PRs and 6 advisories; this stops the queue rotting again. --- This PR doubles as the end-to-end test of the protection rule enabled for #56: - direct pushes to `main` are blocked — verified, `Forgejo: Not allowed to push to protected branch main` - `status_check_contexts: ["CI / *"]` (the pattern the sibling repos on this instance use), so the `CI` workflow's jobs must pass - the new `Dependency audit` workflow is deliberately **not** matched by that glob, so advisories report without gating merges - `required_approvals: 0`, since requiring one on a single-maintainer repo would deadlock on self-approval
Renovate: auto-merge non-major npm updates once CI is green (#56)
All checks were successful
CI / Alembic migration check (pull_request) Successful in 37s
CI / Python lint & type-check (pull_request) Successful in 57s
CI / Frontend lint, test & build (pull_request) Successful in 1m6s
CI / Python tests (pull_request) Successful in 1m44s
CI / Docker build, health smoke & E2E (pull_request) Successful in 4m56s
065869e86c
renovate.json already auto-merged github-actions minor/patch/digest bumps.
Extends the same treatment to the batched npm (non-major) group.

The safety property is that automerge waits for a green branch, so this cannot
land anything CI rejects -- the eslint bump that just broke lint would have sat
red and unmerged rather than auto-merging. Majors are unaffected: they stay
behind major.dependencyDashboardApproval and are merged by hand.

This is the third checkbox on #56, and the reason it pairs with branch
protection: gating merges on green checks is what makes unattended merging of
routine dependency churn safe. Six weeks of drift produced 5 stale PRs and 6
advisories; this keeps the queue from rotting again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Renovate: quarantine releases for 3 days before auto-merging (#56)
All checks were successful
CI / Alembic migration check (pull_request) Successful in 34s
CI / Python tests (pull_request) Successful in 1m44s
CI / Python lint & type-check (pull_request) Successful in 2m1s
CI / Frontend lint, test & build (pull_request) Successful in 1m58s
CI / Docker build, health smoke & E2E (pull_request) Successful in 5m29s
08e3114297
A security review of the previous commit flagged auto-merge without a
release-age quarantine, and it is right. The justification given for automerge
was that Renovate waits for a green branch -- but green CI is the wrong threat
model here. A hijacked package's postinstall does not fail ruff, mypy, vitest
or Playwright; it just runs.

The exposure is concrete: npm ci executes in the frontend and docker-e2e jobs
on the shared runner, release.yml runs with secrets.GITHUB_TOKEN holding
registry push credentials, and the resulting image is deployed to the dev
server. Without a soak, a compromised minor/patch publish could be picked up
within hours, pass every check, and merge to main unattended.

minimumReleaseAge: "3 days" is applied to BOTH automerge rules, not just npm.
The github-actions rule had the same unguarded automerge and actions execute
directly in the runner, so guarding only npm would have half-closed the hole.

Malicious npm publishes are typically detected and yanked within hours to a
couple of days, so the quarantine removes the window in which we would be the
early adopter. Cost is that patches land three days later, which for this
project is nothing. Majors are unaffected -- still dashboard-gated and merged
by hand.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Author
Contributor

Correction — added a 3-day release quarantine (08e3114)

A security review flagged this PR for enabling auto-merge without a release-age quarantine. It was right, and it exposes a flaw in the justification I gave above.

I argued the change was safe because "Renovate's automerge waits for a green branch, so it cannot land anything CI rejects." That is true, and it is the wrong threat model. Green CI is not a defence against supply-chain compromise — a hijacked package's postinstall does not fail ruff, mypy, vitest or Playwright. It simply runs.

The exposure here is concrete rather than theoretical:

  • npm ci executes in the frontend and docker-e2e jobs on the shared runner
  • release.yml runs with secrets.GITHUB_TOKEN, which holds registry push credentials
  • the resulting image is deployed to the dev server

So a compromised minor/patch publish could have been picked up within hours, passed every check, and merged to main unattended.

Change

minimumReleaseAge: "3 days" on both automerge rules — npm and github-actions. The actions rule had the same unguarded automerge, and actions execute directly in the runner, so guarding only npm would have half-closed the hole.

Verified no automerge rule is left unguarded:

npm (non-major)      automerge=True  minimumReleaseAge=3 days
radix-ui             automerge=None  minimumReleaseAge=None
eslint               automerge=None  minimumReleaseAge=None
github-actions       automerge=True  minimumReleaseAge=3 days

radix-ui and eslint set only groupName, so they inherit both automerge and the quarantine from the npm rule for minor/patch. A radix major matches only the radix rule (which has no matchUpdateTypes), never the non-major rule, so majors stay dashboard-gated and hand-merged.

Malicious npm publishes are typically caught and yanked within hours to a couple of days, so the quarantine removes the window where we would be the early adopter. The cost is patches landing three days later.

## Correction — added a 3-day release quarantine (`08e3114`) A security review flagged this PR for enabling auto-merge without a release-age quarantine. It was right, and it exposes a flaw in the justification I gave above. I argued the change was safe because *"Renovate's automerge waits for a green branch, so it cannot land anything CI rejects."* That is true, and it is the wrong threat model. **Green CI is not a defence against supply-chain compromise** — a hijacked package's `postinstall` does not fail `ruff`, `mypy`, `vitest` or Playwright. It simply runs. The exposure here is concrete rather than theoretical: - `npm ci` executes in the `frontend` and `docker-e2e` jobs on the shared runner - `release.yml` runs with `secrets.GITHUB_TOKEN`, which holds registry push credentials - the resulting image is deployed to the dev server So a compromised minor/patch publish could have been picked up within hours, passed every check, and merged to `main` unattended. ### Change `minimumReleaseAge: "3 days"` on **both** automerge rules — npm *and* github-actions. The actions rule had the same unguarded automerge, and actions execute directly in the runner, so guarding only npm would have half-closed the hole. Verified no automerge rule is left unguarded: ``` npm (non-major) automerge=True minimumReleaseAge=3 days radix-ui automerge=None minimumReleaseAge=None eslint automerge=None minimumReleaseAge=None github-actions automerge=True minimumReleaseAge=3 days ``` `radix-ui` and `eslint` set only `groupName`, so they inherit both `automerge` and the quarantine from the npm rule for minor/patch. A radix **major** matches only the radix rule (which has no `matchUpdateTypes`), never the non-major rule, so majors stay dashboard-gated and hand-merged. Malicious npm publishes are typically caught and yanked within hours to a couple of days, so the quarantine removes the window where we would be the early adopter. The cost is patches landing three days later.
claude-bot deleted branch chore/renovate-automerge-npm 2026-08-31 04:23:50 +00:00
Sign in to join this conversation.
No description provided.