From 0b67dbab8ee081a85c424685b61114806cc68918 Mon Sep 17 00:00:00 2001 From: Ryan Hughes Date: Fri, 4 Sep 2026 23:40:31 -0400 Subject: [PATCH] Harden emulated ARM builds --- README.md | 8 ++++--- bin/build | 18 ++++++++++++-- build/build.sh | 50 +++++++++++++++++++++++++++------------ helpers/docker-helpers.sh | 18 +++++++++++--- 4 files changed, 71 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index c5338a0a..c34f0047 100644 --- a/README.md +++ b/README.md @@ -32,12 +32,14 @@ The filesystem no longer encodes release policy. Instead: The repository host builds every architecture it publishes on the same machine. A foreign architecture runs under QEMU user emulation, which -`bin/build` sets up on first use (and re-checks after each reboot by -actually running a container for the target platform): +`bin/build` checks by actually running a container for the target platform. +Rootful Docker registers QEMU on first use. Rootless Podman uses the host's +registration and prints the one-time Arch setup commands when it is missing or +lacks the credential flag required by `sudo` inside the builder: ```bash # Verify -docker run --rm --platform linux/arm64 alpine:latest uname -m +podman run --rm --platform linux/arm64 docker.io/library/alpine:latest uname -m # Should output: aarch64 ``` diff --git a/bin/build b/bin/build index 7e6ae58d..c2d001c9 100755 --- a/bin/build +++ b/bin/build @@ -174,10 +174,24 @@ HOST_ARCH=$(uname -m) if [[ "$HOST_ARCH" != "$ARCH" ]]; then PROBE_IMAGE="alpine:3.21" [[ "$CONTAINER_ENGINE" == "podman" ]] && PROBE_IMAGE="docker.io/library/alpine:3.21" - if ! "$CONTAINER_ENGINE" run "${CONTAINER_RUN_ARGS[@]}" --rm "$(get_platform_arg "$ARCH")" "$PROBE_IMAGE" /bin/true >/dev/null 2>&1; then - print_info "Setting up QEMU for $ARCH emulation on this $HOST_ARCH host..." + + # Rootless Podman cannot repair host binfmt state itself. Validate the flags + # before the basic probe, because an F-only registration can start an ARM + # container but silently breaks sudo inside it. + if [[ "$CONTAINER_ENGINE" == "podman" ]]; then setup_qemu "$ARCH" fi + + if ! "$CONTAINER_ENGINE" run "${CONTAINER_RUN_ARGS[@]}" --rm "$(get_platform_arg "$ARCH")" "$PROBE_IMAGE" /bin/true >/dev/null 2>&1; then + if [[ "$CONTAINER_ENGINE" == "podman" ]]; then + print_error "QEMU $ARCH is registered, but the container probe failed" + print_info "Refresh the registration with: sudo systemctl restart systemd-binfmt" + exit 1 + else + print_info "Setting up QEMU for $ARCH emulation on this $HOST_ARCH host..." + setup_qemu "$ARCH" + fi + fi fi # Clean build-output directory to start fresh, unless the caller seeded it diff --git a/build/build.sh b/build/build.sh index 8534ee9c..9357ebd4 100755 --- a/build/build.sh +++ b/build/build.sh @@ -5,6 +5,10 @@ # Setup directories ARCH=${ARCH:-x86_64} +# ARCH selects the repository target for this script, but make and Kbuild also +# interpret an exported ARCH themselves (Linux calls this target "arm64"). +# Keep the shell variable local to the orchestrator so PKGBUILDs see CARCH only. +export -n ARCH MIRROR=${MIRROR:-edge} DRY_RUN=${DRY_RUN:-false} PKGBUILDS_DIR=${PKGBUILDS_DIR:-/pkgbuilds} @@ -60,14 +64,11 @@ if [[ "$DRY_RUN" != true ]]; then # Configure Omarchy repositories for dependency resolution echo "==> Configuring Omarchy repositories for dependency resolution..." - # Always add omarchy-build repo (for incremental builds) - # Packages in build-output are unsigned, so use SigLevel = Never - sudo tee -a /etc/pacman.conf > /dev/null < omarchy-build (priority 1): $BUILD_OUTPUT_DIR" # Initialize empty build database if it doesn't exist @@ -90,12 +91,7 @@ EOF # Add omarchy repo if it has a database (stable packages) if [[ -f "$FINAL_OUTPUT_DIR/omarchy.db.tar.zst" ]] || [[ -f "$FINAL_OUTPUT_DIR/omarchy.db" ]]; then - sudo tee -a /etc/pacman.conf > /dev/null < omarchy (priority 2): $FINAL_OUTPUT_DIR" fi @@ -336,8 +332,14 @@ build_package() { # Ensure output directory exists mkdir -p "$BUILD_OUTPUT_DIR" + local dependency_pkg_file="" for pkg_file in *.pkg.tar.*; do - [[ -f "$pkg_file" ]] && cp "$pkg_file" "$BUILD_OUTPUT_DIR/" + [[ -f "$pkg_file" && "$pkg_file" != *.sig ]] || continue + cp "$pkg_file" "$BUILD_OUTPUT_DIR/" + + if [[ "$(bsdtar -xOf "$pkg_file" .PKGINFO 2>/dev/null | sed -n 's/^pkgname = //p')" == "$pkg" ]]; then + dependency_pkg_file="$BUILD_OUTPUT_DIR/$pkg_file" + fi done cd "$BUILD_OUTPUT_DIR" @@ -353,6 +355,24 @@ build_package() { cd /src/$pkg + # A lower-priority official repository may contain an older package with + # the same name. Install the exact artifact we just built before building + # its consumers, so pacman cannot select that older provider instead. + if [[ "${INSTALL_PACKAGES[$pkg]:-}" == "1" ]]; then + if [[ -z "$dependency_pkg_file" ]]; then + echo " Could not find the built $pkg package to install as a dependency" + FAILED_PACKAGES="$FAILED_PACKAGES $pkg" + return 1 + fi + + echo " Installing freshly built $pkg for dependent packages..." + if ! sudo /usr/local/bin/pacman-for-makepkg -U --needed --noconfirm "$dependency_pkg_file"; then + echo " Failed to install freshly built dependency $pkg" + FAILED_PACKAGES="$FAILED_PACKAGES $pkg" + return 1 + fi + fi + echo " Successfully built $pkg" SUCCESSFUL_PACKAGES="$SUCCESSFUL_PACKAGES $pkg" return 0 diff --git a/helpers/docker-helpers.sh b/helpers/docker-helpers.sh index 3ec61974..cc18222a 100644 --- a/helpers/docker-helpers.sh +++ b/helpers/docker-helpers.sh @@ -69,13 +69,25 @@ setup_qemu() { if [[ "$CONTAINER_ENGINE" == "podman" ]]; then local registration="/proc/sys/fs/binfmt_misc/qemu-$target_arch" - if [[ -r "$registration" ]] && grep -q '^flags:.*F' "$registration"; then + local packaged_registration="/usr/lib/binfmt.d/qemu-$target_arch-static.conf" + local flags="" + + if [[ -r "$registration" ]]; then + flags=$(sed -n 's/^flags: //p' "$registration") + fi + + if [[ "$flags" == *F* && "$flags" == *C* ]]; then print_success "QEMU $target_arch emulation is registered" return 0 fi - print_error "Rootless Podman requires host QEMU binfmt registration with the F flag" - print_info "Install it with: sudo pacman -S qemu-user-static qemu-user-static-binfmt" + print_error "Rootless Podman requires QEMU binfmt registration with the F and C flags" + print_info "F keeps the emulator available inside containers; C lets container sudo preserve credentials." + print_info "Configure it once with:" + echo " sudo pacman -S --needed qemu-user-static qemu-user-static-binfmt" + echo " sudo mkdir -p /etc/binfmt.d" + echo " sed 's/:FP$/:FPC/' $packaged_registration | sudo tee /etc/binfmt.d/qemu-$target_arch-static.conf >/dev/null" + echo " sudo systemctl restart systemd-binfmt" exit 1 fi