Skip to content

feat(renovate): PR-age cooldown for registries without trusted timestamps - #286

Merged
DevSecNinja merged 1 commit into
mainfrom
feat/renovate-pr-cooldown
Jul 27, 2026
Merged

feat(renovate): PR-age cooldown for registries without trusted timestamps#286
DevSecNinja merged 1 commit into
mainfrom
feat/renovate-pr-cooldown

Conversation

@DevSecNinja

Copy link
Copy Markdown
Owner

Problem

Verified against the latest Renovate run log for DevSecNinja/truenas-apps (Renovate 43.280.0):

Marking N release(s) as pending, as they do not have a releaseTimestamp
  and we're running with minimumReleaseAgeBehaviour=timestamp-required
Branch renovate/ghcr.io-…traefik-forward-auth-4.x creation is disabled
  because internalChecksFilter was not met

Renovate v42+ defaults minimumReleaseAgeBehaviour: timestamp-required and deliberately does not trust the publisher-controlled OCI org.opencontainers.image.created annotation. Images from ghcr.io / lscr.io / quay.io expose no trusted release timestamp, so every release is marked pending and internalChecksFilter: strict refuses to open a branch. The update parks under the dashboard's "Pending Status Checks" section and no PR is ever created30 of 35 branches were blocked this way, incl. traefik-forward-auth (stuck at 4.8.0 since March, ref DevSecNinja/truenas-apps#115).

Neither native option alone is acceptable: timestamp-required never merges these; timestamp-optional opens PRs but skips the soak entirely, contradicting ADR 0004.

Solution — measure the soak from PR age for these registries

  • .renovate/packageRules.json5: minimumReleaseAgeBehaviour: "timestamp-optional" scoped to docker deps on ghcr.io/lscr.io/quay.io → Renovate opens the PR and enables platformAutomerge.
  • .github/workflows/renovate-pr-cooldown.yml (new reusable workflow): posts a required pr-cooldown commit status that stays pending until the PR branch head commit is ≥ 14 days old (a rebase to a newer version restarts the clock). GitHub auto-merge merges the moment it turns green. Human and non-gated PRs always get success, so the required check never blocks anything else.
  • Local caller (renovate-pr-cooldown-caller.yml), workflow-templates/ entry + .properties.json for adoption.
  • ADR 0005 documents the decision; the misleading base.json5 internalChecksFilter comment from fix(renovate): set internalChecksFilter to strict #284 is corrected (strict is the right default for the timestamp-required path, not the fix for the stuck updates).

Datasources with trusted timestamps (docker.io, github tags/releases) keep the native soak and are not gated by pr-cooldown.

Consuming-repo wiring (per ADR 0005)

  1. Add the caller workflow from workflow-templates/renovate-pr-cooldown.yml (triggers: pull_request, schedule, workflow_dispatch).
  2. Add pr-cooldown to the branch ruleset's required status checks.

Validation

actionlint, zizmor (no findings), yamllint, yamlfmt -lint, dprint check all clean (via config-sync/files configs); JSON5 presets parse; embedded github-script node --check + a mocked decision matrix (gated-old→success, gated-new→pending, docker.io→pass, human→pass) all correct.

Caveat

docker.io images that also lack a timestamp (e.g. dozzle/cloudflared/mongo) are intentionally not gated here, per "only the registries we know are unreliable." They can be added to gated-branch-prefixes + the packageRule later if desired.

Refs DevSecNinja/truenas-apps#115

…mestamps

Renovate v42+ defaults `minimumReleaseAgeBehaviour` to `timestamp-required`
and does not trust the publisher-controlled OCI `created` annotation. Images
from ghcr.io / lscr.io / quay.io expose no trusted release timestamp, so every
release is marked "pending" and `internalChecksFilter: strict` refuses to open
a branch — the update sits under the dependency dashboard's "Pending Status
Checks" forever and no PR is ever created (DevSecNinja/truenas-apps#115, e.g.
ghcr.io/italypaleale/traefik-forward-auth stuck for months).

Preserve the ADR 0004 soak by measuring it from PR age instead of release age
for exactly those registries:

- packageRules.json5: set `minimumReleaseAgeBehaviour: "timestamp-optional"`
  for docker deps on ghcr.io/lscr.io/quay.io so Renovate opens the PR and
  enables platformAutomerge.
- New reusable workflow `renovate-pr-cooldown.yml` posts a required
  `pr-cooldown` status check that stays pending until the PR branch head commit
  is >=14 days old (rebasing to a newer version restarts the clock). GitHub
  auto-merge then merges once it turns green. Human and non-gated PRs always
  get a passing status so the required check never blocks them.
- Add local caller + workflow-template + properties for adoption.
- Document the decision in ADR 0005 and correct the misleading base.json5
  `internalChecksFilter` comment (strict is the correct default for the
  timestamp-required path, not the fix for the stuck updates).

Datasources with trusted timestamps (docker.io, github tags/releases) keep the
native `timestamp-required` soak and are not gated by pr-cooldown.

Refs DevSecNinja/truenas-apps#115

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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