chore(renovate): group the freesailarr stack into one PR per cycle
Validate Helm Charts / Detect changed charts (pull_request) Successful in 6s
Validate Helm Charts / Resource quantities (pull_request) Successful in 30s
Validate Helm Charts / Selector match (pull_request) Successful in 17s
Validate Helm Charts / HTTPRoute match (pull_request) Successful in 14s
Validate Helm Charts / Build freesailarr (pull_request) Successful in 7s
Validate Helm Charts / Trivy config freesailarr (pull_request) Successful in 24s
Install-test / Install-test freesailarr (pull_request) Successful in 4m9s
Validate Helm Charts / Install-test (pull_request) Successful in 4m9s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 12s
Publish Freesailarr Helm Chart / Publish Freesailarr Helm Chart (push) Successful in 12s
SonarQube Analysis / SonarQube Analysis (push) Successful in 50s

The first groupName in this config, and a deliberate reversal for one chart
of the stance the file records above its app-version managers: that the
helm-values manager finds freesailarr's ten images and proposes one update
per image. That described what Renovate does, not what the chart wants. For
every other chart here one PR per image is already one PR per chart.

What it fixes, measured against the Gitea API on 2026-10-12: eight of the
ten images had an open PR at the same moment (#102, #103, #104, #105, #106,
#107, #108, #111). Each edits freesailarr/values.yaml and, through the
postUpgradeTasks hook, freesailarr/Chart.yaml - so the API reported
mergeable: false for all eight, since they rewrite the same version: line.
One can merge; the other seven then need a rebase each, and every rebase
restarts the most expensive install-test in this repo.

The second symptom is sharper: all eight carried
version: 3.1.1+up1.0.0 -> 3.1.2+up1.0.0 while main already stood at
3.3.0+up1.0.0, because the hook derives everything from the base commit and
those branches predated the Gateway-API rounds. A rebase repairs it, which
is the point - nothing fails if the rebase does not happen first.

Two rules rather than one, and not for tidiness: renovate rejects the
combination outright with "packageRules cannot combine both
matchUpdateTypes and separateMinorPatch", a config-validation error. That
is consistent, since separateMinorPatch decides the split a rule would then
be selecting on. Without the second rule the group still arrives as two
PRs - verified by dry-run against a checkout holding those eight updates,
they split onto renovate/freesailarr-stack (the three minors) and
renovate/patch-freesailarr-stack (the five patches); note that the setting
prefixes the branch rather than suffixing it. With both rules, one branch
carries all eight.

Decisions, with their reasons in the rules themselves: matched by file name
so that no second list of image names has to be kept current (an image
added later would silently never join the group); mediathekarr included,
since its digest PR blocks the others identically - checked with a stale
digest, because a current pin makes a working and a broken rule look the
same; gluetun and busybox included, as neither's role has anything to do
with the blocking; and majors kept out via matchUpdateTypes, so the
stepwise app-major procedure and separateMultipleMajor keep working.

What it cannot show from here: Renovate runs as the shared instance outside
this repository, so the evidence available is a config that validates and a
local lookup that groups as intended - not the next real run.
This commit was merged in pull request #121.
This commit is contained in:
jklein committed 2026-10-12 22:00:42 +00:00
1 parent 7003f3674c
commit d36112aaf6
2 files changed
+137 -2

No files matched your search

+116
View File
@@ -229,6 +229,122 @@
// the major bucket once.
allowedVersions: "!/^(14\\.3\\.9|20\\.04\\.1)$/",
},
{
description: [
"The freesailarr stack arrives as ONE grouped PR per cycle instead of one PR per image.",
"This is the only groupName in this file and a deliberate reversal of the stance written",
"above the app-version managers, which says the built-in helm-values manager finds the ten",
"images in that chart's values.yaml and 'proposes one update per image'. That sentence",
"described what Renovate does; it was never an argument that one PR per image is what this",
"chart wants. For every other chart in this repo it still is — one chart, one image, one",
"PR is already the grouping — and that is why this rule is scoped to one file rather than",
"turning into a repo-wide policy.",
"",
"WHAT CHANGED, measured on 2026-10-12 against the Gitea API: eight of this chart's ten",
"images had an open Renovate PR at the same moment — #102 bazarr, #103 qbittorrent, #104",
"sonarr, #105 flaresolverr, #106 recyclarr, #107 prowlarr, #108 radarr, #111 seerr. Each",
"one edits freesailarr/values.yaml and, through the postUpgradeTasks hook above, also",
"freesailarr/Chart.yaml. The API reported `mergeable: false` for all eight, because the",
"`version:` line they rewrite is the same line in all eight. One of them can merge; the",
"other seven then need a rebase round each, and every rebase restarts the most expensive",
"install-test in this repo — a real Wireguard tunnel and ten containers in one pod.",
"",
"The second symptom is the sharper one. All eight carried",
"`version: 3.1.1+up1.0.0 -> 3.1.2+up1.0.0` while main already stood at 3.3.0+up1.0.0:",
"the hook derives everything from the BASE COMMIT (see its comment above), and these",
"branches were cut before the Gateway-API rounds landed. Merging one unrebased would have",
"moved the chart version backwards by two minors and republished the chart under a version",
"the registry already has. A rebase repairs it — the point is that nothing FAILS when the",
"rebase does not happen first, so the repair depends on the bot reaching the branch before",
"a human reaches the merge button.",
"",
"WHY GROUPING FIXES BOTH: one branch, one `version:` bump computed once from one base, one",
"publish, one install-test run for the whole stack. Nothing is left to serialize, so",
"nothing is left to rebase.",
"",
"MATCHED BY FILE, NOT BY A LIST OF IMAGE NAMES. What creates the collision is 'touches",
"freesailarr/Chart.yaml', and that is true of every dependency Renovate finds in that",
"chart's values.yaml, whatever its manager, datasource or role in the pod. A",
"matchPackageNames list would be a second place to keep the chart's image set current, and",
"its failure mode is silence: an image added later simply never joins the group, with no",
"error and no warning (#158 — a valid rule is not a matching rule). matchFileNames needs",
"no maintenance to stay complete.",
"",
"Consequences of that choice, each one intended and not a side effect:",
" - mediathekarr IS in the group, even though it comes from its own customManagers block",
" further down rather than from helm-values. Its digest PR edits the same two files and",
" blocks the other seven exactly as they block each other, so leaving it beside the",
" group would keep a smaller copy of the problem alive. Its own block is untouched; a",
" group decides which BRANCH an update lands on, not how it is found. `digest` is in",
" matchUpdateTypes precisely so this one is covered. Verified the way that block's own",
" comment demands, because while the pin is current a working rule and a broken one look",
" identical: with the real digest the run produces no mediathekarr update at all, so it",
" proves nothing; with the digest set to sha256:0000... the digest update appears ON THE",
" GROUP BRANCH next to the other eight, not on renovate/docker.io-pcjones-mediathekarr-",
" beta as it would without this rule.",
" - gluetun and the busybox init image are in the group too, against the instinct to hold",
" them apart because gluetun is the tunnel every other container's network namespace",
" depends on and busybox is 'only' an init container. Neither property has anything to",
" do with the blocking, and a grouped PR hides nothing: the review still sees every",
" changed line, it just no longer sees them eight times. (The busybox pinned in",
" ci/<chart>/ is a different thing and stays unmanaged — see the ci/** rule at the top.)",
"",
"MAJORS STAY OUT, which is what matchUpdateTypes is doing here. CLAUDE.md and README",
"require an app major to be taken stepwise, in its own PR, and separateMultipleMajor above",
"splits a multi-major jump into one PR per major line to produce exactly that sequence. A",
"group that threw a major in with seven patches would undo both, and a major is the update",
"where the review most needs one image at a time. Majors therefore keep arriving",
"individually and keep their deliberately missing age gate.",
"",
"A groupName ALONE DOES NOT DO THE JOB, which is why the rule directly below exists and",
"is not a stylistic split. The global `separateMinorPatch: true` at the top of this file",
"keeps splitting minor from patch INSIDE the group. Measured (renovate 44.x,",
"--platform=local --dry-run=full, real registries, 2026-10-12, against a checkout at the",
"eight pending updates): with the group rule and nothing else, the eight land on TWO",
"branches —",
" renovate/freesailarr-stack prowlarr 2.6.5, radarr 6.4.4, seerr v3.5.0 (minor)",
" renovate/patch-freesailarr-stack bazarr, qbittorrent, sonarr, flaresolverr, recyclarr",
"— and note the shape: separateMinorPatch prefixes the branch, it does not suffix it, so",
"the second name is easy to miss when scanning a branch list. Two PRs editing the same",
"`version:` line is the original problem at a quarter of the size, so the override is",
"required for the result this rule is for. With it, the same run puts all eight on one",
"branch.",
"",
"And the override CANNOT live in this rule: renovate rejects the combination outright with",
" packageRules[n]: packageRules cannot combine both matchUpdateTypes and",
" separateMinorPatch",
"which is a config-validation error, not a warning — found by running the dry-run instead",
"of trusting the draft. It is consistent, too: separateMinorPatch is what DECIDES the",
"updateType split, so a rule may not both perform the split and select on its outcome.",
"Hence two rules over the same matchFileNames, one selecting updateTypes to group and one",
"setting the split. Keep them together and keep them in this order.",
"",
"WHAT THIS RULE CANNOT DO: it takes effect on the next run of the shared instance, and",
"that instance is outside this repository. What is verifiable from here is that the config",
"validates and that the lookup groups as intended; that the bot then behaves that way is",
"not provable from this side (see README, 'Renovate on Gitea').",
],
matchFileNames: ["freesailarr/values.yaml"],
matchUpdateTypes: ["patch", "minor", "digest"],
groupName: "freesailarr stack",
groupSlug: "freesailarr-stack",
},
{
description: [
"The other half of the freesailarr grouping directly above — read that block first, the",
"whole argument is there. Separate only because Renovate refuses matchUpdateTypes and",
"separateMinorPatch in one rule ('packageRules cannot combine both matchUpdateTypes and",
"separateMinorPatch', a validation error). Without this, the group still arrives as two",
"PRs, one of them on the easily missed renovate/patch-freesailarr-stack.",
"",
"It deliberately carries NO matchUpdateTypes, so it also applies to a major — which costs",
"nothing: separateMinorPatch only ever decides whether minor and patch share a branch, and",
"a major is in neither bucket. Majors stay single PRs because the rule above does not",
"group them, not because of anything here.",
],
matchFileNames: ["freesailarr/values.yaml"],
separateMinorPatch: false,
},
],
customManagers: [