fix(ci): ensure /dev/fd symlinks exist for mkinitcpio and procsub
Tests / tests (push) Successful in 2m33s

The chroot's /dev only gains fd/stdin/stdout/stderr symlinks at boot, and
container executors often lack them, which breaks mkinitcpio's preset step
(pipes through /dev/stdin) and bash process substitution
("/dev/fd/63: No such file or directory").

- build-iso workflow: create the links in the executor's /dev, then mount a
  fresh devtmpfs and create them there too so the chroot tree is covered
  whether the executor's /dev is devtmpfs or a plain tmpfs
- customize_airootfs.sh: self-heal the same links inside the chroot
This commit is contained in:
alvinlollo
2026-10-06 14:53:16 +11:00
parent c64c64dc97
commit a6c99b72d0
2 changed files with 41 additions and 5 deletions
+28 -5
View File
@@ -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
# <chroot>/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: |