From 6627ab293a26e76bb29237a244c9f6c12be95811 Mon Sep 17 00:00:00 2001 From: renovate-bot Date: Sat, 7 Nov 2026 08:20:00 +0000 Subject: [PATCH] chore(deps): migrate config renovate.json5 --- renovate.json5 | 824 ++++++++++++++++++++++--------------------------- 1 file changed, 376 insertions(+), 448 deletions(-) diff --git a/renovate.json5 b/renovate.json5 index 144aff1..f8a68f6 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -1,452 +1,380 @@ -// ================================================================================================= -// Renovate configuration — JSON5, so the rationale can live NEXT TO what it explains. -// ================================================================================================= -// THIS FILE WAS `renovate.json` AND IT WAS INVALID. It carried five `_comment` / `_*_comment` -// pseudo-comment keys, because JSON has no comments. Renovate REJECTS unknown top-level options, so -// the whole config failed validation and the first manual run of the workflow ERRORED OUT. Proven -// with the official validator (`ghcr.io/renovatebot/renovate renovate-config-validator`): -// -// ERROR: Found errors in configuration -// Invalid configuration option: _comment -// Invalid configuration option: _docker_comment -// Invalid configuration option: _ignorePaths_comment -// Invalid configuration option: _kubernetes_comment -// Invalid configuration option: _unmanageable_comment -// -// Renovate discovers `renovate.json5` natively (no RENOVATE_CONFIG_FILE needed, and nothing in -// .gitea/ or ci/ pins the old filename — only prose in comments), and JSON5 permits real `//` -// comments. So the fix keeps every word of the original rationale, positioned, instead of -// flattening it into a `description` string. -// -// VALIDATE BEFORE COMMITTING — it is one command and it is the only thing that catches this class: -// podman run --rm -v "$PWD/renovate.json5:/tmp/renovate.json5:ro,Z" \ -// ghcr.io/renovatebot/renovate renovate-config-validator /tmp/renovate.json5 -// -// CORRECTION (2026-10-29): THE VALIDATOR ABOVE WAS NOT THE RENOVATE THE WORKFLOW RUNS. The bare -// `ghcr.io/renovatebot/renovate` tag is `latest` (44.167.0 when this was measured), while -// renovatebot/github-action@v41.0.9 defaults `renovate-version` to `39` (its action.yml), i.e. -// 39.264.0. Validated against THAT, this file failed with five errors — every -// `managerFilePatterns` key ("Custom Manager contains disallowed fields", "Invalid configuration -// option: flux.managerFilePatterns", ...), because 39.x only knows `fileMatch`; the rename landed -// in 40.2.0. So even after the workflow found this file, every run would have stopped at -// CONFIG_VALIDATION. renovate.yaml now pins the exact release this file is validated against — -// since 2026-10-30 as the job's `container.image` tag (the action is gone: it needed a Docker -// daemon jobs here do not get). VALIDATE WITH THAT SAME TAG: -// podman run --rm -v "$PWD/renovate.json5:/tmp/renovate.json5:ro,Z" \ -// ghcr.io/renovatebot/renovate: \ -// renovate-config-validator /tmp/renovate.json5 -// (Not `--strict`: that also fails on a pending migration of the pre-existing top-level -// `"docker": {"enabled": true}` block at the bottom, which is a warning, not an error.) -// -// `regex` vs `custom.regex` in enabledManagers, RESOLVED FROM SOURCE (Renovate 39.264.0 and -// later): both work. lib/config/migrations/custom/enabled-managers-migration.ts rewrites -// `regex` -> `custom.regex`, and that migration runs on every real repository run -// (lib/workers/repository/init/merge.ts -> migrateAndValidate), not only in the validator; -// lib/modules/manager/index.ts getEnabledManagersList accepts either spelling. `custom.regex` is -// the canonical name (introduced in 37.51.1, renovatebot/renovate#24079), so it is used below and -// nothing depends on the migration any more. -{ "$schema": "https://docs.renovatebot.com/renovate-schema.json", - // Renovate config — Phase 4 automated updates (ADR-0009, R-UPD-01/02). AS OF 2026-10-17 - // RENOVATE OWNS BOTH HALVES: Helm chart/dependency bumps AND per-service container image tags. - // It opens PRs for everything; allowed low-risk classes auto-merge on green CI. The ADR-0009 - // split that gave image tags to Flux image-automation is formally closed — see - // .gitea/workflows/renovate.yaml for the three measured reasons (it pushes commits rather than - // opening PRs, so it bypasses the CLAUDE.md §1 gate; `main` is protected with - // user_can_push:false and the flux-system GitRepository has no secretRef, so it cannot write - // anyway; and enabling it would need an in-cluster git WRITE credential for the repo that - // defines the cluster). image-automation has been commented out at - // platform/kustomization.yaml:52 since it was authored and the cluster holds zero - // ImagePolicy/ImageRepository/ImageUpdateAutomation objects, so nothing is being torn down. - // CONSEQUENCE: the `docker` and `kubernetes` managers, previously disabled ONLY to avoid - // fighting that controller, are now ENABLED — without them every plain `image: repo/name:tag` - // line in this repo was maintained by nothing at all, which is how the oauth2-proxy auth - // sidecars reached 7 minors behind the oauth2-proxy running in the same cluster. RUNNER: - // .gitea/workflows/renovate.yaml (scheduled daily); platform/endpoint are set there, not here. - "extends": ["config:recommended", ":dependencyDashboard", ":semanticCommits", "helpers:pinGitHubActionDigests"], - "timezone": "America/New_York", - "schedule": ["after 3am and before 6am"], - "prConcurrentLimit": 5, - "prHourlyLimit": 2, - "commitMessagePrefix": "chore(deps):", - "labels": ["dependencies", "automated-update"], - "ignoreUnstable": true, - "respectLatest": true, - "enabledManagers": ["flux", "kustomize", "helmv3", "kubernetes", "custom.regex", "github-actions"], - "flux": { - "managerFilePatterns": [ - "/(^|/)platform/.+\\.ya?ml$/", - "/(^|/)apps/.+\\.ya?ml$/" - ] - }, - "kustomize": { - "managerFilePatterns": [ - "/(^|/)kustomization\\.ya?ml$/" - ] - }, - // ADDED 2026-10-17. The `kubernetes` manager reads plain `image:` fields out of Kubernetes - // manifests, and it is NOT auto-detected — it only looks at files matched here, which is why - // this block is as broad as it is. Scope covers every place this repo writes a container image: - // platform components, the rendered app waves, the ServiceDefinition catalog that generates - // them, the runbooks web server and the pm-agent job template. `catalog/` matters as much as - // `apps/`: apps/wave-*/*.yaml are GENERATED by catalog/render.py, so a bump applied only to the - // rendered output is reverted by the next render and would also fail ci/render-golden.sh. - // Renovate will raise BOTH, and both must land together. - "kubernetes": { - "managerFilePatterns": [ - "/(^|/)platform/.+\\.ya?ml$/", - "/(^|/)platform-config/.+\\.ya?ml$/", - "/(^|/)apps/.+\\.ya?ml$/", - "/(^|/)catalog/.+\\.ya?ml$/", - "/(^|/)runbooks/serving/.+\\.ya?ml$/", - "/(^|/)infra/agent/.+\\.ya?ml$/" - ] - }, - "customManagers": [ - { - "customType": "regex", - "description": "Inline `# renovate: datasource=... depName=...` markers on pinned URLs / version literals (e.g. platform/kubevirt/kustomization.yaml release URLs, ci/versions.env, infra/devcontainer/versions.env).", - "managerFilePatterns": [ - "/(^|/)kustomization\\.ya?ml$/", - "/(^|/)versions\\.env$/" - ], - "matchStrings": [ - "#\\s*renovate:\\s*datasource=(?\\S+)\\s+depName=(?\\S+)(?:\\s+versioning=(?\\S+))?\\s*\\n.*?(?v?[0-9]+\\.[0-9]+\\.[0-9]+)" - ], - "versioningTemplate": "{{#if versioning}}{{{versioning}}}{{else}}semver{{/if}}" - }, - // ADDED 2026-10-29 — THIS REPO'S OWN IMAGES (toolbox, agent-runner, conformance-runner). - // CI publishes them (.gitea/workflows/images.yaml) under an immutable `sha-` tag and a - // moving `main` tag, and never writes to git. Every reference to them is DIGEST-ONLY - // (`@sha256:`, no tag), because the admission policies - // (platform/kyverno/admission/pm-agent-job-shape.yaml, conformance-trigger-job-shape.yaml) - // admit exactly that form. A digest alone gives Renovate nothing to follow, so the tag to - // follow rides in a marker comment on the line ABOVE the reference: - // - // # renovate-digest: image=// tag=main - // sha256:<64 hex> - // - // `main` is not a version, so Renovate skips the release lookup and proposes a `digest` - // update whenever the tag's digest moves (lib/workers/repository/process/lookup/index.ts: - // an unversioned currentValue WITH a currentDigest yields updateType `digest`). The - // `sha256:` prefix is INSIDE currentDigest on purpose: Renovate replaces that capture - // verbatim with the new digest, which carries the prefix. - // - // DELIBERATELY NOT TRACKED: infra/agent/worker-job-template.yaml. It is baked INTO - // agent-runner, so bumping it would rebuild agent-runner and loop forever; pm-dispatch - // overwrites that field from PM_WORKER_IMAGE at runtime anyway. - // REGISTRY AUTH: the Gitea registry answers 401 to anonymous reads, and the platform token is - // scoped to hostType `gitea`, which a docker lookup never matches - // (lib/util/host-rules.ts find). renovate.yaml supplies a hostType `docker` rule. - { - "customType": "regex", - "description": "This repo's own CI-published images, pinned by digest, following the tag named in a `# renovate-digest:` marker on the previous line.", - "managerFilePatterns": [ - "/(^|/)versions\\.env$/", - "/(^|/)platform/pm-agent/.+\\.ya?ml$/" - ], - "matchStrings": [ - "#\\s*renovate-digest:\\s*image=(?\\S+)\\s+tag=(?\\S+)\\s*\\n[^\\n]*?(?sha256:[a-f0-9]{64})" - ], - "datasourceTemplate": "docker", - "versioningTemplate": "docker" - }, - // ADDED 2026-10-30 — THE CATALOG IS THE SOURCE; apps/wave-*/*.yaml IS GENERATED FROM IT. - // First real dry run (dispatch runs 353/354): EVERY app-image update edited only the rendered - // apps/wave-*/*.yaml and NONE edited catalog/. The `kubernetes` manager lists catalog/ above - // but extracts nothing there: a ServiceDefinition has no `apiVersion`, so it is not a - // Kubernetes manifest to that manager (measured: 0 deps from catalog/). Every such PR would - // fail ci/render-golden.sh (the canonical render reverts the bump), sit red and hold a - // prConcurrentLimit slot. These two managers read the SOURCES, under the SAME depName as the - // rendered line, so both land in ONE branch and render==golden holds: `source.image` in each - // ServiceDefinition, and render.py's OAUTH2_PROXY_IMAGE (the six auth sidecars). - { - "customType": "regex", - "description": "ServiceDefinition `source.image: :` (catalog/*.yaml) — the source of the rendered apps/wave-* image.", - "managerFilePatterns": ["/(^|/)catalog/[^/]+\\.ya?ml$/"], - "matchStrings": [ - "\\n\\s+image:\\s*(?[^\\s:@#\"']+):(?[^\\s@#\"']+)" - ], - "datasourceTemplate": "docker" - }, - { - "customType": "regex", - "description": "catalog/render.py OAUTH2_PROXY_IMAGE — the per-app oauth2-proxy sidecar every forward-auth app is rendered with.", - "managerFilePatterns": ["/(^|/)catalog/render\\.py$/"], - "matchStrings": [ - "\\nOAUTH2_PROXY_IMAGE\\s*=\\s*\"(?[^\":@]+):(?[^\"@]+)\"" - ], - "datasourceTemplate": "docker" - }, - // ADDED 2026-10-30 — CNPG `Cluster.spec.imageName`. The `kubernetes` manager reads `image:` only, - // so the three database Clusters (platform/keycloak/postgres-cluster.yaml, apps/wave-0/ - // nextcloud-db.yaml, apps/wave-0/semaphore-db.yaml) were untracked, while the grant Job in - // nextcloud-db.yaml — whose comment says "keep the tag in step with spec.imageName" — WAS. A real - // run would have bumped the Job and left the database behind. Same depName, so one branch. - { - "customType": "regex", - "description": "CloudNativePG Cluster `imageName: :`.", - "managerFilePatterns": [ - "/(^|/)platform/.+\\.ya?ml$/", - "/(^|/)apps/.+\\.ya?ml$/" - ], - "matchStrings": [ - "\\n\\s+imageName:\\s*(?[^\\s:@#\"']+):(?[^\\s@#\"']+)" - ], - "datasourceTemplate": "docker" - } +{ + $schema: 'https://docs.renovatebot.com/renovate-schema.json', + extends: [ + 'config:recommended', + ':dependencyDashboard', + ':semanticCommits', + 'helpers:pinGitHubActionDigests', ], - "packageRules": [ - { - "description": "Auto-merge the allowed low-risk classes on green CI \u2014 no human authoring (R-UPD-02). The Gitea merge gate (make -C ci gate) is the required check.", - "matchUpdateTypes": [ - "minor", - "patch", - "digest" - ], - "automerge": true, - "automergeType": "pr", - "platformAutomerge": true - }, - { - "description": "MAJOR / potentially-breaking bumps require human review (docs/11 safety rail). Never auto-merged.", - "matchUpdateTypes": [ - "major" - ], - "automerge": false, - "addLabels": [ - "needs-review", - "breaking" - ] - }, - { - "description": "Keep the KubeVirt + CDI operator pair on a compatible track \u2014 grouped so they bump together (platform/kubevirt/kustomization.yaml).", - "matchDepNames": [ - "kubevirt/kubevirt", - "kubevirt/containerized-data-importer" - ], - "groupName": "kubevirt-cdi" - }, - { - "description": "Pinned tool versions (ci/versions.env, infra/devcontainer/versions.env) are a deliberate change surface \u2014 label but do not auto-merge; a version bump there changes CI reproducibility.", - "matchFileNames": [ - "ci/versions.env", - "infra/devcontainer/versions.env" - ], - "automerge": false, - "addLabels": [ - "ci-tooling" - ] - }, - { - "description": "ADDED 2026-10-17. NEVER AUTO-MERGE THE BLAST-RADIUS COMPONENTS, not even a patch. The default rule above auto-merges minor/patch/digest on a green gate, which is right for an app image: a bad Sonarr bump inconveniences one pod. It is wrong for these five, because the gate is a STATIC check \u2014 it renders manifests and lints them, it does not boot a node, drain a volume or form an etcd quorum (vars.CLUSTER_AVAILABLE is unset, so L2/L3 conformance does not run in CI at all). So for anything whose failure mode is the CLUSTER rather than a service, a green gate is not evidence and a human must read the changelog. kured is here because it reboots control-plane nodes; longhorn because a storage-engine bump migrates live volume data; cilium because it is the CNI and a bad bump is a total partition (and its version is DUAL-PINNED in infra/ansible/roles/cilium/defaults/main.yml, which Renovate does not see, so the two would silently desync); cloudnative-pg because it drives Postgres switchovers; kube-prometheus-stack because ci/versions.env asserts an exact promtool/Prometheus lockstep and a chart bump that moves Prometheus fails the gate until that file moves with it.", - "matchPackageNames": [ - "kured", - "longhorn", - "cilium", - "cloudnative-pg", - "kube-prometheus-stack", - // ADDED 2026-10-30. gitea and act_runner ARE the merge gate: a bad auto-merged bump takes - // down the very check that would have to approve its revert (the dry run proposed gitea - // 1.24.3 -> 1.27.3 and act_runner 0.2.11 -> 0.6.1, both as auto-mergeable "minor"; a 0.x - // minor is a breaking line by semver). The postgresql image restarts every CNPG database, - // Keycloak's (the IdP) included, now that imageName is tracked. - "gitea/gitea", - "gitea/act_runner", - "ghcr.io/cloudnative-pg/postgresql" - ], - "automerge": false, - "addLabels": [ - "needs-review", - "blast-radius" - ] - }, - // ADDED 2026-10-30 — THE KEYCLOAK OPERATOR IS THE KEYCLOAK VERSION. The operator, its CRDs - // and the server image are released in lockstep from keycloak/keycloak-k8s-resources, and the - // Keycloak CR carries no spec.image, so the operator's RELATED_IMAGE_KEYCLOAK picks the server - // (measured on prod 2026-10-30: operator 26.0.6 -> keycloak-0/1 run quay.io/keycloak/keycloak: - // 26.0.6). The three release URLs in platform/keycloak/kustomization.yaml carry `# renovate:` - // markers under this ONE depName, so they move together in one branch (groupName makes that - // explicit). NEVER AUTO-MERGED, not even a patch: Keycloak is the IdP that gates SSO for every - // app AND kubectl OIDC, and platform/keycloak/passkey-reconciler.yaml encodes Keycloak 26.0.6 - // Admin API shapes read from that tag's source (sub-flow rows named by displayName, the - // RpEntityName all-or-nothing guard, requiredActions REPLACE-not-merge, ...). The gate is - // static and does not run that reconciler against a server, so a green check says nothing - // about whether a bump breaks it. Majors still wait on the dashboard (rule at the bottom). - { - "description": "Keycloak operator + CRDs + server (lockstep, one branch). IdP blast radius; passkey-reconciler.yaml encodes 26.0.6 Admin API shapes — re-check it against the new tag before merging. Never auto-merge.", - "matchDepNames": ["keycloak/keycloak-k8s-resources"], - "groupName": "keycloak-operator", - "automerge": false, - "addLabels": ["needs-review", "blast-radius"] - }, - // ADDED 2026-10-30 — quay.io/keycloak/keycloak IS NOT A DEPENDENCY RENOVATE MAY MOVE. Its only - // occurrences are catalog/keycloak.yaml `source.image` and the apps/wave-1/keycloak.yaml it - // renders, which is NOT listed in apps/wave-1/kustomization.yaml and deploys nothing (the - // operator above runs the server). A PR bumping it would change no running pod while looking - // like a Keycloak upgrade, and could drift from the operator's real version. - { - "description": "quay.io/keycloak/keycloak appears only in the documentation-only catalog/keycloak.yaml and its undeployed render; the operator (keycloak/keycloak-k8s-resources) chooses the real server image.", - "matchPackageNames": ["quay.io/keycloak/keycloak"], - "enabled": false - }, - { - "description": "ADDED 2026-10-29. This repo's own images are followed ONLY through the `# renovate-digest:` markers above. The `kubernetes` manager also sees platform/pm-agent/deployment.yaml and infra/agent/worker-job-template.yaml, where the image has a digest and no tag; left on, it would look those up as `latest` (a tag CI never pushes) and, for the template, re-open the self-referential loop the custom manager deliberately avoids.", - "matchManagers": ["kubernetes"], - "matchPackageNames": [ - "gitea.home.keithjackson.dev/home-network/toolbox", - "gitea.home.keithjackson.dev/home-network/agent-runner", - "gitea.home.keithjackson.dev/home-network/conformance-runner" - ], - "enabled": false - }, - { - "description": "ADDED 2026-10-29. Digest bumps for this repo's own images: the image was built and verified by CI before it was published, so these auto-merge on a green gate like any digest (rule 1). Labelled so they are easy to find. NOTE: the conformance-runner bump edits conformance/versions.env, a sensitive path (infra/gitea/approval-policy.json), so it will wait for an administrator's /approve-override regardless of this rule.", - "matchManagers": ["custom.regex"], - "matchPackageNames": [ - "gitea.home.keithjackson.dev/home-network/toolbox", - "gitea.home.keithjackson.dev/home-network/agent-runner", - "gitea.home.keithjackson.dev/home-network/conformance-runner" - ], - "addLabels": ["own-image"] - }, - { - "description": "ADDED 2026-10-30. The `flux` manager also extracts `image:` lines inside HelmRelease values, which the `kubernetes` manager already owns. Measured: exactly two (alpine in platform/oauth2-proxy/helmrelease.yaml, velero-plugin-for-aws in platform/velero/helmrelease.yaml), each extracted twice; the second rewrite of the same line logged `Cannot find replaceString in current file content` in runs 353/354. Flux keeps its charts (datasource helm).", - "matchManagers": ["flux"], - "matchDatasources": ["docker"], - "enabled": false - }, - { - "description": "ADDED 2026-10-30. MIRRORS catalog/.yaml `update.range` — the operator's declared per-service policy (schemas/servicedefinition.schema.json), which Renovate does not read. Without it the dry run proposed gitea 28.2.0, qbittorrent 20.04.1 (both stale/odd tags, not real successors), radarr 6.x and nextcloud 35 (four majors past 31; Nextcloud cannot skip majors). KEEP IN STEP with the catalog ranges.", - "matchPackageNames": ["gitea/gitea"], - "allowedVersions": "<2.0.0" - }, - { "matchPackageNames": ["nextcloud"], "allowedVersions": "<32.0.0" }, - { "matchPackageNames": ["lscr.io/linuxserver/qbittorrent"], "allowedVersions": "<6.0.0" }, - { "matchPackageNames": ["lscr.io/linuxserver/radarr"], "allowedVersions": "<6.0.0" }, - { "matchPackageNames": ["lscr.io/linuxserver/sonarr"], "allowedVersions": "<5.0.0" }, - { "matchPackageNames": ["semaphoreui/semaphore"], "allowedVersions": "<3.0.0" }, - // ADDED 2026-10-30 — pihole, jackett and plex, PINNED to the digests that were already running - // (catalog/{pihole,jackett,plex}.yaml; each tag was resolved against the registry and matched the - // pods' imageID, so the pin changed nothing at runtime). None of the three catalog `update.range` - // values has a ceiling (pihole >=2024.0.0, jackett >=0.22.0, plex >=1.40.0), so the - // allowedVersions mirror above needs no entry for them. - // - // pihole/pihole needs NO rule: its CalVer tags (YYYY.MM.N, e.g. 2026.10.0) parse under the - // default `docker` versioning. Patches and minors (2026.10.0 -> 2026.10.1 / 2026.11.0) therefore - // AUTO-MERGE on a green gate under rule 1 — the operator's explicit choice, not an oversight; do - // not add a hold here. A YEAR ROLLOVER (2026.x -> 2027.x) is a MAJOR, so it waits for a tick on - // the Dependency Dashboard (rule below) and is never auto-merged. - { - "description": "ADDED 2026-10-30. LinuxServer tags: `vX.Y.Z-lsN` is the IMMUTABLE build tag (bare `X.Y.Z` and `latest` are re-pushed for every ls rebuild). Docker versioning would treat `-ls7` as a compatibility suffix and only ever compare tags with the SAME suffix, so it would never move. This is Renovate's own regex-versioning example for linuxserver images: the ls build number becomes the `build` component, so an ls rebuild of the same Jackett is an update too. The `^v` and `$` anchors exclude `latest`, `development`, the bare mutable `X.Y.Z` tags and the arch-prefixed (`amd64-v...`) tags.", - "matchDepNames": ["lscr.io/linuxserver/jackett"], - "versioning": "regex:^v(?\\d+)\\.(?\\d+)\\.(?\\d+)-ls(?\\d+)$" - }, - { - "description": "ADDED 2026-10-30. LOOK JACKETT UP ON ghcr.io, NOT lscr.io. lscr.io is LinuxServer's redirector in front of ghcr.io/linuxserver (same manifests: v0.24.2865-ls7 is sha256:4fcf3cc2... on both). Renovate's docker datasource fetches up to 1000 pages of tags from ghcr.io but only `dockerMaxPages` (default 20, a GLOBAL-only option) from any other host (lib/modules/datasource/docker/index.js getTags). Jackett has 32,093 tags at ~1000 per page (measured 2026-10-30), oldest first; the running tag is #31,980, so a lookup via lscr.io never sees it or anything newer and silently proposes NOTHING (measured in a dry run before this rule). overridePackageName changes only the lookup; the file keeps `lscr.io/...`, so the deployed image string does not change. Matched by depName because packageName is what this rule rewrites.", - "matchDepNames": ["lscr.io/linuxserver/jackett"], - "overridePackageName": "ghcr.io/linuxserver/jackett" - }, - { - "description": "ADDED 2026-10-30. Jackett publishes ~daily (13 ls builds in 12 days, measured 2026-10-30) and every one is a `patch` under the regex above, i.e. auto-mergeable. Left on the global daily window that is a PR, a gate run and a jackett pod restart EVERY DAY for an indexer proxy whose individual builds nobody needs promptly. WEEKLY instead: Renovate proposes only the newest tag when the window opens, so nothing is skipped, it is just batched. The window is still inside the global 03:00-06:00 local one that .gitea/workflows/renovate.yaml's 08:15 UTC cron lands in. NOTE: catalog/jackett.yaml `update.cadence` still says daily (`0 4 * * *`); that field drove Flux image-automation, which is retired, and this rule is the effective cadence now.", - "matchDepNames": ["lscr.io/linuxserver/jackett"], - "schedule": ["after 3am and before 6am on sunday"] - }, - { - "description": "ADDED 2026-10-30. Plex tags are `...-` (e.g. 1.43.4.10903-e5521bd8c). Docker versioning reads `-e5521bd8c` as a compatibility suffix that differs on EVERY release, so it would never find a successor. The trailing `$` excludes the per-arch variants and the floating `latest`/`public`/`beta`/`plexpass` tags. A version tag does NOT self-update inside the container: cont-init 50-plex-update exits early when /version.txt matches the installed package, so what is pinned is what runs.", - "matchPackageNames": ["plexinc/pms-docker"], - "versioning": "regex:^(?\\d+)\\.(?\\d+)\\.(?\\d+)\\.(?\\d+)-[0-9a-f]+$" - }, - { - "description": "ADDED 2026-10-30. A CNPG Postgres MAJOR is a migration (new cluster + import), not an image bump; the dry run proposed 16.4 -> 18.6. Minors stay (and are blast-radius, above).", - "matchPackageNames": ["ghcr.io/cloudnative-pg/postgresql"], - "allowedVersions": "<17.0.0" - }, - { - "description": "ADDED 2026-10-30. A HelmRelease semver RANGE is a policy ceiling, and Renovate does not respect it — it WIDENS it (dry run: external-dns `<1.22.0` -> `<1.24.0`, headlamp `<0.42.0` -> `<0.47.0`), and the default rule would then auto-merge that as a 'minor', silently letting Flux adopt every new minor. Raising a ceiling is deliberate per-component work: propose it, never auto-merge it.", - "matchManagers": ["flux"], - "matchCurrentValue": "/^[<>=~^]|\\.x$/", - "automerge": false, - "addLabels": ["needs-review", "range-widen"] - }, - { - "description": "ADDED 2026-10-30. velero-plugin-for-aws is pinned to a RELEASE CANDIDATE (v1.14.3-rc.2) and Renovate proposed NOTHING for it, although v1.14.3 and v1.14.4 are stable on Docker Hub. Docker versioning treats everything after the first `-` as a compatibility SUFFIX, so only other `*-rc.2` tags qualify and the RC is a fossil forever. Semver versioning reads it as a prerelease, and a prerelease upgrades to the stable release.", - "matchPackageNames": ["velero/velero-plugin-for-aws"], - "versioning": "semver" - }, - { - "description": "ADDED 2026-10-30. A python MINOR is a language-runtime release (the dry run proposed 3.13 -> 3.15 for the Keycloak reconcilers), and the gate does not execute those scripts. Review it; patches still auto-merge.", - "matchPackageNames": ["python"], - "matchUpdateTypes": ["minor"], - "automerge": false, - "addLabels": ["needs-review"] - }, - // OPERATOR DECISION 2026-10-30: these two never auto-merge, at ANY update type. Both sit after - // the auto-merge rule (rule 1) so they override it; `needs-review` is added, not replaced. - { - "description": "ADDED 2026-10-30 (operator decision). The oauth2-proxy image, under the ONE depName every source of it emits: catalog/render.py OAUTH2_PROXY_IMAGE (custom regex manager), the rendered apps/wave-* sidecars and platform/crafty/deployment.yaml (kubernetes manager). WHY: since v7.13.0 oauth2-proxy validates REFRESHED OIDC sessions against the ACCESS token instead of the id_token (upstream CHANGELOG, v7.13.0), every sidecar sets --cookie-refresh, and that refresh path has never run against this Keycloak (the platform oauth2-proxy runs v7.15.5 WITHOUT --cookie-refresh). render-golden and render.py --selftest pass on the bump, but both check rendered text, not the binary, so a green gate is not evidence here.", - "matchPackageNames": ["quay.io/oauth2-proxy/oauth2-proxy"], - "automerge": false, - "addLabels": ["needs-review"] - }, - { - "description": "ADDED 2026-10-30 (operator decision). Renovate's OWN version: the job `container.image` pin in .gitea/workflows/renovate.yaml (ghcr.io/renovatebot/renovate), and renovatebot/github-action should it ever be reintroduced (ci/lint.sh rejects it today). WHY: nothing re-validates renovate.json5 against the NEW version before it takes effect. The gate does not run renovate-config-validator, and this exact gap has bitten before: the action's default 39.x rejected this file (managerFilePatterns needs >= 40.2.0) and every run stopped at CONFIG_VALIDATION. Run the validator from the new tag, then merge.", - "matchPackageNames": ["ghcr.io/renovatebot/renovate", "renovatebot/github-action"], - "automerge": false, - "addLabels": ["needs-review"] - }, - { - "description": "ADDED 2026-10-30. MAJORS WAIT FOR A TICK ON THE DEPENDENCY DASHBOARD instead of opening a PR. They are never auto-merged anyway, and with prConcurrentLimit 5 the dry run's 10 majors would hold every PR slot open indefinitely, starving the patch/minor/digest PRs that can merge on green.", - "matchUpdateTypes": ["major"], - "dependencyDashboardApproval": true - } + timezone: 'America/New_York', + schedule: [ + 'after 3am and before 6am', ], - // WHAT RENOVATE STILL CANNOT FIX, so nobody mistakes a working bot for full coverage. A - // FLOATING tag has no version to compare, so Renovate skips it silently — it does not pin it - // and does not warn. As of 2026-10-17 that is: plexinc/pms-docker:latest, - // lscr.io/linuxserver/jackett:latest, pihole/pihole (NO TAG AT ALL), quay.io/keycloak/keycloak - // (no tag), nextcloud:31-apache and docker:27-dind. Those must be pinned BY HAND to a concrete - // version before any automation can maintain them, and until then they drift on every pull with - // nothing recording what is running. CORRECTED 2026-10-30: nextcloud:31-apache and docker:27-dind - // ARE maintained (the first dry run proposed 35-apache and 29-dind; the suffix is kept). - // CORRECTED 2026-10-30: quay.io/keycloak/keycloak was never a running reference — the operator - // picks the server image — and the Keycloak version is now tracked via the operator URLs. - // CORRECTED 2026-10-30 (feat/pin-pihole-jackett-plex): pms-docker, jackett and pihole are now - // PINNED (1.43.4.10903-e5521bd8c, v0.24.2865-ls7, 2026.10.0 — the digests that were running) and - // maintained via the versioning rules above. None of the three publishes "only latest": all three - // publish immutable version tags, they were simply never referenced. - // Separately, the HelmRelease minor-locking ranges (9 of 12, e.g. `>=88.0.0 <89.0.0` on - // kube-prometheus-stack): THIS FILE USED TO SAY they cap Renovate at patches. They do not — - // Renovate WIDENS the range (runs 353/354). The `range-widen` rule above keeps that a reviewed - // PR rather than an auto-merge. Both lists are in decisions/2026-10-17-automated-upgrades.md. - // platform/kyverno/tests/** ADDED 2026-10-17: those Kyverno policy fixtures pin `alpine:latest` - // and a `conformance-runner:latest`, which are test inputs whose whole point is to be a fixed - // shape, not software to keep current. `conformance/**` was already ignored for the same - // reason; this closes the matching hole that enabling the `kubernetes` manager would otherwise - // have opened. - // conformance/** NARROWED 2026-10-29 to its subdirectories: ignorePaths applies to custom - // managers too (lib/workers/repository/extract/file-match.ts getMatchingFiles -> - // filterIgnoredFiles), so the blanket glob also hid conformance/versions.env, which now carries - // the conformance-runner digest Renovate must maintain. Everything else under conformance/ - // stays ignored, for the original reason (test fixtures with deliberately fixed shapes). - "ignorePaths": [ - "**/*.example", - "**/*.sops.yaml", - "conformance/config/**", - "conformance/incluster/**", - "conformance/lib/**", - "conformance/requirements/**", - "conformance/runs/**", - "conformance/tests/**", - "platform/image-automation/per-service/**", - "platform/kyverno/tests/**" + prConcurrentLimit: 5, + prHourlyLimit: 2, + commitMessagePrefix: 'chore(deps):', + labels: [ + 'dependencies', + 'automated-update', + ], + ignoreUnstable: true, + respectLatest: true, + enabledManagers: [ + 'flux', + 'kustomize', + 'helmv3', + 'kubernetes', + 'custom.regex', + 'github-actions', + ], + flux: { + managerFilePatterns: [ + '/(^|/)platform/.+\\.ya?ml$/', + '/(^|/)apps/.+\\.ya?ml$/', + ], + }, + kustomize: { + managerFilePatterns: [ + '/(^|/)kustomization\\.ya?ml$/', + ], + }, + kubernetes: { + managerFilePatterns: [ + '/(^|/)platform/.+\\.ya?ml$/', + '/(^|/)platform-config/.+\\.ya?ml$/', + '/(^|/)apps/.+\\.ya?ml$/', + '/(^|/)catalog/.+\\.ya?ml$/', + '/(^|/)runbooks/serving/.+\\.ya?ml$/', + '/(^|/)infra/agent/.+\\.ya?ml$/', + ], + }, + customManagers: [ + { + customType: 'regex', + description: 'Inline `# renovate: datasource=... depName=...` markers on pinned URLs / version literals (e.g. platform/kubevirt/kustomization.yaml release URLs, ci/versions.env, infra/devcontainer/versions.env).', + managerFilePatterns: [ + '/(^|/)kustomization\\.ya?ml$/', + '/(^|/)versions\\.env$/', + ], + matchStrings: [ + '#\\s*renovate:\\s*datasource=(?\\S+)\\s+depName=(?\\S+)(?:\\s+versioning=(?\\S+))?\\s*\\n.*?(?v?[0-9]+\\.[0-9]+\\.[0-9]+)', + ], + versioningTemplate: '{{#if versioning}}{{{versioning}}}{{else}}semver{{/if}}', + }, + { + customType: 'regex', + description: "This repo's own CI-published images, pinned by digest, following the tag named in a `# renovate-digest:` marker on the previous line.", + managerFilePatterns: [ + '/(^|/)versions\\.env$/', + '/(^|/)platform/pm-agent/.+\\.ya?ml$/', + ], + matchStrings: [ + '#\\s*renovate-digest:\\s*image=(?\\S+)\\s+tag=(?\\S+)\\s*\\n[^\\n]*?(?sha256:[a-f0-9]{64})', + ], + datasourceTemplate: 'docker', + versioningTemplate: 'docker', + }, + { + customType: 'regex', + description: 'ServiceDefinition `source.image: :` (catalog/*.yaml) — the source of the rendered apps/wave-* image.', + managerFilePatterns: [ + '/(^|/)catalog/[^/]+\\.ya?ml$/', + ], + matchStrings: [ + '\\n\\s+image:\\s*(?[^\\s:@#"\']+):(?[^\\s@#"\']+)', + ], + datasourceTemplate: 'docker', + }, + { + customType: 'regex', + description: 'catalog/render.py OAUTH2_PROXY_IMAGE — the per-app oauth2-proxy sidecar every forward-auth app is rendered with.', + managerFilePatterns: [ + '/(^|/)catalog/render\\.py$/', + ], + matchStrings: [ + '\\nOAUTH2_PROXY_IMAGE\\s*=\\s*"(?[^":@]+):(?[^"@]+)"', + ], + datasourceTemplate: 'docker', + }, + { + customType: 'regex', + description: 'CloudNativePG Cluster `imageName: :`.', + managerFilePatterns: [ + '/(^|/)platform/.+\\.ya?ml$/', + '/(^|/)apps/.+\\.ya?ml$/', + ], + matchStrings: [ + '\\n\\s+imageName:\\s*(?[^\\s:@#"\']+):(?[^\\s@#"\']+)', + ], + datasourceTemplate: 'docker', + }, + ], + packageRules: [ + { + matchCategories: [ + 'docker', + ], + enabled: true, + }, + { + description: 'Auto-merge the allowed low-risk classes on green CI — no human authoring (R-UPD-02). The Gitea merge gate (make -C ci gate) is the required check.', + matchUpdateTypes: [ + 'minor', + 'patch', + 'digest', + ], + automerge: true, + automergeType: 'pr', + platformAutomerge: true, + }, + { + description: 'MAJOR / potentially-breaking bumps require human review (docs/11 safety rail). Never auto-merged.', + matchUpdateTypes: [ + 'major', + ], + automerge: false, + addLabels: [ + 'needs-review', + 'breaking', + ], + }, + { + description: 'Keep the KubeVirt + CDI operator pair on a compatible track — grouped so they bump together (platform/kubevirt/kustomization.yaml).', + matchDepNames: [ + 'kubevirt/kubevirt', + 'kubevirt/containerized-data-importer', + ], + groupName: 'kubevirt-cdi', + }, + { + description: 'Pinned tool versions (ci/versions.env, infra/devcontainer/versions.env) are a deliberate change surface — label but do not auto-merge; a version bump there changes CI reproducibility.', + matchFileNames: [ + 'ci/versions.env', + 'infra/devcontainer/versions.env', + ], + automerge: false, + addLabels: [ + 'ci-tooling', + ], + }, + { + description: 'ADDED 2026-10-17. NEVER AUTO-MERGE THE BLAST-RADIUS COMPONENTS, not even a patch. The default rule above auto-merges minor/patch/digest on a green gate, which is right for an app image: a bad Sonarr bump inconveniences one pod. It is wrong for these five, because the gate is a STATIC check — it renders manifests and lints them, it does not boot a node, drain a volume or form an etcd quorum (vars.CLUSTER_AVAILABLE is unset, so L2/L3 conformance does not run in CI at all). So for anything whose failure mode is the CLUSTER rather than a service, a green gate is not evidence and a human must read the changelog. kured is here because it reboots control-plane nodes; longhorn because a storage-engine bump migrates live volume data; cilium because it is the CNI and a bad bump is a total partition (and its version is DUAL-PINNED in infra/ansible/roles/cilium/defaults/main.yml, which Renovate does not see, so the two would silently desync); cloudnative-pg because it drives Postgres switchovers; kube-prometheus-stack because ci/versions.env asserts an exact promtool/Prometheus lockstep and a chart bump that moves Prometheus fails the gate until that file moves with it.', + matchPackageNames: [ + 'kured', + 'longhorn', + 'cilium', + 'cloudnative-pg', + 'kube-prometheus-stack', + 'gitea/gitea', + 'gitea/act_runner', + 'ghcr.io/cloudnative-pg/postgresql', + ], + automerge: false, + addLabels: [ + 'needs-review', + 'blast-radius', + ], + }, + { + description: 'Keycloak operator + CRDs + server (lockstep, one branch). IdP blast radius; passkey-reconciler.yaml encodes 26.0.6 Admin API shapes — re-check it against the new tag before merging. Never auto-merge.', + matchDepNames: [ + 'keycloak/keycloak-k8s-resources', + ], + groupName: 'keycloak-operator', + automerge: false, + addLabels: [ + 'needs-review', + 'blast-radius', + ], + }, + { + description: 'quay.io/keycloak/keycloak appears only in the documentation-only catalog/keycloak.yaml and its undeployed render; the operator (keycloak/keycloak-k8s-resources) chooses the real server image.', + matchPackageNames: [ + 'quay.io/keycloak/keycloak', + ], + enabled: false, + }, + { + description: "ADDED 2026-10-29. This repo's own images are followed ONLY through the `# renovate-digest:` markers above. The `kubernetes` manager also sees platform/pm-agent/deployment.yaml and infra/agent/worker-job-template.yaml, where the image has a digest and no tag; left on, it would look those up as `latest` (a tag CI never pushes) and, for the template, re-open the self-referential loop the custom manager deliberately avoids.", + matchManagers: [ + 'kubernetes', + ], + matchPackageNames: [ + 'gitea.home.keithjackson.dev/home-network/toolbox', + 'gitea.home.keithjackson.dev/home-network/agent-runner', + 'gitea.home.keithjackson.dev/home-network/conformance-runner', + ], + enabled: false, + }, + { + description: "ADDED 2026-10-29. Digest bumps for this repo's own images: the image was built and verified by CI before it was published, so these auto-merge on a green gate like any digest (rule 1). Labelled so they are easy to find. NOTE: the conformance-runner bump edits conformance/versions.env, a sensitive path (infra/gitea/approval-policy.json), so it will wait for an administrator's /approve-override regardless of this rule.", + matchManagers: [ + 'custom.regex', + ], + matchPackageNames: [ + 'gitea.home.keithjackson.dev/home-network/toolbox', + 'gitea.home.keithjackson.dev/home-network/agent-runner', + 'gitea.home.keithjackson.dev/home-network/conformance-runner', + ], + addLabels: [ + 'own-image', + ], + }, + { + description: 'ADDED 2026-10-30. The `flux` manager also extracts `image:` lines inside HelmRelease values, which the `kubernetes` manager already owns. Measured: exactly two (alpine in platform/oauth2-proxy/helmrelease.yaml, velero-plugin-for-aws in platform/velero/helmrelease.yaml), each extracted twice; the second rewrite of the same line logged `Cannot find replaceString in current file content` in runs 353/354. Flux keeps its charts (datasource helm).', + matchManagers: [ + 'flux', + ], + matchDatasources: [ + 'docker', + ], + enabled: false, + }, + { + description: "ADDED 2026-10-30. MIRRORS catalog/.yaml `update.range` — the operator's declared per-service policy (schemas/servicedefinition.schema.json), which Renovate does not read. Without it the dry run proposed gitea 28.2.0, qbittorrent 20.04.1 (both stale/odd tags, not real successors), radarr 6.x and nextcloud 35 (four majors past 31; Nextcloud cannot skip majors). KEEP IN STEP with the catalog ranges.", + matchPackageNames: [ + 'gitea/gitea', + ], + allowedVersions: '<2.0.0', + }, + { + matchPackageNames: [ + 'nextcloud', + ], + allowedVersions: '<32.0.0', + }, + { + matchPackageNames: [ + 'lscr.io/linuxserver/qbittorrent', + ], + allowedVersions: '<6.0.0', + }, + { + matchPackageNames: [ + 'lscr.io/linuxserver/radarr', + ], + allowedVersions: '<6.0.0', + }, + { + matchPackageNames: [ + 'lscr.io/linuxserver/sonarr', + ], + allowedVersions: '<5.0.0', + }, + { + matchPackageNames: [ + 'semaphoreui/semaphore', + ], + allowedVersions: '<3.0.0', + }, + { + description: "ADDED 2026-10-30. LinuxServer tags: `vX.Y.Z-lsN` is the IMMUTABLE build tag (bare `X.Y.Z` and `latest` are re-pushed for every ls rebuild). Docker versioning would treat `-ls7` as a compatibility suffix and only ever compare tags with the SAME suffix, so it would never move. This is Renovate's own regex-versioning example for linuxserver images: the ls build number becomes the `build` component, so an ls rebuild of the same Jackett is an update too. The `^v` and `$` anchors exclude `latest`, `development`, the bare mutable `X.Y.Z` tags and the arch-prefixed (`amd64-v...`) tags.", + matchDepNames: [ + 'lscr.io/linuxserver/jackett', + ], + versioning: 'regex:^v(?\\d+)\\.(?\\d+)\\.(?\\d+)-ls(?\\d+)$', + }, + { + description: "ADDED 2026-10-30. LOOK JACKETT UP ON ghcr.io, NOT lscr.io. lscr.io is LinuxServer's redirector in front of ghcr.io/linuxserver (same manifests: v0.24.2865-ls7 is sha256:4fcf3cc2... on both). Renovate's docker datasource fetches up to 1000 pages of tags from ghcr.io but only `dockerMaxPages` (default 20, a GLOBAL-only option) from any other host (lib/modules/datasource/docker/index.js getTags). Jackett has 32,093 tags at ~1000 per page (measured 2026-10-30), oldest first; the running tag is #31,980, so a lookup via lscr.io never sees it or anything newer and silently proposes NOTHING (measured in a dry run before this rule). overridePackageName changes only the lookup; the file keeps `lscr.io/...`, so the deployed image string does not change. Matched by depName because packageName is what this rule rewrites.", + matchDepNames: [ + 'lscr.io/linuxserver/jackett', + ], + overridePackageName: 'ghcr.io/linuxserver/jackett', + }, + { + description: "ADDED 2026-10-30. Jackett publishes ~daily (13 ls builds in 12 days, measured 2026-10-30) and every one is a `patch` under the regex above, i.e. auto-mergeable. Left on the global daily window that is a PR, a gate run and a jackett pod restart EVERY DAY for an indexer proxy whose individual builds nobody needs promptly. WEEKLY instead: Renovate proposes only the newest tag when the window opens, so nothing is skipped, it is just batched. The window is still inside the global 03:00-06:00 local one that .gitea/workflows/renovate.yaml's 08:15 UTC cron lands in. NOTE: catalog/jackett.yaml `update.cadence` still says daily (`0 4 * * *`); that field drove Flux image-automation, which is retired, and this rule is the effective cadence now.", + matchDepNames: [ + 'lscr.io/linuxserver/jackett', + ], + schedule: [ + 'after 3am and before 6am on sunday', + ], + }, + { + description: 'ADDED 2026-10-30. Plex tags are `...-` (e.g. 1.43.4.10903-e5521bd8c). Docker versioning reads `-e5521bd8c` as a compatibility suffix that differs on EVERY release, so it would never find a successor. The trailing `$` excludes the per-arch variants and the floating `latest`/`public`/`beta`/`plexpass` tags. A version tag does NOT self-update inside the container: cont-init 50-plex-update exits early when /version.txt matches the installed package, so what is pinned is what runs.', + matchPackageNames: [ + 'plexinc/pms-docker', + ], + versioning: 'regex:^(?\\d+)\\.(?\\d+)\\.(?\\d+)\\.(?\\d+)-[0-9a-f]+$', + }, + { + description: 'ADDED 2026-10-30. A CNPG Postgres MAJOR is a migration (new cluster + import), not an image bump; the dry run proposed 16.4 -> 18.6. Minors stay (and are blast-radius, above).', + matchPackageNames: [ + 'ghcr.io/cloudnative-pg/postgresql', + ], + allowedVersions: '<17.0.0', + }, + { + description: "ADDED 2026-10-30. A HelmRelease semver RANGE is a policy ceiling, and Renovate does not respect it — it WIDENS it (dry run: external-dns `<1.22.0` -> `<1.24.0`, headlamp `<0.42.0` -> `<0.47.0`), and the default rule would then auto-merge that as a 'minor', silently letting Flux adopt every new minor. Raising a ceiling is deliberate per-component work: propose it, never auto-merge it.", + matchManagers: [ + 'flux', + ], + matchCurrentValue: '/^[<>=~^]|\\.x$/', + automerge: false, + addLabels: [ + 'needs-review', + 'range-widen', + ], + }, + { + description: 'ADDED 2026-10-30. velero-plugin-for-aws is pinned to a RELEASE CANDIDATE (v1.14.3-rc.2) and Renovate proposed NOTHING for it, although v1.14.3 and v1.14.4 are stable on Docker Hub. Docker versioning treats everything after the first `-` as a compatibility SUFFIX, so only other `*-rc.2` tags qualify and the RC is a fossil forever. Semver versioning reads it as a prerelease, and a prerelease upgrades to the stable release.', + matchPackageNames: [ + 'velero/velero-plugin-for-aws', + ], + versioning: 'semver', + }, + { + description: 'ADDED 2026-10-30. A python MINOR is a language-runtime release (the dry run proposed 3.13 -> 3.15 for the Keycloak reconcilers), and the gate does not execute those scripts. Review it; patches still auto-merge.', + matchPackageNames: [ + 'python', + ], + matchUpdateTypes: [ + 'minor', + ], + automerge: false, + addLabels: [ + 'needs-review', + ], + }, + { + description: 'ADDED 2026-10-30 (operator decision). The oauth2-proxy image, under the ONE depName every source of it emits: catalog/render.py OAUTH2_PROXY_IMAGE (custom regex manager), the rendered apps/wave-* sidecars and platform/crafty/deployment.yaml (kubernetes manager). WHY: since v7.13.0 oauth2-proxy validates REFRESHED OIDC sessions against the ACCESS token instead of the id_token (upstream CHANGELOG, v7.13.0), every sidecar sets --cookie-refresh, and that refresh path has never run against this Keycloak (the platform oauth2-proxy runs v7.15.5 WITHOUT --cookie-refresh). render-golden and render.py --selftest pass on the bump, but both check rendered text, not the binary, so a green gate is not evidence here.', + matchPackageNames: [ + 'quay.io/oauth2-proxy/oauth2-proxy', + ], + automerge: false, + addLabels: [ + 'needs-review', + ], + }, + { + description: "ADDED 2026-10-30 (operator decision). Renovate's OWN version: the job `container.image` pin in .gitea/workflows/renovate.yaml (ghcr.io/renovatebot/renovate), and renovatebot/github-action should it ever be reintroduced (ci/lint.sh rejects it today). WHY: nothing re-validates renovate.json5 against the NEW version before it takes effect. The gate does not run renovate-config-validator, and this exact gap has bitten before: the action's default 39.x rejected this file (managerFilePatterns needs >= 40.2.0) and every run stopped at CONFIG_VALIDATION. Run the validator from the new tag, then merge.", + matchPackageNames: [ + 'ghcr.io/renovatebot/renovate', + 'renovatebot/github-action', + ], + automerge: false, + addLabels: [ + 'needs-review', + ], + }, + { + description: "ADDED 2026-10-30. MAJORS WAIT FOR A TICK ON THE DEPENDENCY DASHBOARD instead of opening a PR. They are never auto-merged anyway, and with prConcurrentLimit 5 the dry run's 10 majors would hold every PR slot open indefinitely, starving the patch/minor/digest PRs that can merge on green.", + matchUpdateTypes: [ + 'major', + ], + dependencyDashboardApproval: true, + }, + ], + ignorePaths: [ + '**/*.example', + '**/*.sops.yaml', + 'conformance/config/**', + 'conformance/incluster/**', + 'conformance/lib/**', + 'conformance/requirements/**', + 'conformance/runs/**', + 'conformance/tests/**', + 'platform/image-automation/per-service/**', + 'platform/kyverno/tests/**', ], - // The `docker` manager (Dockerfiles, docker-compose) and the `kubernetes` manager above were - // BOTH previously `enabled: false`, solely so Renovate would not fight Flux image-automation - // over a running tag. That controller was never enabled and never will be here (see _comment), - // so the only thing those two flags were achieving was leaving every plain `image:` tag in the - // repo unmanaged. `docker` is left out of enabledManagers because this repo contains no - // Dockerfile that ships to the cluster; `kubernetes` is now ON and is the one that matters. - "docker": { - "enabled": true - } }