From 6b8fc0f96058a5ba2f52420c9098ad6ad535d456 Mon Sep 17 00:00:00 2001 From: Manuel de Brito Fontes Date: Mon, 2 Nov 2026 21:03:08 -0300 Subject: [PATCH 1/4] release: prune the cache before the builds, not after A recipe renamed since the cache was stored left its old packages in it: gst-plugins-bad 1.29.2 beside gst-plugins-bad-1.28 1.28.8, whose provides it outranks, failed snapshot's build on the builder and nowhere else. The cache was pruned only at the end of a release that had already succeeded, so that release could never come. Prune after the pull, which also writes the index with the builder's own key. Co-Authored-By: Claude Opus 5.5 --- Taskfile.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index 9083b8b..c4584c1 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -480,6 +480,10 @@ tasks: - go run ./cmd/image cache pull # The cache goes back however the job ends: what a failed release built is not built again. - defer: go run ./cmd/image cache push + # Every build no recipe makes goes first, and the index with it: a recipe renamed since the + # cache was stored leaves an older package of the name others ask for, or of a higher version, + # and the build that resolves to it fails. + - task: packages:prune - task: release:inputs # The harness on a test build of the same commit, its roots built from the locks: a release # whose checks fail is not built, and the release's stick is made from the same roots. @@ -490,8 +494,6 @@ tasks: - task: scan - task: release:publish vars: {VERSION: '{{.VERSION}}', NEW: '{{.NEW}}'} - # Every build no recipe makes goes before the cache takes packages/ back (the deferred push). - - task: packages:prune packages:prune: desc: "Remove every apk in packages/ that no recipe makes now, and index again: old versions, and a build of a recipe never committed, which apko would take over the recipe's own." From 3b944d6d4c3394b77110ee0cba5fb19bdca0fee6 Mon Sep 17 00:00:00 2001 From: Manuel de Brito Fontes Date: Mon, 2 Nov 2026 21:21:53 -0300 Subject: [PATCH 2/4] kernel: erofs does not log a killed reader's read as an error A process killed while a block it needs is being read gets -EINTR, which erofs logged as a filesystem error. It failed test:usb's journal check and showed in GNOME Logs, about 3 runs in 10, with nothing wrong: corruption is -EIO, and panics under dm-verity here. Real errors keep their message. Co-Authored-By: Claude Opus 5.5 --- ...2-erofs-interrupted-read-is-no-error.patch | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 kernel/patches/0012-erofs-interrupted-read-is-no-error.patch diff --git a/kernel/patches/0012-erofs-interrupted-read-is-no-error.patch b/kernel/patches/0012-erofs-interrupted-read-is-no-error.patch new file mode 100644 index 0000000..23265ab --- /dev/null +++ b/kernel/patches/0012-erofs-interrupted-read-is-no-error.patch @@ -0,0 +1,49 @@ +construct: not upstream yet, to be sent to linux-erofs. A read of a block not yet in the page cache +waits killably, so a process killed meanwhile gets -EINTR, and erofs logged that as a filesystem +error: at every app that exits while its page faults are in flight (VS Code closing, about 3 harness +runs in 10, on the journal and in GNOME Logs). The filesystem is fine, and dm-verity would have said +otherwise: corruption is -EIO, and panics here. The other errors keep their message. + +Subject: [PATCH] erofs: Do not log a read interrupted by a fatal signal as an error + +--- a/fs/erofs/dir.c ++++ b/fs/erofs/dir.c +@@ -83,9 +83,10 @@ + + de = erofs_bread(&buf, dbstart, true); + if (IS_ERR(de)) { +- erofs_err(sb, "failed to readdir of logical block %llu of nid %llu", +- erofs_blknr(sb, dbstart), EROFS_I(dir)->nid); + err = PTR_ERR(de); ++ if (err != -EINTR) ++ erofs_err(sb, "failed to readdir of logical block %llu of nid %llu", ++ erofs_blknr(sb, dbstart), EROFS_I(dir)->nid); + break; + } + +--- a/fs/erofs/inode.c ++++ b/fs/erofs/inode.c +@@ -52,8 +52,9 @@ + ptr = erofs_read_metabuf(&buf, sb, erofs_pos(sb, blkaddr), in_mbox); + if (IS_ERR(ptr)) { + err = PTR_ERR(ptr); +- erofs_err(sb, "failed to read inode meta block (nid: %llu): %d", +- vi->nid, err); ++ if (err != -EINTR) ++ erofs_err(sb, "failed to read inode meta block (nid: %llu): %d", ++ vi->nid, err); + goto err_out; + } + +@@ -91,8 +92,9 @@ + erofs_pos(sb, blkaddr + 1), in_mbox); + if (IS_ERR(ptr)) { + err = PTR_ERR(ptr); +- erofs_err(sb, "failed to read inode payload block (nid: %llu): %d", +- vi->nid, err); ++ if (err != -EINTR) ++ erofs_err(sb, "failed to read inode payload block (nid: %llu): %d", ++ vi->nid, err); + goto err_out; + } + ofs = vi->inode_isize - gotten; From df3b9a80eae6a3b50979a611731d5cafbc314f19 Mon Sep 17 00:00:00 2001 From: Manuel de Brito Fontes Date: Mon, 2 Nov 2026 21:21:56 -0300 Subject: [PATCH 3/4] harness: dbx current waits for LVFS's metadata It ran early enough to pass with no metadata, when fwupd offers nothing whatever the dbx, which is the case it exists to catch. Co-Authored-By: Claude Opus 5.5 --- cmd/image/test.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/cmd/image/test.go b/cmd/image/test.go index abfe45c..b979f7f 100644 --- a/cmd/image/test.go +++ b/cmd/image/test.go @@ -151,6 +151,7 @@ func test(args []string) (err error) { r.add("the login keyring is open", strings.TrimSpace(keyring) == "kept", oneLine(keyring)) if *stick { r.secureBoot(g) + r.dbxCurrent(g) r.efiPrograms(g) r.loaderCache(g) } @@ -944,11 +945,18 @@ func keyringRoundTrip(user string, uid int) string { func (r *report) secureBoot(g *guest) { out, _ := g.run(`bootctl status 2>/dev/null | grep -E 'Secure Boot:'; cat /sys/kernel/security/lockdown`, "") r.add("secure boot", strings.Contains(out, "Secure Boot: enabled (user)") && strings.Contains(out, "[integrity]"), oneLine(out)) - // The stick's dbx (espTree), and fwupd finding it current. A newer dbx from Microsoft fails this at - // once, rather than as a notification over the views on the runs GNOME Software is quick enough: - // build/dbx.auth moves, to the .bin of LVFS's newest com.microsoft.dbx.x64-kek2023.firmware cabinet. - dbx, _ := g.run(`test -s /sys/firmware/efi/efivars/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f && echo enrolled; fwupdmgr get-updates --no-unreported-check --json 2>&1 | grep -c '"UEFI dbx"'`, "") - r.add("dbx current", strings.Contains(dbx, "enrolled") && strings.TrimSpace(lastLine(dbx)) == "0", oneLine(dbx)) +} + +// dbxCurrent checks the stick's dbx (espTree) is the one fwupd finds current. A newer dbx from +// Microsoft fails this at once, rather than as a notification over the views on the runs GNOME +// Software is quick enough: build/dbx.auth moves, to the .bin of LVFS's newest +// com.microsoft.dbx.x64-kek2023.firmware cabinet. It waits for LVFS's metadata, of megabytes where a +// machine's own remotes' are not: without it fwupd offers nothing, whatever the dbx. +func (r *report) dbxCurrent(g *guest) { + out, _ := g.run(`test -s /sys/firmware/efi/efivars/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f && echo enrolled; `+ + `for i in $(seq 180); do test -n "$(find /var/cache/fwupd/metadata.xmlb -size +1M 2>/dev/null)" && { echo metadata; break; }; sleep 1; done; `+ + `fwupdmgr get-updates --no-unreported-check --json 2>&1 | grep -c '"UEFI dbx"'`, "") + r.add("dbx current", strings.Contains(out, "enrolled") && strings.Contains(out, "metadata") && strings.TrimSpace(lastLine(out)) == "0", oneLine(out)) } // efiPrograms checks the root's EFI programs are the signed ones the firmware runs: systemd-boot on From 42697aea2aef41c01e8714b9a6553cc425e69362 Mon Sep 17 00:00:00 2001 From: Manuel de Brito Fontes Date: Mon, 2 Nov 2026 21:21:56 -0300 Subject: [PATCH 4/4] kernel: a kernel newer than Arch's is built over Arch's newest Construct adopts 7.3 before Arch has a tag for it. kernel/Dockerfile already says what that leaves out and when the base moves; the rule and the pin's comment said the same version, which is not always so. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 3 ++- build/versions.env | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 62c8053..9482cc0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -148,7 +148,8 @@ falling back from a release that does not come up. ## The kernel and firmware -- The base is Arch Linux's configuration for the same kernel (`kernel/Dockerfile`), with general-PC +- The base is Arch Linux's configuration for the same kernel, or its newest tag's while Arch has none + for a kernel we adopt first (`kernel/Dockerfile` says what that leaves out), with general-PC drivers as modules unless the supported-hardware policy and measured image or boot budgets say otherwise. Adopt an Arch kernel tag after its configuration assertions and relevant compatibility and performance checks pass; availability alone is not a release criterion. diff --git a/build/versions.env b/build/versions.env index d070883..7dd4c56 100644 --- a/build/versions.env +++ b/build/versions.env @@ -6,7 +6,7 @@ WOLFI_BASE_IMAGE=cgr.dev/chainguard/wolfi-base:latest@sha256:88a20e2d017bd20cdc0cb3910de603e7bea9512a6c351a8dd3c9df212ab03d73 # The kernel, from kernel.org. A new series can fail the fragments' assertions; Arch's base config -# (kernel/Dockerfile) is pinned to the same version. +# (kernel/Dockerfile) is pinned to the same version, or Arch's newest while it has none for this one. KERNEL_VERSION=7.3.1 KERNEL_SHA256=fa595aad93734f829c963c2a90ef7c3673224fd7fdae7b49406e397cde2de231