-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdefault.json
More file actions
581 lines (581 loc) · 46.3 KB
/
Copy pathdefault.json
File metadata and controls
581 lines (581 loc) · 46.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:best-practices"
],
"timezone": "Europe/Paris",
"labels": [
"dependencies"
],
"vulnerabilityAlerts": {
"enabled": true,
"automerge": false,
"addLabels": [
"security"
]
},
"osvVulnerabilityAlerts": true,
"packageRules": [
{
"description": "Auto-merge routine update types. Deliberately an explicit allowlist instead of a global automerge=true so the remaining types (rollback, replacement, bump) fail closed to manual review. pinDigest is included because it is the same trust class as pin/digest (one-time action-digest pinning). lockFileMaintenance is included because its PRs are structurally release-inert (lockfiles are in the CI detect job's path-exclusion list, so a lockfile-only push cannot publish or tag) and only ever move transitive versions inside already-declared ranges; leaving it out produced ~10 permanently-green PRs every Monday whose weekly hand-merge was pure toil, not review. Renovate does apply packageRules to the lock-file-maintenance config (flatten.ts calls applyPackageRules twice on it with updateType set), so matching it here is enough.",
"matchUpdateTypes": [
"minor",
"patch",
"digest",
"pin",
"pinDigest",
"lockFileMaintenance"
],
"automerge": true
},
{
"description": "Major updates require manual approval. addLabels (not labels) so the top-level 'dependencies' label is kept — labels is non-mergeable and would replace it.",
"matchUpdateTypes": [
"major"
],
"automerge": false,
"addLabels": [
"major-update"
]
},
{
"description": "Skip the root-level example compose.yaml each app/image repo ships — it references the repo's own image, so updating it is churn. A packageRule (not ignorePaths) because ignorePaths is non-mergeable: overriding it here would silently drop config:recommended's node_modules/vendor/tests/examples ignore defaults for every consumer. Root-level only; nested compose files (e.g. a private deployment repo's apps/*/compose.yaml) are unaffected.",
"matchFileNames": [
"compose.yaml"
],
"enabled": false
},
{
"description": "All six synced workflow destinations (ci/codeql/coverage/release/scorecard/security, per cplieger/ci scripts/classify-repos.py) are OWNED by repo-sync: the templates carry the cplieger/ci digest pin, so every ci release already propagates fleet-wide as auto-merging chore(sync) PRs with the new digest baked in. Disable Renovate on these destination files so sync is the single writer. Previously only scorecard.yml was covered and Renovate raced repo-sync on every ci release in the other five: the sync PR usually landed first, Renovate autoclosed its now-redundant digest PR (GitHub permanently drops auto-merge arming when a PR closes), and the next release reopened the same PR unarmed — Renovate arms platform automerge only at PR creation, never on reopen — leaving permanently-green never-merging PRs cycling open/autoclosed fleet-wide (observed 2026-07-30..08-04 across 13 repos). Excluded: cplieger/ci (these filenames ARE the canonical reusable workflows there, where Renovate must keep updating pins) and cplieger/.github (hand-maintained ci.yaml; not a sync target since the repo is not releaseable). A future non-sync-target repo hand-maintaining one of these six filenames needs its own exclusion here or its pins freeze.",
"matchManagers": [
"github-actions"
],
"matchFileNames": [
".github/workflows/ci.yaml",
".github/workflows/codeql.yml",
".github/workflows/coverage.yml",
".github/workflows/release.yaml",
".github/workflows/scorecard.yml",
".github/workflows/security.yml"
],
"matchRepositories": [
"!cplieger/ci",
"!cplieger/.github"
],
"enabled": false
},
{
"description": "Never digest-track cplieger/ci reusable workflows on a branch ref (a stray `# main`/`# master` instead of a release tag). A branch ref moves with every commit, so Renovate digest-chases HEAD and, with digest auto-merge on, loops forever (and fans out to every consumer via Sync). Reusable-workflow pins MUST track a release tag such as `# v2`.",
"matchManagers": [
"github-actions"
],
"matchDepNames": [
"cplieger/ci"
],
"matchCurrentValue": "/^(main|master)$/",
"enabled": false
},
{
"description": "Group Go dependency updates. gomodUpdateImportPaths rewrites the /vN import path in .go source on a major bump (Go semantic import versioning: v2+ changes the module path, so source imports must change too). Without it Renovate edits only go.mod/go.sum and the PR lands non-compiling — e.g. github-scout#9, where the atomicfile v1->v2 bump left a stale v1 import plus a duplicate require. It is a built-in postUpdateOption (no allowedCommands allowlist needed), uses marwan-at-work/mod, handles standard github.com/* paths (not gopkg.in), and shells out to the go toolchain — which our self-hosted bot (apps/renovate, binarySource=install under the default UID 12021:0) installs on demand, the same toolchain access the OBS-033/034 fix restored. gomodTidy then reconciles go.sum. It does NOT fix genuine API breaks (renamed funcs, changed return signatures — e.g. atomicfile v2's WriteFile now returns (Result, error)); those still need a manual follow-up, but with imports correct, downstream CI flags only the real breakage instead of a phantom import error. Deliberately NOT paired with a postUpgradeTasks `go build` gate: that would require widening the global RENOVATE_ALLOWED_COMMANDS (a security surface) across every cplieger/* repo for only a marginal fail-fast gain, since each repo's own CI already rejects a non-compiling PR (that is how #9 surfaced).",
"matchManagers": [
"gomod"
],
"groupName": "Go dependencies",
"postUpdateOptions": [
"gomodTidy",
"gomodUpdateImportPaths"
]
},
{
"description": "Keep indirect (transitive) Go modules updated. Renovate disables indirect gomod updates by default; re-enabling stops transitive deps (e.g. golang.org/x/* pulled via go-pkcs12 / filippo.io/age) from silently rotting into CVEs. Folds into the 'Go dependencies' group and minor/patch auto-merge above.",
"matchManagers": [
"gomod"
],
"matchDepTypes": [
"indirect"
],
"enabled": true
},
{
"description": "Block the Go semantic-import-versioning trap where a transitive module's vN+1 path is offered as a 'major' update. modernc.org/libc is an indirect dep pinned by modernc.org/sqlite on the v1 line; the modernc.org/libc/v2 path is a separate dormant line (v2.1.30, frozen 2024-02) that nothing imports, so Renovate's 'major' PR only adds a phantom require that go mod tidy strips. Let sqlite drive libc; v1 minor/patch still flow via the indirect rule above.",
"matchManagers": [
"gomod"
],
"matchDepNames": [
"modernc.org/libc",
"modernc.org/libc/v2"
],
"matchUpdateTypes": [
"major"
],
"enabled": false
},
{
"description": "devDependencies use scope 'devdeps' (commit prefix 'chore(devdeps)') so cliff can skip them — they don't ship to consumers and shouldn't trigger version bumps. Runtime/peer deps keep the default 'deps' scope which cliff treats as a release.",
"matchDepTypes": [
"devDependencies"
],
"semanticCommitScope": "devdeps"
},
{
"description": "Adopt our own first-party libraries immediately (skip the config:best-practices minimum-release-age / stability-days wait). We author and release these ourselves (@cplieger/* on npm/JSR, github.com/cplieger/* Go modules); the release-to-Renovate-to-redeploy chain is intentional, so the bake window only adds friction and blocks propagation of our own versions. Third-party deps keep the inherited stability window.",
"matchPackageNames": [
"@cplieger/**",
"github.com/cplieger/**"
],
"minimumReleaseAge": "0 days"
},
{
"description": "Group the web-terminal npm pair (@cplieger/web-terminal-engine + @cplieger/web-terminal-ui) into ONE branch across ALL update types and managers (package.json pins, Dockerfile ARG pins, lockfiles). The UI declares the engine as a peerDependency with a caret range (^3.0.0 as of ui 4.1.0), so the two are only npm-resolvable in matching pairs: bumping the engine alone (or the UI alone) makes the consumer's tree peer-inconsistent, npm's lockfile refresh fails, and Renovate aborts the WHOLE repo run with 'Lock file error' — observed 2026-07-19 stalling every Renovate update on vibekit and web-terminal-kiro after engine v3 shipped. separateMajorMinor=false is the load-bearing part: the incident shape is exactly an engine MAJOR paired with a UI MINOR, which default grouping would still split into two peer-broken branches. Automerge stays safe under grouping (a branch automerges only when every member update allows it, so any major in the pair keeps the manual gate). matchDepNames (not matchPackageNames) so the engine repo's own frozen npm-alias devDep @cplieger/web-terminal-engine-v3 — whose packageName IS the engine — can never be swept into the group; that alias is additionally ignoreDeps'd in the engine repo. The Go module github.com/cplieger/web-terminal-engine/v* is deliberately NOT in this group: cross-language pairing is governed by the wire-compatibility floors, not package-version lockstep, and a Go major is real porting work that should stand alone.",
"matchDepNames": [
"@cplieger/web-terminal-engine",
"@cplieger/web-terminal-ui"
],
"groupName": "web-terminal client pair",
"separateMajorMinor": false,
"separateMultipleMajor": false
},
{
"description": "Coalesce same-sweep tool-registry bumps (cplieger/tool-catalog's registries.env pins) into one PR so both registries land in a single publish run. Scoped to minor/patch: a rare registry MAJOR keeps its own PR under the manual major gate, and a digest-only bump (retag) stands alone under the no-automerge alarm below — neither may hold the other registry's flow hostage in a blocked group branch. Successor of the pre-externalization registry group removed as zero-consumer in 2026-07 — the consumer is back, now in the publisher repo instead of the app images. matchDepNames keeps it a no-op for every other repo.",
"matchDepNames": [
"jdx/mise",
"aquaproj/aqua-registry"
],
"matchUpdateTypes": [
"minor",
"patch"
],
"groupName": "tool registries"
},
{
"description": "Tool-registry retag alarm (cplieger/tool-catalog): a digest-only bump means the pinned tag now dereferences to a DIFFERENT commit — upstream moved a tag without a version bump. For registries whose install definitions feed every toolbelt consumer, that is a supply-chain red flag deserving eyes, not automerge (the git-cliff asset-swap rule is the model; this restores the registry-scoped copy removed with the app-image pins in 2026-07, per ci-renovate.md's re-add note). Normal version bumps carry their digest change as minor/patch and keep flowing.",
"matchDepNames": [
"jdx/mise",
"aquaproj/aqua-registry"
],
"matchUpdateTypes": [
"digest"
],
"automerge": false,
"addLabels": [
"security"
],
"prBodyNotes": [
":rotating_light: **Upstream registry tag moved WITHOUT a version bump.** The pinned tag now dereferences to a different commit. A silently moved tag on a registry that feeds tool install definitions fleet-wide is a supply-chain red flag: verify the upstream repo's activity (release notes, commit history, security advisories) before merging. Merging publishes a new catalog compiled from the moved tag."
]
},
{
"description": "Recompute Dockerfile sha256 integrity pins in the SAME commit as the version bump, so these PRs are born green and automerge with no human step. No datasource publishes the sha256 of the artifact a version names (github-tags exposes a git commit, npm exposes SHA-512, keepalived.org / samba.org / the postfix mirror publish nothing), so every one of these pins used to need a maintainer to run curl | sha256sum and paste the result while the build sat red. Our Renovate is SELF-HOSTED, so postUpgradeTasks is available and does exactly this: the command runs after the manager rewrites the version literal (Renovate writes updated files to disk first, so the script sees the new version) and its Dockerfile edit is folded into the same commit. Each pin declares its own source URL in a `# repin: dep=<depName> url=<...>` marker line directly above the ARG, which is also the human-readable recompute recipe those Dockerfiles used to carry as prose. scripts/repin-sha.sh is synced from cplieger/ci; it is a no-op for any dep with no matching marker, so this rule is harmless in a repo that has not adopted a given pin. allowedCommands is globalOnly and lives in homelab/apps/renovate (anchored to exactly this invocation); with no allowlist entry nothing executes, so the mechanism is fail-closed by default. FAILURE POSTURE: a failing command does not abort the branch, it records an artifactError, which forces a PR, prints an artifact-problem section in the PR body and skips branch-automerge -- and the stale sha still fails the Dockerfile's own sha256sum -c gate, reddening ci / validate. So the worst case is the old behaviour, a red PR waiting for a human, never a silent bad merge. executionMode stays the default `update` so depName/newVersion are the single dep's values rather than a whole grouped branch's. matchDepNames keeps it a no-op for every other dep in the fleet.",
"matchManagers": [
"custom.regex"
],
"matchDepNames": [
"acassen/keepalived",
"cplieger/tool-catalog",
"emikulic/darkhttpd",
"krallin/tini",
"net-snmp/net-snmp",
"networkupstools/nut",
"pkolaczk/fclones",
"radvd-project/radvd",
"ryanoasis/nerd-fonts",
"stephane/libmodbus",
"typescript",
"vdukhovni/postfix"
],
"postUpgradeTasks": {
"commands": [
"sh scripts/repin-sha.sh {{{depName}}} {{{newVersion}}}"
],
"fileFilters": [
"Dockerfile",
"**/Dockerfile"
],
"executionMode": "update"
}
},
{
"description": "fclones (docker-fclones-scheduler) splits its integrity pins across the amd64 tarball sha256 and the arm64 COMMIT the version tag dereferences to. The sha256 half is handled by the repin postUpgradeTask above; the COMMIT half is not, and a stale one fail-closes the build, so this rule survives in reduced form to label the bump and embed only the command that is still manual. matchDepNames keeps it a no-op for other repos.",
"matchManagers": [
"custom.regex"
],
"matchDepNames": [
"pkolaczk/fclones"
],
"addLabels": [
"manual-sha-bump"
],
"prBodyNotes": [
":warning: **Manual commit-pin bump required.** `FCLONES_SHA256_AMD64` is recomputed automatically by the repin postUpgradeTask, but the arm64 half pins the COMMIT the tag dereferences to and no script here moves it. Recompute `ARG FCLONES_COMMIT` and paste it into the Dockerfile in this PR:\n\n```sh\ngit ls-remote https://github.com/pkolaczk/fclones.git \"refs/tags/{{{newVersion}}}^{}\"\n```\n\nFollow-up: converting this pair to the fleet's `digest=commit` customManager shape would let Renovate move it with no manual step at all."
]
},
{
"description": "rmlint (docker-rmlint-scheduler) builds rmlint from source across coupled pins the customManager does not move together: it bumps RMLINT_VERSION but leaves ARG RMLINT_COMMIT (the commit the tag dereferences to) and the 'Audited against rmlint <version>;' comment in config.go stale, both of which fail-close the build (a commit-mismatch check and a go-builder grep gate). Label the bump and embed the recompute + re-audit steps so the maintainer fix is mechanical. matchDepNames keeps it a no-op for other repos.",
"matchManagers": [
"custom.regex"
],
"matchDepNames": [
"sahib/rmlint"
],
"addLabels": [
"manual-sha-bump"
],
"prBodyNotes": [
":warning: **Manual pin bump + re-audit required.** rmlint builds from source with coupled pins the bot cannot move.\n\n1. Recompute `ARG RMLINT_COMMIT` (the tag dereferenced to its commit) and paste it into the Dockerfile:\n\n```sh\ngit ls-remote https://github.com/sahib/rmlint.git \"refs/tags/{{{newVersion}}}^{}\"\n```\n\n2. Re-audit `rmlint --help` for new output-redirect / command-exec flags, extend `dangerousFlags` in `config.go` if needed, and bump the `Audited against rmlint <version>;` comment to `{{{newVersion}}}` (a go-builder grep gate fails the build until it matches). See CONTRIBUTING.md."
]
},
{
"description": "rsync (docker-rsync-scheduler) builds from the pinned download.samba.org dist tarball (the stable release asset, NOT a GitHub tag archive) behind a sha256sum -c gate: the github-tags datasource bumps RSYNC_VERSION but leaves RSYNC_SHA256 stale, fail-closing the build. Label the bump PR and embed the recompute command. Tags are plain vX.Y.Z, so default versioning needs no override. matchDepNames keeps it a no-op for other repos.",
"matchManagers": [
"custom.regex"
],
"matchDepNames": [
"RsyncProject/rsync"
],
"addLabels": [
"manual-sha-bump"
],
"prBodyNotes": [
":warning: **Manual SHA bump required.** Verify the upstream signature FIRST, then recompute `RSYNC_SHA256` (the pin covers the download.samba.org dist tarball, not a GitHub tag archive), paste it into the Dockerfile in this PR, then push. Releases >= 3.4.0 are signed by Andrew Tridgell <andrew@tridgell.net> (signer named on https://rsync.samba.org/download.html; key from https://keys.openpgp.org/, fingerprint `9FEF 112D CE19 A0DC 7E88 2CB8 1BB2 4997 A853 5F6F`):\n\n```sh\nV={{{newVersion}}}\ncurl -sLO \"https://download.samba.org/pub/rsync/rsync-${V#v}.tar.gz\"\ncurl -sLO \"https://download.samba.org/pub/rsync/rsync-${V#v}.tar.gz.asc\"\ncurl -sL \"https://keys.openpgp.org/vks/v1/by-email/andrew%40tridgell.net\" | gpg --dearmor -o rsync-signing-key.gpg\ngpg --show-keys --with-fingerprint rsync-signing-key.gpg # expect the fingerprint above\ngpgv --keyring ./rsync-signing-key.gpg \"rsync-${V#v}.tar.gz.asc\" \"rsync-${V#v}.tar.gz\"\nsha256sum \"rsync-${V#v}.tar.gz\" # paste into RSYNC_SHA256 only after gpgv passes\n```"
]
},
{
"description": "Group the Go toolchain version dep (golang) with its per-arch tarball sha256 deps (golang-amd64 / golang-arm64, from the custom datasources) into ONE PR so GO_VERSION and both GO_SHA256_* pins always move together and the build never lands with a stale sha. This replaced the former golang manual-sha-bump rule now that the shas auto-update (go.dev's ?mode=json publishes each tarball's sha256, so the customDatasources resolve the new digest per arch — same auto-update model as the kiro-cli pin). For repos that pin only the Go version with no sha lines (vibekit / web-terminal-server), only `golang` matches, so the PR is just renamed — no behavior change.",
"matchDepNames": [
"go",
"golang",
"golang-amd64",
"golang-arm64"
],
"groupName": "golang toolchain"
},
{
"description": "Go toolchain MINOR-line bumps (1.26.5 -> 1.27.0) require manual approval. Go's version scheme is not semver: the leading 1 is frozen by the compatibility promise, so Go's `minor` is the real breaking axis, not a routine update — Go 1.27 alone moves Unicode 15 -> 17, re-backs encoding/json on the v2 implementation, changes the DEFLATE encoder's output bytes, and permanently removes the asynctimerchan GODEBUG. Without this rule the automerge-routine-update-types rule at the top of this file treats it as an ordinary `minor` and merges it unattended in every repo the day the release reaches go.dev's stable list. Deliberately scoped to `minor` only: Go PATCH updates are the stdlib-CVE stream the go-directive customManager exists to deliver (GO-2026-5856 et al.) and must keep flowing without a human, and they reach the `FROM golang:` two-component pins as a digest bump rather than a version bump, so neither is touched here. matchDepNames covers all four toolchain shapes in one rule, including BOTH deps named `golang` (the golang-version ARG GO_VERSION pin and the docker builder image), so the whole 'golang toolchain' group carries the gate and no member can drift ahead alone. Label idiom matches homelab's own critical-infra minor gate (requires-approval + minor-update). The per-release migration surface this gate protects is inventoried in .kiro/steering/go-stdlib-changelog.md.",
"matchDepNames": [
"go",
"golang",
"golang-amd64",
"golang-arm64"
],
"matchUpdateTypes": [
"minor"
],
"automerge": false,
"addLabels": [
"requires-approval",
"minor-update"
]
},
{
"description": "Group the kiro-cli version+amd64-sha dep with its arm64 sha dep (custom.kiro-cli-arm64 datasource) into ONE PR so KIRO_CLI_VERSION, KIRO_CLI_SHA256, and KIRO_CLI_SHA256_ARM64 always move together and neither arch's install gate ever lands with a stale digest — the same grouping model as the golang toolchain rule above. Both entrypoints (web-terminal-kiro, vibekit) enforce the digest on BOTH arches, so an ungrouped arm64 bump would fail-closed every aarch64 boot until the paired PR landed.",
"matchDepNames": [
"kiro-cli",
"kiro-cli-arm64"
],
"groupName": "kiro-cli"
},
{
"description": "Group the toolbelt Go module with its Dockerfile ARG twin (TOOLBELT_TOOLCATALOG_VERSION) into ONE PR. The toolbelt consumers pin this module TWICE on purpose: go.mod requires the runtime engine that re-verifies required-tools.txt before every catalog swap, and the Dockerfile ARG selects the build-time `toolcatalog verify` binary — the two MUST be the same version or the build gate and the runtime gate are different verifiers. web-terminal-kiro asserts that equality with a toolbelt-pin-gate in the builder stage, so a half-bump does not drift silently, it fail-closes every image build (docker, docker-arm64, and the Trivy image scan) until the second pin catches up. Without this rule the two pins reach Renovate through DIFFERENT managers — gomod (swept into the 'Go dependencies' group branch) and the generic Dockerfile-ARG custom.regex manager (its own per-dep branch) — so one bump produced two half-PRs, neither of which can be green-and-complete on its own: observed 2026-08-03 as vibekit#827 (ARG only) + vibekit#828 (go.mod only) and web-terminal-kiro#666 (go.mod only, red on the pin gate). Same cross-manager coupling model as the 'kiro-cli' and 'golang toolchain' groups above and the 'web-terminal client pair' group, and the reason those exist: a pin pair that a gate compares must never be splittable into two branches. separateMajorMinor is left at the default because both pins are the same dep at the same version and therefore always carry the same update type — a toolbelt major keeps the group behind the manual major gate. The /** form keeps the group intact across a future Go semantic-import major (toolbelt/v3).",
"matchDepNames": [
"github.com/cplieger/toolbelt",
"github.com/cplieger/toolbelt/**"
],
"groupName": "toolbelt"
},
{
"description": "golang docker image: wait 1 day before pinning a version bump. The official golang tag publishes its multi-arch manifest in stages, so Renovate can resolve the tag digest during the publish window while the manifest-list index is still EMPTY (0 platform manifests); that empty digest then fails every consumer's native amd64+arm64 docker build with 'no match for platform in manifest'. A 1-day stabilization delay rides out the publish race (the same failure mode homelab guards PostgreSQL against). Scoped to matchDatasources=docker so it never delays the go.dev-tarball golang-version dep or the golang-amd64/golang-arm64 sha deps.",
"matchDatasources": [
"docker"
],
"matchDepNames": [
"golang"
],
"minimumReleaseAge": "1 day"
},
{
"description": "docker/buildx: wait 3 days before bumping the BUILDX_VERSION pin in cplieger/ci's docker-release.yaml. setup-buildx-action does not download from the buildx release itself — it resolves the URL from docker/actions-toolkit's .github/buildx-releases.json, a static file a bot regenerates AFTER each buildx release. Bumping the pin inside that window fails EVERY docker release in the fleet with 'Cannot find Buildx release <v> in releases JSON', and nothing catches it pre-merge: ci.yaml's build-ability job passes no version input, so every PR stays green while every post-merge release dies. Cost the fleet a release outage on 2026-08-04 (buildx v0.36.1 published 15:50Z, Renovate landed it 16:02Z, the JSON still lacked it 7+ hours later; web-terminal-kiro, web-terminal-server and vibekit all failed). 3 days rather than the golang rule's 1 day because that JSON's bot is irregular (updates observed 2026-07-23, -28, -30) and a day would not have covered this incident; it also matches the npm bake window config:best-practices already imposes. Only cplieger/ci pins buildx, so this is a no-op everywhere else.",
"matchDatasources": [
"github-releases"
],
"matchDepNames": [
"docker/buildx"
],
"minimumReleaseAge": "3 days"
},
{
"description": "Plex (plexinc/pms-docker): tags are <major>.<minor>.<patch>.<build>-<commithash>. Parse them so build-number bumps are detected while the per-release commit hash is ignored. Default docker versioning otherwise treats the -<hash> suffix as a fixed variant/compatibility tag and never finds a newer image sharing that exact suffix, so no update is ever offered (this once froze a pinned image from updating after its reference gained a docker.io/ registry prefix). Only one private consumer references this image, so this is a no-op for every other repo.",
"matchPackageNames": [
"docker.io/plexinc/pms-docker",
"plexinc/pms-docker"
],
"versioning": "regex:^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)\\.(?<build>\\d+)-[a-f0-9]+$"
},
{
"description": "qbittorrent-nox (qBittorrent official image): stay on the libtorrent 1.x tag line and never bump to the lt2 variant. Tags are <version>-<build> for lt1 (e.g. 5.2.2-1) and <version>-lt2-<build> for lt2 (e.g. 5.2.2-lt2-1). We deliberately hold lt1.2 to avoid libtorrent 2.0's mmap page-cache RAM blowup at high torrent counts; lt2 becomes viable only once 2.1 (pread_disk_io, no mmap) ships stable. The custom versioning regex parses <major>.<minor>.<patch>-<build> so build bumps (5.2.2-1 -> 5.2.2-2) and version bumps order correctly (default docker versioning misreads the -N suffix) AND it structurally excludes -lt2- tags (they don't match the pattern, so Renovate can't parse them as newer). allowedVersions is the explicit belt-and-suspenders guard. Only one private consumer references this image, so this is a no-op for every other repo.",
"matchPackageNames": [
"ghcr.io/qbittorrent/docker-qbittorrent-nox",
"docker.io/qbittorrentofficial/qbittorrent-nox",
"qbittorrentofficial/qbittorrent-nox"
],
"versioning": "regex:^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)-(?<build>\\d+)$",
"allowedVersions": "/^\\d+\\.\\d+\\.\\d+-\\d+$/"
},
{
"description": "git-cliff digest-ONLY updates (same version, different tarball sha) must never auto-merge: an asset whose digest changes without a version bump is exactly the release-asset-replacement event the pinned sha exists to catch — auto-merging it would ratify the swap unreviewed. Version bumps (which carry their new digest in the same PR) keep the normal minor/patch auto-merge; only the bare digest change is gated. Placed last so automerge=false wins the rule merge.",
"matchDepNames": [
"orhun/git-cliff"
],
"matchUpdateTypes": [
"digest"
],
"automerge": false,
"addLabels": [
"security"
],
"prBodyNotes": [
":rotating_light: **Upstream release asset changed WITHOUT a version bump.** The pinned git-cliff tarball's sha256 no longer matches the published asset for the same tag. A silently replaced release asset is a supply-chain red flag: verify upstream's changelog and announcements before merging."
]
}
],
"customDatasources": {
"git-cliff": {
"defaultRegistryUrlTemplate": "https://api.github.com/repos/orhun/git-cliff/releases?per_page=100",
"format": "json",
"transformTemplates": [
"{ \"releases\": [$[prerelease = false and draft = false].($tag := tag_name; $t := assets[digest != null and name = \"git-cliff-\" & $substring($tag, 1) & \"-x86_64-unknown-linux-gnu.tar.gz\"]; $count($t) = 1 ? { \"version\": $tag, \"digest\": $substringAfter($t.digest, \"sha256:\") })] }"
]
},
"kiro-cli": {
"defaultRegistryUrlTemplate": "https://desktop-release.q.us-east-1.amazonaws.com/index.json",
"format": "json",
"transformTemplates": [
"{ \"releases\": versions.{ \"version\": version, \"digest\": packages[fileType = 'zip' and targetTriple = 'x86_64-unknown-linux-gnu' and variant = 'headless'][0].sha256 } }"
]
},
"kiro-cli-arm64": {
"defaultRegistryUrlTemplate": "https://desktop-release.q.us-east-1.amazonaws.com/index.json",
"format": "json",
"transformTemplates": [
"{ \"releases\": versions.{ \"version\": version, \"digest\": packages[fileType = 'zip' and targetTriple = 'aarch64-unknown-linux-gnu' and variant = 'headless'][0].sha256 } }"
]
},
"golang-amd64": {
"defaultRegistryUrlTemplate": "https://go.dev/dl/?mode=json&include=all",
"format": "json",
"transformTemplates": [
"{ \"releases\": [$[stable=true].{ \"version\": $substringAfter(version, \"go\"), \"digest\": $join(files[os=\"linux\" and arch=\"amd64\" and kind=\"archive\"].sha256) }] }"
]
},
"golang-arm64": {
"defaultRegistryUrlTemplate": "https://go.dev/dl/?mode=json&include=all",
"format": "json",
"transformTemplates": [
"{ \"releases\": [$[stable=true].{ \"version\": $substringAfter(version, \"go\"), \"digest\": $join(files[os=\"linux\" and arch=\"arm64\" and kind=\"archive\"].sha256) }] }"
]
}
},
"customManagers": [
{
"customType": "regex",
"description": "Track the go.mod `go` directive so a Go stdlib patch (e.g. GO-2026-5856, the crypto/tls Encrypted Client Hello leak fixed in go1.26.5) flows fleet-wide. Renovate's built-in gomod manager deliberately leaves the bare `go` directive alone — it treats `go 1.26.4` as a >= minimum-version constraint (go-mod-directive versioning) and only bumps it when a dependency forces it — so a stdlib CVE fix never reaches go.mod and the go-ci `govulncheck` step keeps failing until a manual bump (observed 2026-07: go.mod stuck at 1.26.4 fleet-wide while the Dockerfile ARG GO_VERSION had already advanced to 1.26.5 via the manager below). The golang-version datasource + the 'golang toolchain' group above land this in the same PR as any Dockerfile GO_VERSION bump; minor/patch auto-merge per the update-type rules, majors gated. Matches only a full `go X.Y.Z` line (the fleet convention); a `toolchain goX.Y.Z` line is left to the gomod manager.",
"managerFilePatterns": [
"/(^|/)go\\.mod$/"
],
"matchStrings": [
"\\ngo (?<currentValue>\\d+\\.\\d+\\.\\d+)\\n"
],
"datasourceTemplate": "golang-version",
"depNameTemplate": "go"
},
{
"customType": "regex",
"description": "Dockerfile ARG versions with renovate comments",
"managerFilePatterns": [
"/Dockerfile$/"
],
"matchStrings": [
"#\\s*renovate:\\s*datasource=(?<datasource>[a-z-]+)\\s+depName=(?<depName>[^\\s]+)(\\s+versioning=(?<versioning>[a-z-]+))?\\nARG\\s+\\w+=(?<currentValue>\\S+)"
]
},
{
"customType": "regex",
"description": "Dockerfile *_COMMIT pins that track a BRANCH of a tagless git repo (`branch=` marker): a `# renovate: datasource=git-refs depName=<short> packageName=<repo url> branch=<ref>` comment followed directly by an ARG *_COMMIT=<40-hex> line. This is the git-refs datasource's documented digest mode — the named ref goes in currentValue and the commit is matched as currentDigest — so Renovate resolves that branch's head itself and rewrites the ARG. Use it for upstreams that publish NO tags and no versioned artifact, where the commit is the only identifier the project offers: subflux's libx264 (code.videolan.org/videolan/x264 has zero tags, only master/stable/GPLv2-only, and VideoLAN's versioned snapshot tarballs stopped in 2019-12), for which the pin had silently sat 26 months and 31 commits behind `stable` because no manager could see it. Distinct from the two neighbouring commit-pin conventions and mutually exclusive with both: the generic ARG manager above requires ARG on the line immediately after depName, so the `packageName=` token locks it out; the `digest=commit` manager below needs datasource=github-tags plus that marker token and resolves a TAG to its commit, which a tagless repo cannot offer. Also outside repin-sha.sh's postUpgradeTasks scope, which recomputes the sha256 of an artifact a VERSION names — here there is no version and no sha256, and Renovate supplies the new commit directly. Digest updates auto-merge under the routine-update-types rule at the top of this file; add a matchDepNames automerge=false rule (the git-cliff / tool-registry digest alarms are the pattern) for any pin whose upstream branch should get a human glance first. re2-safe. A no-op in every repo without a `branch=` marker.",
"managerFilePatterns": [
"/Dockerfile$/"
],
"matchStrings": [
"#\\s*renovate:\\s*datasource=git-refs\\s+depName=(?<depName>\\S+)\\s+packageName=(?<packageName>\\S+)\\s+branch=(?<currentValue>\\S+)\\s*\\nARG\\s+\\w+_COMMIT=(?<currentDigest>[a-f0-9]{40})"
],
"datasourceTemplate": "git-refs"
},
{
"customType": "regex",
"description": "Dockerfile tag+commit pin pairs (`digest=commit` marker): a `# renovate: datasource=github-tags depName=... digest=commit` comment followed by an ARG *_REF=<tag> line and an ARG *_COMMIT=<sha> line. currentValue is the tag, currentDigest the commit it dereferences to; github-tags resolves both on every bump (annotated tags dereference to the commit oid), so the pair auto-updates with no manual step — the Dockerfile fetches BY COMMIT (codeload.github.com/<owner>/<repo>/tar.gz/<commit>), making git content addressing the integrity anchor. Use this for any codeload/repo-tarball pin: no digest datasource exists for those tarballs and their bytes are not stable enough to hash (the retired mise/aqua manual-sha-bump model learned both the hard way; those pins have since moved out of the app images entirely — cplieger/tool-catalog publishes the compiled artifact — leaving this Dockerfile shape with no current consumers, while the registries.env manager below is the same convention with a live consumer; pattern reference: ci-renovate.md). The `digest=commit` marker doubles as the mutual-exclusion token: the generic ARG manager above requires ARG immediately after depName/versioning, so it can never double-match these blocks, and this manager requires the marker so it can never match plain ARG pins. re2-safe (no lookaround).",
"managerFilePatterns": [
"/Dockerfile$/"
],
"matchStrings": [
"#\\s*renovate:\\s*datasource=github-tags\\s+depName=(?<depName>[^\\s]+)\\s+digest=commit\\s*\\nARG\\s+\\w+_REF=(?<currentValue>\\S+)\\s*\\nARG\\s+\\w+_COMMIT=(?<currentDigest>[a-f0-9]{40})"
],
"datasourceTemplate": "github-tags",
"versioningTemplate": "semver"
},
{
"customType": "regex",
"description": "registries.env tag+commit pin pairs (cplieger/tool-catalog): the Dockerfile `digest=commit` convention above adapted to the catalog publisher's env file — same marker comment, same *_REF/*_COMMIT pair, minus the ARG keyword. currentValue is the tag, currentDigest the commit it dereferences to; github-tags resolves both on every bump, and scripts/publish.sh fetches codeload tarballs BY COMMIT, so git content addressing stays the integrity anchor. A merged bump fires the repo's publish workflow (push trigger on registries.env), which is what makes catalog releases follow upstream at the Renovate sweep cadence instead of a daily poll. A no-op in every repo without a registries.env file. re2-safe (no lookaround).",
"managerFilePatterns": [
"/(^|/)registries\\.env$/"
],
"matchStrings": [
"#\\s*renovate:\\s*datasource=github-tags\\s+depName=(?<depName>[^\\s]+)\\s+digest=commit\\s*\\n\\w+_REF=(?<currentValue>\\S+)\\s*\\n\\w+_COMMIT=(?<currentDigest>[a-f0-9]{40})"
],
"datasourceTemplate": "github-tags",
"versioningTemplate": "semver"
},
{
"customType": "regex",
"description": "Go toolchain per-arch tarball sha256 pins (web-terminal-kiro). Pairs with the golang-amd64 / golang-arm64 customDatasources to auto-update GO_SHA256_AMD64 / GO_SHA256_ARM64 alongside GO_VERSION, grouped into one 'golang toolchain' PR by the packageRule below — this replaced the former golang manual-sha-bump rule. The `# go<version>` trailer on each ARG line is the load-bearing anchor that carries the version Renovate needs to look up that arch's digest; the arch is captured and routed to the matching custom datasource via datasourceTemplate. re2-safe (no lookbehind); GO_VERSION and the two sha lines are distinct non-overlapping lines, so grouped edits apply cleanly. A no-op in every repo without these `datasource=custom.golang-*` comments (vibekit / web-terminal-server pin only the version, so only the generic manager above fires there).",
"managerFilePatterns": [
"/Dockerfile$/"
],
"matchStrings": [
"#\\s*renovate:\\s*datasource=custom\\.golang-(?<arch>amd64|arm64)\\s+depName=(?<depName>[^\\s]+)\\nARG\\s+GO_SHA256_\\w+=(?<currentDigest>[a-f0-9]{64})\\s+#\\s*go(?<currentValue>\\S+)"
],
"datasourceTemplate": "custom.golang-{{{arch}}}",
"versioningTemplate": "semver"
},
{
"customType": "regex",
"description": "Workflow + composite-action tool version pins (golangci-lint, shellcheck, trivy, cosign, buildx, markdownlint-cli2, ruff, git-cliff, etc.) via a renovate comment + an optionally-prefixed VERSION assignment in EITHER style: shell (`CLIFF_VERSION=v2.13.1` in a run block) or YAML env (`TRIVY_VERSION: v0.71.2` in an env block). The env style was previously unmatched (`=` only), which silently left every env-block pin untracked despite its comment. Optional quotes around the value are tolerated. The action.yml pattern covers composite actions under actions/**.",
"managerFilePatterns": [
"/^\\.github/workflows/.+\\.ya?ml$/",
"/(^|/)action\\.ya?ml$/"
],
"matchStrings": [
"#\\s*renovate:\\s*datasource=(?<datasource>[a-z-]+)\\s+depName=(?<depName>[^\\s]+)(\\s+versioning=(?<versioning>[a-z-]+))?\\s*\\n\\s*[A-Z_]*VERSION[=:]\\s*['\"]?(?<currentValue>[^'\"\\s]+)"
]
},
{
"customType": "regex",
"description": "gitleaks SCANNER version pinned in a gitleaks-action env block. The action bundles its own scanner and hard-codes it (`GITLEAKS_VERSION || \"8.24.3\"` in src/index.js as of v3.0.0), so digest-pinning the action freezes the scanner too -- pinning the action is only half the pin. 8.24.3 silently ignores global `[[allowlists]]` `paths` in .gitleaks.toml, which held homelab's weekly job red for a month on already-allowlisted test fixtures. Needs its own manager rather than the generic one above because gitleaks tags are v-prefixed while the action requires a bare value, so the v has to be stripped via extractVersionTemplate.",
"managerFilePatterns": [
"/^\\.github/workflows/.+\\.ya?ml$/",
"/(^|/)action\\.ya?ml$/"
],
"matchStrings": [
"GITLEAKS_VERSION[=:]\\s*['\"]?(?<currentValue>\\d+\\.\\d+\\.\\d+)"
],
"depNameTemplate": "gitleaks/gitleaks",
"datasourceTemplate": "github-releases",
"extractVersionTemplate": "^v(?<version>.+)$"
},
{
"customType": "regex",
"description": "git-cliff version + tarball sha256 pinned as a pair at every install site in cplieger/ci (workflows + the git-cliff-version composite action). Pairs with the git-cliff customDatasource above, which maps each GitHub release to {version, digest} by exact x86_64-gnu tarball asset name (requiring exactly one match, so sidecar .sig/.sha512 assets and API ordering cannot mis-select) — so a version bump updates BOTH lines together and the sha256sum -c gates at the install sites never go stale (the same auto-update model as the kiro-cli and golang toolchain sha pins). Filtering drops prereleases, drafts, and releases whose asset carries digest=null (pre-2025 releases predating GitHub's asset-digest rollout). Known horizon: the datasource sees the newest 100 releases in one page; a pinned version older than that loses same-version digest-change detection (version bumps still flow from the newer releases on the page). Matches shell (CLIFF_VERSION=v…) and YAML-env (CLIFF_VERSION: v…) styles; the generic workflow tool-pin manager above cannot double-match these sites because its datasource character class rejects the dot in custom.git-cliff. The cplieger/ci contract probe (scripts/test-cliff-bump-semantics.sh) mirrors this manager's block shape — keep them in sync.",
"managerFilePatterns": [
"/^\\.github/workflows/.+\\.ya?ml$/",
"/(^|/)action\\.ya?ml$/"
],
"matchStrings": [
"#\\s*renovate:\\s*datasource=(?<datasource>custom\\.git-cliff)\\s+depName=(?<depName>[^\\s]+)\\s*\\n\\s*CLIFF_VERSION[=:]\\s*['\"]?(?<currentValue>[^'\"\\s]+)['\"]?\\s*\\n\\s*CLIFF_SHA256[=:]\\s*['\"]?(?<currentDigest>[a-f0-9]{64})"
],
"versioningTemplate": "semver"
},
{
"customType": "regex",
"description": "kiro-cli version + per-arch sha256 pinned in entrypoint.sh (web-terminal-kiro/vibekit). Two match shapes: the amd64 pair (KIRO_CLI_VERSION + KIRO_CLI_SHA256, datasource custom.kiro-cli) and the arm64 digest line (KIRO_CLI_SHA256_ARM64, datasource custom.kiro-cli-arm64, whose `# kiro-cli <version>` trailer is the load-bearing anchor carrying the version — same trailer model as the golang per-arch sha manager). The kiro-cli packageRule below groups both deps into one PR so the version and both arch digests always move together. re2-safe; the two shapes match disjoint lines (the first regex's datasource group requires whitespace after custom.kiro-cli, so it cannot match the -arm64 marker).",
"managerFilePatterns": [
"/entrypoint\\.sh$/"
],
"matchStrings": [
"#\\s*renovate:\\s*datasource=(?<datasource>custom\\.kiro-cli)\\s+depName=(?<depName>[^\\s]+)\\s*\\nKIRO_CLI_VERSION=\"(?<currentValue>[^\"]+)\"\\s*\\nKIRO_CLI_SHA256=\"(?<currentDigest>[^\"]+)\"",
"#\\s*renovate:\\s*datasource=(?<datasource>custom\\.kiro-cli-arm64)\\s+depName=(?<depName>[^\\s]+)\\s*\\nKIRO_CLI_SHA256_ARM64=\"(?<currentDigest>[a-f0-9]{64})\"\\s+#\\s*kiro-cli\\s+(?<currentValue>\\S+)"
],
"versioningTemplate": "semver"
},
{
"customType": "regex",
"description": "tools.json github entries (vibekit/web-terminal-kiro)",
"managerFilePatterns": [
"/tools\\.json$/"
],
"matchStrings": [
"\"version\":\\s*\"(?<currentValue>[^\"]+)\",\\s*\"update\":\\s*\\{\\s*\"method\":\\s*\"github\",\\s*\"repo\":\\s*\"(?<depName>[^\"]+)\""
],
"datasourceTemplate": "github-releases"
},
{
"customType": "regex",
"description": "tools.json gitlab entries (vibekit/web-terminal-kiro)",
"managerFilePatterns": [
"/tools\\.json$/"
],
"matchStrings": [
"\"version\":\\s*\"(?<currentValue>[^\"]+)\",\\s*\"update\":\\s*\\{\\s*\"method\":\\s*\"gitlab\",\\s*\"repo\":\\s*\"(?<depName>[^\"]+)\""
],
"datasourceTemplate": "gitlab-releases"
},
{
"customType": "regex",
"description": "tools.json npm entries (vibekit/web-terminal-kiro)",
"managerFilePatterns": [
"/tools\\.json$/"
],
"matchStrings": [
"\"version\":\\s*\"(?<currentValue>[^\"]+)\",\\s*\"update\":\\s*\\{\\s*\"method\":\\s*\"npm\",\\s*\"package\":\\s*\"(?<depName>[^\"]+)\""
],
"datasourceTemplate": "npm"
},
{
"customType": "regex",
"description": "tools.json gomod entries (vibekit/web-terminal-kiro)",
"managerFilePatterns": [
"/tools\\.json$/"
],
"matchStrings": [
"\"version\":\\s*\"(?<currentValue>[^\"]+)\",\\s*\"update\":\\s*\\{\\s*\"method\":\\s*\"gomod\",\\s*\"module\":\\s*\"(?<depName>[^\"]+)\""
],
"datasourceTemplate": "go"
},
{
"customType": "regex",
"description": "tools.json rubygems entries (vibekit/web-terminal-kiro)",
"managerFilePatterns": [
"/tools\\.json$/"
],
"matchStrings": [
"\"version\":\\s*\"(?<currentValue>[^\"]+)\",\\s*\"update\":\\s*\\{\\s*\"method\":\\s*\"rubygems\",\\s*\"package\":\\s*\"(?<depName>[^\"]+)\""
],
"datasourceTemplate": "rubygems"
},
{
"customType": "regex",
"description": "FFmpeg version (n-prefixed tags, e.g. subflux Dockerfile)",
"managerFilePatterns": [
"/Dockerfile$/"
],
"matchStrings": [
"ARG\\s+FFMPEG_VERSION=(?<currentValue>\\S+)"
],
"depNameTemplate": "FFmpeg/FFmpeg",
"datasourceTemplate": "github-tags",
"extractVersionTemplate": "^n(?<version>\\d+\\.\\d+(?:\\.\\d+)?)$",
"versioningTemplate": "semver"
},
{
"customType": "regex",
"description": "xcaddy build --with plugin pins (e.g. docker-caddy Dockerfile)",
"managerFilePatterns": [
"/Dockerfile$/"
],
"matchStrings": [
"--with\\s+(?<depName>github\\.com/[^@\\s]+)@(?<currentValue>v\\d+\\.\\d+\\.\\d+)"
],
"datasourceTemplate": "go",
"versioningTemplate": "semver"
}
],
"dependencyDashboardLabels": [
"renovate"
]
}