Files
home-network-k3s-impl/decisions/2026-10-18-renovate-cron-window.md
T
jakman85andClaude Opus 5 0d4bd7297d
gate / conformance (pull_request) Has been skipped
gate / gate (pull_request) Successful in 1m55s
fix(renovate): the cron fired outside Renovate's own window, so it would never open a PR
.gitea/workflows/renovate.yaml shipped with `cron: "15 3 * * *"`. Gitea Actions
cron is UTC, and renovate.json carries schedule ["after 3am and before 6am"]
with timezone America/New_York -- and THAT inner schedule is authoritative:
Renovate re-checks it and declines to open PRs outside it.

  03:15 UTC  ->  23:15 EDT, the PREVIOUS EVENING in New York
  window     ->  03:00-06:00 local   =>  NOT INSIDE (3h45m early)

The original comment claimed the cron sat "slightly ahead" of the window. It
sat 3h45m behind it, on the previous day. Every nightly run would have started,
logged "not within schedule", opened ZERO PRs and exited SUCCESSFULLY -- a green
tick forever, indistinguishable from "nothing needed updating", hiding the ~30
pending updates PR #73 existed to surface.

Changed to `15 8 * * *`, chosen to survive DST rather than to look tidy:
  EDT (UTC-4): local 03:00-06:00 == 07:00-10:00 UTC
  EST (UTC-5): local 03:00-06:00 == 08:00-11:00 UTC
08:15 UTC is inside BOTH (04:15 EDT / 03:15 EST), verified by computation at
both extremes, so neither switchover needs an edit. A cron cannot follow DST.

Also confirmed while here: RENOVATE_TOKEN now exists as a repo Actions secret,
so the workflow's fail-hard preflight will pass.

STILL UNRESOLVED and the likelier remaining bug: renovate.json's
`enabledManagers` lists bare `regex`, where modern Renovate names the custom
manager `custom.regex`. If the bare name is ignored, every inline `# renovate:`
marker in the tree does nothing. Deliberately NOT changed -- guessing at a key
that could fail validation and take the whole run down is worse than a question
one dry run answers. See the handoff.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-10-18 03:12:28 -05:00

4.5 KiB
Raw Blame History

date, branch, title
date branch title
2026-10-18 fix/renovate-cron-window The Renovate cron fired outside Renovate's own schedule, so it would never have opened a PR

What changed

.gitea/workflows/renovate.yaml shipped (PR #73) with cron: "15 3 * * *". That schedule could never have produced a single PR.

Gitea Actions cron is UTC. renovate.json carries "schedule": ["after 3am and before 6am"] with "timezone": "America/New_York", and that inner schedule is authoritative — Renovate re-checks it and declines to open PRs outside it. So:

cron 03:15 UTC  ->  23:15 EDT, the PREVIOUS EVENING in New York
window          ->  03:00-06:00 local
inside?         ->  NO (3h45m before the window opens)

The original comment claimed the cron was "slightly ahead of the repo window ... so a run is always in flight when the window opens". It is 3h45m behind, on the previous day. Every nightly run would have started, logged "not within schedule", opened zero PRs and exited successfully — a green tick forever, indistinguishable from "nothing needed updating". That is the same "failure-mode-is-a-green-tick" shape this repo keeps hitting, and it would have hidden the ~30 pending updates PR #73 was written to surface.

Changed to cron: "15 8 * * *", chosen to survive DST rather than to be tidy. The window's UTC position moves:

local window in UTC
EDT (UTC-4, Mar–Nov) 03:00–06:00 07:00–10:00
EST (UTC-5, Nov–Mar) 03:00–06:00 08:00–11:00

08:15 UTC is inside both (04:15 EDT / 03:15 EST), so no edit is needed at either switchover. A cron cannot follow DST, so this is the only way to get it right once.

What I verified

  • The defect, by arithmetic rather than assertion: datetime(2026,10,18,03,15, UTC) converted to America/New_York via zoneinfo is 2026-10-17 23:15 EDT; 3 <= hour < 6 is False.
  • The fix, at both DST extremes: 08:15 UTC → 04:15 EDT in July and 03:15 EST in December, both inside 03:00–06:00. Computed, not reasoned.
  • The inner schedule really is authoritative and really is set: renovate.json on main carries timezone = America/New_York and schedule = ['after 3am and before 6am'].
  • RENOVATE_TOKEN now exists as a repository Actions secret (listed by name via the Gitea API alongside SETTINGS_API_TOKEN), so the workflow's fail-hard preflight will pass. It did not exist when PR #73 was authored.
  • The edited workflow parses as YAML.

What I did NOT verify

  • Renovate has still never executed. This fixes when it runs, not whether it works. No run has happened, so nothing here proves it extracts dependencies, authenticates to Gitea, or opens a usable PR.
  • enabledManagers may silently disable the custom managers — UNRESOLVED and the likelier remaining bug. renovate.json lists enabledManagers: [..., 'regex', ...] and defines customManagers (correct modern key; regexManagers is absent, which is right). But modern Renovate names the custom manager custom.regex in enabledManagers, not bare regex. If the bare name is invalid or ignored, every inline # renovate: marker in the tree does nothing — including the external-snapshotter newTag pin PR #73 flagged as probably inert. I did not change it, because guessing at a config key that could fail validation and take the whole run down is worse than leaving a question that one dry run answers definitively. The dry_run: true dispatch log settles it: look for a per-manager dependency-extraction count, and for any config validation warning naming regex.
  • Whether a dry run inside vs outside the window differs usefully. Renovate honours its schedule even in dry-run, so a manual dispatch outside 03:00–06:00 local will also log "not within schedule". That makes the first dispatch's output less informative than it looks; to see what Renovate would do, either dispatch inside the local window or override the schedule for that run. Not tested either way.
  • No alert fires if the schedule stops firing. PR #73 recorded this gap and it is still open: a Renovate workflow that silently stops running (disabled Actions, expired token, deleted secret) produces no signal at all. Closing it needs a check on last-successful-run age, which nothing does.
  • No conformance test asserts the cron falls inside renovate.json's window, so this exact bug can return. A static check comparing the two is feasible and is not written.