diff --git a/.gitea/workflows/build-iso.yaml b/.gitea/workflows/build-iso.yaml index e9d67a6..fb9c592 100644 --- a/.gitea/workflows/build-iso.yaml +++ b/.gitea/workflows/build-iso.yaml @@ -43,13 +43,36 @@ jobs: set -euo pipefail pacman -Syu --noconfirm --needed archiso jq - - name: Ensure /dev/stdin exists for mkinitcpio + - name: Ensure /dev/fd symlinks exist for mkinitcpio and procsub run: | set -euo pipefail - # Docker/container environments may lack /dev/stdin; mkinitcpio needs it - [ -e /dev/stdin ] || ln -sf /proc/self/fd/0 /dev/stdin - [ -e /dev/stdout ] || ln -sf /proc/self/fd/1 /dev/stdout - [ -e /dev/stderr ] || ln -sf /proc/self/fd/2 /dev/stderr + # mkinitcpio's preset step pipes through /dev/stdin and bash process + # substitution (< <(...)) uses /dev/fd; both break if these symlinks + # are missing (they are normally created once at boot by mkinitcpio's + # init). Two places need them, so cover both: + # 1. the executor's own /dev: mkarchiso reads /dev/stdin on the host + # (machine-id, version, pkglist) and the workflow's own scripts use + # process substitution here. A no-op if the links already exist. + ln -sfn /proc/self/fd /dev/fd + ln -sfn /proc/self/fd/0 /dev/stdin + ln -sfn /proc/self/fd/1 /dev/stdout + ln -sfn /proc/self/fd/2 /dev/stderr + # 2. the chroot's /dev: devtmpfs is single-instance, so mounting it + # here yields the same tree that pacstrap/arch-chroot expose as + # /dev. When the executor's /dev is itself devtmpfs (1) + # already fixed that tree; this pass is what makes it work when + # the executor's /dev is a plain tmpfs or directory instead. + d="$(mktemp -d)" + trap 'umount "$d" 2>/dev/null || true; rmdir "$d" 2>/dev/null || true' EXIT + mount -t devtmpfs devtmpfs "$d" + ln -sfn /proc/self/fd "$d/fd" + ln -sfn /proc/self/fd/0 "$d/stdin" + ln -sfn /proc/self/fd/1 "$d/stdout" + ln -sfn /proc/self/fd/2 "$d/stderr" + ls -l "$d/fd" "$d/stdin" "$d/stdout" "$d/stderr" + umount "$d" + rmdir "$d" + trap - EXIT - name: Build Arch ISO run: | diff --git a/airootfs/root/customize_airootfs.sh b/airootfs/root/customize_airootfs.sh index 77e2b3f..24e3ab4 100644 --- a/airootfs/root/customize_airootfs.sh +++ b/airootfs/root/customize_airootfs.sh @@ -1,6 +1,19 @@ #!/usr/bin/env bash set -euo pipefail +# The chroot's /dev is the kernel's single shared devtmpfs, which only gets the +# fd/stdin/stdout/stderr symlinks at boot (from mkinitcpio's init). If the +# runner environment lacks them, process substitution (< <(...)) and upstream +# tools (pacman-key) fail with "… /dev/fd/63: No such file or directory". +# Self-heal here; the CI build-iso workflow does the same before pacstrap so +# that mkinitcpio's hook (which pipes through /dev/stdin) works too. +for _link in "fd:/proc/self/fd" "stdin:/proc/self/fd/0" \ + "stdout:/proc/self/fd/1" "stderr:/proc/self/fd/2"; do + [[ -e "/dev/${_link%%:*}" || -L "/dev/${_link%%:*}" ]] || + ln -sfn "${_link#*:}" "/dev/${_link%%:*}" +done +unset _link + # Populate the keyring in the built root from the keyring bundles # (/usr/share/pacman/keyrings, incl. the copied cachyos keyring). # This mirrors the guide; it runs inside the image you are making.