From b8cd2966f4649db5ae766970fdb654d64db21772 Mon Sep 17 00:00:00 2001 From: Lukas Greve Date: Wed, 23 Sep 2026 12:46:08 +0200 Subject: [PATCH] build-image: serialize host-wide with flock, clean up stale mounts/devices Gitea runs each matrix entry as its own task, so build-image.yaml's `max-parallel: 1` never serialized the two editions -- both builds started at once on the fedora:host runner and corrupted each other's global anaconda/livemedia-creator state (/mnt/sysroot, /mnt/sysimage, loop and device-mapper devices), surfacing as bootctl failing with "Couldn't find EFI system partition" even though the dish's EFI partition was fine. build-image.sh now takes a host-wide flock around livemedia-creator, and under the lock first clears any stale mount/dm/loop device a previously failed build left behind. Verified on the runner: both editions launched concurrently now run serially under the lock and build successfully. Co-Authored-By: Claude Sonnet 5 --- .gitea/workflows/build-image.yaml | 4 ++-- build-image.sh | 28 +++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/build-image.yaml b/.gitea/workflows/build-image.yaml index 2cf3925..760304f 100644 --- a/.gitea/workflows/build-image.yaml +++ b/.gitea/workflows/build-image.yaml @@ -42,8 +42,8 @@ jobs: runs-on: fedora strategy: fail-fast: false - # One image at a time: each build takes loop devices, RAM and tens of GiB - max-parallel: 1 + # NB: Gitea runs every matrix entry as its own task, so `max-parallel` + # does not serialize them; build-image.sh takes a host-wide flock instead. matrix: include: - edition: phyllomeos diff --git a/build-image.sh b/build-image.sh index e37ad67..34d574c 100755 --- a/build-image.sh +++ b/build-image.sh @@ -175,8 +175,34 @@ fi resultdir="$(mktemp -u "${TMPDIR:-/tmp}/lmc-result.XXXXXX")" +# Only one livemedia-creator run per host at a time. It and anaconda use +# host-global resources (/mnt/sysroot, /mnt/sysimage, loop and device-mapper +# devices), so concurrent builds corrupt each other -- e.g. bootctl failing with +# "Couldn't find EFI system partition" when the CI matrix started both editions +# at once on a shared runner (Gitea expands each matrix entry into its own +# task, so `max-parallel` does not serialize them). A host-wide flock does. +# Under the lock, first clear mounts/devices a previously failed build left +# behind, which would otherwise poison this one. +LOCK=/run/lock/phyllomeos-build-image.lock echo "Building disk image for '$dish' (target release $releasever)..." -sudo livemedia-creator --ks "$disk_ks" --no-virt --resultdir "$resultdir" \ +echo "Waiting for the host build lock ($LOCK) if another build is running..." +sudo flock "$LOCK" env DISH="$dish" bash -c ' + for mnt in /mnt/sysimage /mnt/sysroot; do + if findmnt -rn "$mnt" >/dev/null 2>&1; then + echo "Cleaning stale mount $mnt from a failed build" + umount -R "$mnt" || true + fi + done + for dm in $(dmsetup ls 2>/dev/null | awk -v d="$DISH" "index(\$1, d) == 1 {print \$1}" | sort -r); do + echo "Removing stale device-mapper device $dm" + dmsetup remove --retry "$dm" || true + done + for lo in $(losetup -a | awk -F: "/lmc-result\\./ {print \$1}"); do + echo "Detaching stale loop device $lo" + losetup -d "$lo" || true + done + exec livemedia-creator "$@" +' _ --ks "$disk_ks" --no-virt --resultdir "$resultdir" \ --make-disk --image-name "${dish}.img" --releasever "$releasever" # livemedia-creator's exact output filename depends on the installed lorax