diff --git a/.github/workflows/renovate-hash-sync.yml b/.github/workflows/renovate-hash-sync.yml index dfc4a75..c71105d 100644 --- a/.github/workflows/renovate-hash-sync.yml +++ b/.github/workflows/renovate-hash-sync.yml @@ -96,6 +96,14 @@ on: types: [opened, synchronize] paths: - "configs/mister_de10nano_defconfig" + # The kernel-only defconfig copies the main one's kernel stanza, so a + # 6.18 bump touches BOTH files (one Renovate PR, same depName). Listed + # here so a bump is still picked up if a future change ever moves the + # kernel version into the copy alone. configs/mister_rt.fragment is + # deliberately absent: its -rc hash cannot be auto-refreshed (no signed + # manifest upstream; see the kernel step's own note), so triggering this + # workflow for it would do nothing but burn a runner. + - "configs/mister_kernel_defconfig" - "package/rtl8812au/rtl8812au.mk" - "package/rtl8814au-morrownr/rtl8814au-morrownr.mk" - "package/rtl8821au-morrownr/rtl8821au-morrownr.mk" @@ -279,21 +287,45 @@ jobs: newhash=$(echo "$matchline" | awk '{print $1}') newline="sha256 ${newhash} linux-${kver}.tar.xz" - # Match the RELEASE tarball line SPECIFICALLY (sha256 ... linux-.tar.xz), - # never "the first sha256 line": linux.hash now also carries entries this - # step must NOT manage -- e.g. the RT beta's -rc cgit snapshot - # (linux-*.tar.gz, no signed manifest exists; see the hash file's own -rc - # block for its provenance rules). A first-line match would clobber - # whichever entry happened to be on top; the filename-anchored match is - # order-independent and leaves foreign lines alone. An RT kernel bump - # (configs/mister_rt.fragment) therefore always needs its hash refreshed - # BY HAND per the hash file's documented TOFU procedure. - oldline=$(grep -m1 -E '^sha256 .* linux-[0-9][0-9.]*\.tar\.xz$' "$linuxhash" || true) + # Match the RELEASE tarball line SPECIFICALLY, never "the first sha256 + # line": linux.hash also carries entries this step must NOT manage -- + # notably the RT beta's kernel (configs/mister_rt.fragment). A + # first-line match would clobber whichever entry happened to be on top. + # + # Scope the match to THIS pin's major series (linux-.*.tar.xz). + # An extension-only match (linux-*.tar.xz) is not enough: it is + # sufficient only while the RT pin is an -rc, because Buildroot fetches + # -rc as a cgit snapshot (.tar.gz). The moment that pin reaches a + # stable mainline release it becomes linux-7.2.tar.xz -- which the old + # pattern also matched. Verified: with the RT line first, a 6.18 bump + # OVERWROTE it and left the stale 6.18 line intact, producing two 6.18 + # entries and no RT entry at all. The major-scoped match keeps the two + # pins on their own lines regardless of order. + # + # An RT kernel bump still always needs its hash refreshed BY HAND, per + # the hash file's documented TOFU procedure -- this step never touches + # that line either way. + major="${kver%%.*}" + release_re="^sha256 .* linux-${major}\.[0-9][0-9.]*\.tar\.xz\$" + + # If two lines somehow match, do not guess which one this pin owns. + nmatch=$(grep -c -E "$release_re" "$linuxhash" || true) + if [ "${nmatch:-0}" -gt 1 ]; then + echo "::error::$linuxhash has $nmatch lines matching the ${major}.x release pattern -- refusing to guess which one belongs to this pin." >&2 + grep -n -E "$release_re" "$linuxhash" >&2 || true + exit 1 + fi + + oldline=$(grep -m1 -E "$release_re" "$linuxhash" || true) if [ "$oldline" != "$newline" ]; then - awk -v newline="$newline" ' + # Replace by EXACT string match on the line we found, not by + # re-deriving a regex inside awk -- passing a regex through `awk -v` + # would have its backslash escapes eaten, silently turning `\.` into + # "any character". + awk -v oldline="$oldline" -v newline="$newline" ' BEGIN { done = 0 } - /^sha256 .* linux-[0-9][0-9.]*\.tar\.xz$/ && !done { print newline; done = 1; next } + $0 == oldline && !done { print newline; done = 1; next } { print } ' "$linuxhash" > "$linuxhash.tmp" mv "$linuxhash.tmp" "$linuxhash" diff --git a/docs/renovate.md b/docs/renovate.md index 4a31443..2286dfe 100644 --- a/docs/renovate.md +++ b/docs/renovate.md @@ -39,7 +39,8 @@ for the specific pieces most likely to need a fix on the first live run. | Pin | File(s) | Mechanism | Hash companion | |---|---|---|---| | Buildroot release | `Makefile` (`BUILDROOT_VERSION`) | `customManagers` regex, `github-tags` datasource, `allowedVersions` locked to `2026.02.x` | `BUILDROOT_SHA256` — **manual**, see below | -| Kernel | `configs/mister_de10nano_defconfig` (`BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE`) | `customManagers` regex + a `customDatasources` entry over `kernel.org/releases.json`, filtered to `moniker=longterm` and the `6.18.` prefix; `allowedVersions` locked to `6.18.y` as defense in depth | `board/mister/de10nano/patches/linux/linux.hash` — auto-refreshed by `renovate-hash-sync.yml` from kernel.org's signed `sha256sums.asc` | +| Kernel (6.18.y longterm) | **both** `configs/mister_de10nano_defconfig` *and* `configs/mister_kernel_defconfig` (`BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE`) | one `customManagers` regex listing both files + a `customDatasources` entry over `kernel.org/releases.json`, filtered to `moniker=longterm` and the `6.18.` prefix; `allowedVersions` locked to `6.18.y` as defense in depth. Same `depName` for both files, so Renovate emits **one PR touching both** | `board/mister/de10nano/patches/linux/linux.hash` — auto-refreshed by `renovate-hash-sync.yml` from kernel.org's signed `sha256sums.asc` | +| Kernel (RT/beta, mainline `-rc`) | `configs/mister_rt.fragment` (same symbol, different line) | a **separate** `customManagers` regex + its own `kernelMainline` datasource (`moniker=mainline`); `allowedVersions` locked to `7.x`. Labeled `rt-kernel-pin` + `needs-manual-hash` + `needs-manual-version-check` | **always manual** — the hash-sync kernel step reads its version from the DE10-Nano defconfig and rewrites only that pin's line, so it never touches this one. The PR stays red until a human writes it. Provenance depends on which side of the `-rc` boundary the pin is on: TOFU for a cgit `.tar.gz` snapshot (kernel.org signs no `-rc` manifest), or the signed `sha256sums.asc`/`.sign` once it reaches a stable release | | 9 driver commit-SHA pins | `package/{rtl8812au,rtl8814au-morrownr,rtl8821au-morrownr,rtl8821cu-morrownr,rtl8188fu,rtl8188eu-aircrack-ng,rtl88x2bu,xone,midilink}/*.mk` | `customManagers` regex per package, `git-refs` datasource tracking the upstream default branch's HEAD via `currentDigest` | matching `.hash` file — auto-refreshed by `renovate-hash-sync.yml` | | munt tag pin | `package/munt/munt.mk` | `github-tags` datasource, custom `regex:` versioning for the `munt_MAJOR_MINOR_PATCH` tag scheme | `package/munt/munt.hash` — auto-refreshed | | bcm20702-firmware tag pin | `package/bcm20702-firmware/bcm20702-firmware.mk` | `github-tags` datasource, `loose` versioning (flagged `needs-manual-version-check` — see caveat below) | `package/bcm20702-firmware/bcm20702-firmware.hash` — auto-refreshed | @@ -49,12 +50,34 @@ for the specific pieces most likely to need a fix on the first live run. | CI container digests | `ubuntu:26.04@sha256:...` in `build.yml`, `release.yml`, `reproducibility.yml`'s `container:` blocks | Renovate's built-in `github-actions` manager, `docker` datasource, `pinDigests: true` | n/a — digest updates carry their own content-hash | | GitHub Actions | every SHA-pinned `uses:` line (with a `# vX.Y.Z` comment) across `build.yml`, `release.yml`, `reproducibility.yml`, `publish-db.yml` | Renovate's built-in `github-actions` manager (no custom config needed — it already understands "SHA-pinned + trailing semver comment" and updates both together) | n/a | -That is **18 customManagers entries** (Buildroot, kernel, 9 driver commits, -munt, bcm20702-firmware, libchdr, lzma-sdk, and the 3 sdcard payload pins) -plus the two built-in managers +That is **19 customManagers entries** (Buildroot, the 6.18 kernel, the RT/beta +kernel, 9 driver commits, munt, bcm20702-firmware, libchdr, lzma-sdk, and the 3 +sdcard payload pins) plus the two built-in managers (`docker` digests, `github-actions`), covering every version/commit/digest pin this repository maintains by hand except the four listed below. +### Why the two kernel pins are separate managers + +`configs/mister_rt.fragment` carries the **identical Kconfig symbol** as the two +defconfigs, but pins a different upstream line (mainline, currently `7.2-rc3` and headed for +7.2 final) with different cadence, provenance, and review bar. A single manager +covering all three files would hand the longterm datasource a `-rc` +`currentValue` and the mainline datasource a `6.18.x` one. So there are two +managers with two datasources and two `depName`s, and each file pattern names +its files explicitly — no globbing over `configs/`, which would silently +re-couple them. + +Conversely, the two 6.18 files are deliberately in the **same** manager: +`mister_kernel_defconfig` copies the main defconfig's kernel/toolchain stanza +(see its header and `scripts/check-kernel-defconfig-sync.sh`), so they must move +together. Verified against a real Renovate run with both files held back: two +extracted upgrades, exactly **one** branch +(`renovate/kernel-longterm-6.18-6.x`) covering both. + +That lockstep check is what caught the 6.18.38 → 6.18.39 bump landing in only +one of the two files, back when the manager listed just +`mister_de10nano_defconfig`. + ## What is deliberately NOT managed, and why - **`package/xow-firmware`** — pins two Microsoft Windows Update driver diff --git a/renovate.json b/renovate.json index c5f48f0..30b15fa 100644 --- a/renovate.json +++ b/renovate.json @@ -57,6 +57,14 @@ "transformTemplates": [ "{\"releases\": [$map($filter(releases, function($r) { $r.moniker = \"longterm\" and $contains($r.version, \"6.18.\") }), function($r) { {\"version\": $r.version} })]}" ] + }, + "kernelMainline": { + "description": "kernel.org's releases.json filtered to the CURRENT MAINLINE release -- the line the RT/beta kernel variant tracks (configs/mister_rt.fragment, docs/rt-beta-kernel.md). This is normally a -rc (e.g. 7.2-rc3), which is exactly why it is a separate datasource from kernelLongterm618: the two pins track different upstream lines with different cadence, different provenance, and different review bars, and must never be able to update each other. The outer [ ] is required for the same reason documented above -- releases.json carries exactly one mainline entry, and JSONata would collapse a single-element sequence into a bare object, which Renovate rejects as `no-result` silently.", + "defaultRegistryUrlTemplate": "https://www.kernel.org/releases.json", + "format": "json", + "transformTemplates": [ + "{\"releases\": [$map($filter(releases, function($r) { $r.moniker = \"mainline\" }), function($r) { {\"version\": $r.version} })]}" + ] } }, "customManagers": [ @@ -75,9 +83,10 @@ }, { "customType": "regex", - "description": "Bump the pinned kernel (BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE) in the DE10-Nano defconfig, constrained to the current 6.18.y longterm line by the custom datasource above. The companion tarball hash (board/mister/de10nano/patches/linux/linux.hash) is NOT updated here -- it comes from kernel.org's own signed sha256sums.asc; see .github/workflows/renovate-hash-sync.yml, which handles this specific case automatically (unlike Buildroot's hash, above, kernel.org's manifest IS fetchable and parseable without a GPG keyring step beyond what the project's own manual process already does). CI (build.yml, pull_request-triggered) then proves the 24 carried Linux patches still apply against the bumped source -- see docs/renovate.md and PLAN.md \u00a713.", + "description": "Bump the pinned 6.18.y kernel (BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE) EVERYWHERE it appears, constrained to the current longterm line by the custom datasource above. BOTH defconfigs are listed on purpose: configs/mister_kernel_defconfig deliberately copies the main defconfig's kernel/toolchain stanza (see its header and scripts/check-kernel-defconfig-sync.sh), so a manager covering only the main one bumps half the repo and leaves the copy behind -- which is exactly what happened on the 6.18.38 -> 6.18.39 bump and is caught, loudly, by the lockstep check. Because both files carry the same depName, Renovate emits ONE PR touching both. configs/mister_rt.fragment carries the same SYMBOL but is deliberately NOT matched here: it pins the mainline -rc line and has its own manager below. The companion tarball hash (board/mister/de10nano/patches/linux/linux.hash) is NOT updated here -- it comes from kernel.org's own signed sha256sums.asc; see .github/workflows/renovate-hash-sync.yml. CI (build.yml, pull_request-triggered) then proves the carried Linux patches still apply against the bumped source -- see docs/renovate.md and PLAN.md \u00a713.", "managerFilePatterns": [ - "/^configs/mister_de10nano_defconfig$/" + "/^configs/mister_de10nano_defconfig$/", + "/^configs/mister_kernel_defconfig$/" ], "matchStrings": [ "BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE=\"(?[\\d.]+)\"" @@ -86,6 +95,19 @@ "datasourceTemplate": "custom.kernelLongterm618", "versioningTemplate": "loose" }, + { + "customType": "regex", + "description": "Bump the RT/beta kernel pin (BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE in configs/mister_rt.fragment), which tracks the mainline -rc line rather than 6.18.y -- see docs/rt-beta-kernel.md and ADR 0021. Kept as a SEPARATE manager from the 6.18 one above, with its own datasource and its own depName, because the two must never be able to propose each other's version: the fragment carries the identical Kconfig symbol, so a single shared manager would hand the longterm datasource a -rc currentValue (and vice versa). The matchString accepts BOTH an -rc and a plain mainline version (7.2-rc3 and 7.2), because 7.2 final IS the intended destination of this pin -- docs/rt-beta-kernel.md picks 7.2 precisely because PREEMPT_RT landed for ARM in 7.1, so the variant follows that line through its -rc series and out the other side. IMPORTANT -- the companion hash is NEVER auto-refreshed for this pin, unlike the 6.18 one: renovate-hash-sync.yml's kernel step reads its version from the DE10-Nano defconfig and only ever rewrites that pin's own line, so it leaves this one alone in every case. An RT bump PR is therefore EXPECTED to be red at the kernel download (\"No hash found for linux-...\", because BR2_DOWNLOAD_FORCE_CHECK_HASHES empties Buildroot's BR_NO_CHECK_HASH_FOR exemption) until a human refreshes it by hand. That is the intended, safe failure mode -- the same shape as the Buildroot hash above. NOTE that the artifact changes shape across the -rc boundary, and so does the provenance a human should demand: while on an -rc, Buildroot fetches a cgit-generated snapshot (linux-.tar.gz from git.kernel.org) for which kernel.org publishes NO signed manifest, so the value is TOFU per linux.hash's own -rc block; once this pin reaches a stable mainline release it becomes an ordinary linux-.tar.xz, which IS covered by kernel.org's signed sha256sums.asc and by a maintainer .sign, and should be transcribed from those rather than TOFU-pinned.", + "managerFilePatterns": [ + "/^configs/mister_rt\\.fragment$/" + ], + "matchStrings": [ + "BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE=\"(?\\d+\\.\\d+(?:-rc\\d+)?)\"" + ], + "depNameTemplate": "kernel-mainline-rt", + "datasourceTemplate": "custom.kernelMainline", + "versioningTemplate": "loose" + }, { "customType": "regex", "description": "RTL8812AU driver commit pin (morrownr/8812au-20210820, main branch). Hash: package/rtl8812au/rtl8812au.hash, refreshed by renovate-hash-sync.yml.", @@ -335,6 +357,21 @@ "kernel-pin" ] }, + { + "description": "RT/beta kernel pin (configs/mister_rt.fragment), tracking mainline -rc. Constrained to the 7.x line as defense in depth, mirroring the 6.18 rule above: a jump to a new MAJOR is a deliberate decision about what the beta variant tracks, not routine automation. Labeled distinctly from kernel-pin so a reviewer is never in doubt about WHICH kernel a PR moves -- the two carry the identical Kconfig symbol and otherwise look alike in a diff. needs-manual-hash is not decorative: this PR cannot go green until a human writes the new hash into linux.hash by hand. The hash-sync workflow never touches this line -- and how the value should be obtained depends on which side of the -rc boundary the pin is on (TOFU for a cgit -rc snapshot, kernel.org's signed manifest for a stable release). See the manager description. needs-manual-version-check is carried for the same reason bcm20702-firmware is: `loose` versioning's ordering of a -rcN suffix is not something to trust blindly.", + "matchManagers": [ + "custom.regex" + ], + "matchDepNames": [ + "kernel-mainline-rt" + ], + "allowedVersions": "/^7\\./", + "addLabels": [ + "rt-kernel-pin", + "needs-manual-hash", + "needs-manual-version-check" + ] + }, { "description": "Driver/firmware commit and tag pins -- label distinctly so a reviewer knows these change out-of-tree kernel-module source, not just a build tool version.", "matchManagers": [