ci(renovate): run every 3 hours and stop rebasing on every base move #55

Merged
ullrich merged 1 commit from ci/renovate-cadence-and-rebase into main 2026-07-30 11:06:01 +00:00
Owner

Hourly Renovate starved CI. Both changes here reduce the load it generates rather than adding capacity.

The evidence

Measured on 2026-07-30 — one run executing, four waiting, three of those PR CI:

id=290  waiting  renovate.yml   (push)
id=289  waiting  ci.yml         (pull_request)
id=286  waiting  ci.yml         (pull_request)
id=284  waiting  ci.yml         (pull_request)
id=281  running  ci.yml         (push)

PR #54's checks sat queued 46 minutes without starting. All four runner containers were healthy — pure saturation. A PR that was substantively green (Checks, Visual Tests, Security Scan, Dockerfile Package Check all passing) could not get a verdict.

The loop was self-reinforcing: waking hourly rebased any PR whose base had moved → each rebase fired a fresh 6-job CI run → with nine open Renovate PRs that produced more work per hour than two slots could absorb. A full Renovate run also holds a slot for ~21 min by itself.

Changes

0 * * * *0 */3 * * * — auto-merge latency stays bounded at ~3h, still far better than the weekly cron that deferred merges indefinitely, at a third of the slot usage. Fires at 00,03,06,09,12,15,18,21 UTC.

rebaseWhen: "behind-base-branch""conflicted" — most rebases happened only because main had moved, not because anything conflicted, so this removes the bulk of the generated runs.

The second change is a trade, and the config says so

behind-base-branch existed to close the stale-base gap left by having no merge queue (#25). Giving it up means a PR can merge having been tested against an older main, so two PRs that each pass can still break main together. CI / * stays required on every PR, which catches everything except genuine semantic conflicts between concurrently-open changes.

If that starts happening, in order of preference: a merge queue (#25), block_on_outdated_branch on the protection rule (which would force every human PR to rebase too), or revert this and add runner capacity.

Worth recording the irony: behind-base-branch also defeated itself. Each rebase restarted CI, so a branch went green after the run that touched it had finished — deferring the very auto-merge it was meant to protect. That was the original bug behind making the cron hourly in the first place.

Verified

  • config parses via json5; rebaseWhen=conflicted with the rest of the auto-merge rule intact (squash, 3 days, at any time)
  • workflow on: block still has exactly one cron plus workflow_dispatch with its dry_run input
  • 0 */3 * * * expands to the eight expected hours
Hourly Renovate starved CI. Both changes here reduce the load it generates rather than adding capacity. ## The evidence Measured on 2026-07-30 — one run executing, four waiting, three of those PR CI: ``` id=290 waiting renovate.yml (push) id=289 waiting ci.yml (pull_request) id=286 waiting ci.yml (pull_request) id=284 waiting ci.yml (pull_request) id=281 running ci.yml (push) ``` **PR #54's checks sat queued 46 minutes without starting.** All four runner containers were healthy — pure saturation. A PR that was substantively green (Checks, Visual Tests, Security Scan, Dockerfile Package Check all passing) could not get a verdict. The loop was self-reinforcing: waking hourly rebased any PR whose base had moved → each rebase fired a fresh **6-job** CI run → with **nine** open Renovate PRs that produced more work per hour than two slots could absorb. A full Renovate run also holds a slot for ~21 min by itself. ## Changes **`0 * * * *` → `0 */3 * * *`** — auto-merge latency stays bounded at ~3h, still far better than the weekly cron that deferred merges indefinitely, at a third of the slot usage. Fires at 00,03,06,09,12,15,18,21 UTC. **`rebaseWhen: "behind-base-branch"` → `"conflicted"`** — most rebases happened only because `main` had moved, not because anything conflicted, so this removes the bulk of the generated runs. ## The second change is a trade, and the config says so `behind-base-branch` existed to close the stale-base gap left by having no merge queue (#25). Giving it up means **a PR can merge having been tested against an older `main`**, so two PRs that each pass can still break `main` together. `CI / *` stays required on every PR, which catches everything except genuine semantic conflicts between concurrently-open changes. If that starts happening, in order of preference: a merge queue (#25), `block_on_outdated_branch` on the protection rule (which would force every human PR to rebase too), or revert this and add runner capacity. Worth recording the irony: `behind-base-branch` also **defeated itself**. Each rebase restarted CI, so a branch went green *after* the run that touched it had finished — deferring the very auto-merge it was meant to protect. That was the original bug behind making the cron hourly in the first place. ## Verified - config parses via `json5`; `rebaseWhen=conflicted` with the rest of the auto-merge rule intact (`squash`, `3 days`, `at any time`) - workflow `on:` block still has exactly one cron plus `workflow_dispatch` with its `dry_run` input - `0 */3 * * *` expands to the eight expected hours
ci(renovate): run every 3 hours and stop rebasing on every base move
Some checks failed
CI / Dockerfile Package Check (pull_request) Successful in 34s
CI / Security Scan (pull_request) Successful in 1m12s
CI / Visual Tests (pull_request) Successful in 6m20s
CI / Checks (pull_request) Successful in 8m14s
CI / Journal Image Smoke Test (pull_request) Successful in 4m44s
CI / E2E Tests (pull_request) Failing after 8m50s
bfc9246c20
Hourly Renovate starved CI. Both changes reduce the load it generates.

Measured on 2026-07-30: one run executing and four waiting, three of those PR
CI. PR #54's checks sat queued for 46 minutes without starting, so a PR that was
substantively green (Checks, Visual Tests, Security Scan, Dockerfile Package
Check all passing) could not get a verdict at all.

The loop was self-reinforcing. Waking hourly rebased any PR whose base had
moved; each rebase fired a fresh 6-job CI run; with nine open Renovate PRs that
produced more work per hour than two runner slots could absorb. A full Renovate
run also holds a slot for ~21 minutes on its own.

Two changes, both aimed at the churn rather than at capacity:

- cron 0 * * * * -> 0 */3 * * *. Auto-merge latency stays bounded at ~3h, still
  far better than the weekly cron that deferred merges indefinitely, at a third
  of the slot usage.
- rebaseWhen "behind-base-branch" -> "conflicted". Most rebases happened only
  because `main` had moved, not because anything conflicted, so this removes the
  bulk of the generated CI runs.

The second one is a trade and the config now says so plainly. "behind-base-branch"
existed to close the stale-base gap left by having no merge queue (#25): a PR can
now merge having been tested against an older `main`, so two PRs that each pass
can still break `main` together. `CI / *` remains required on every PR, which
catches everything except genuine semantic conflicts between concurrently-open
changes. If that starts happening the fixes in order of preference are a merge
queue (#25), `block_on_outdated_branch` on the protection rule (which would force
every human PR to rebase too), or reverting this and adding runner capacity.

Worth noting the irony: "behind-base-branch" also defeated itself. Each rebase
restarted CI, so a branch went green after the run that touched it had already
finished — deferring the very auto-merge it was meant to protect.

Verified: config parses via json5 with rebaseWhen=conflicted and the rest of the
auto-merge rule intact (squash, 3 days, at any time); the workflow `on:` block
still has exactly one cron plus workflow_dispatch with its dry_run input; and
`0 */3 * * *` fires at 00,03,06,09,12,15,18,21 UTC.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X8hNxgYp777FRqYtVmQNaU
ullrich deleted branch ci/renovate-cadence-and-rebase 2026-07-30 11:06:02 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
trails-cool/trails!55
No description provided.