Files
c630/build/build-image.sh
rob thijssen 280874f564 Build Fedora aarch64 images for the Lenovo Yoga C630
Assembles a ready-to-write disk image via Gitea Actions. Mainline has carried
sdm850-lenovo-yoga-c630.dts since 5.5 and Fedora ships it in kernel-core, so
unlike aarch64-laptops/build there is no kernel or GRUB to compile — what is
left is producing an image that boots on firmware which hands Linux no device
tree.

The build runs in an aarch64 container under qemu-user and builds filesystems
from directory trees with mke2fs -d and mcopy rather than mounting loop
devices, so it works on runners that will not hand out /dev/loop-control.

A kernel-install hook writes the devicetree line into each BLS entry; without
it the first `dnf update kernel` would produce an unbootable system.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XWRjNJMistCy6ngXH5aJLS
2026-07-27 11:24:53 +03:00

117 lines
4.1 KiB
Bash
Executable File

#!/usr/bin/bash
#
# Host-side driver. Runs on an x86_64 CI runner (or your workstation) and does
# the real work inside an aarch64 Fedora container under qemu-user emulation.
#
# ./build/build-image.sh --variant minimal
#
# Everything arch-specific happens in build/stage2.sh, which runs inside that
# container. This script's only jobs are checking that emulation is wired up
# and handing the container the right mounts.
set -euo pipefail
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$REPO_DIR"
VARIANT=minimal
OUTPUT_DIR="$REPO_DIR/output"
CONTAINER_IMAGE=""
KEEP_ROOTFS=0
usage() {
cat <<EOF
Usage: $0 [options]
--variant NAME Package variant from config/packages/ (default: minimal)
--output DIR Where to write the image (default: ./output)
--size MIB Image size in MiB (default: from config/device.env)
--image REF Build container image (default: registry.fedoraproject.org/fedora:\$FEDORA_RELEASE)
--keep-rootfs Leave the staged rootfs behind for inspection
-h, --help This message
EOF
}
while [ $# -gt 0 ]; do
case "$1" in
--variant) VARIANT="$2"; shift 2 ;;
--output) OUTPUT_DIR="$2"; shift 2 ;;
--size) export IMAGE_SIZE_MIB="$2"; shift 2 ;;
--image) CONTAINER_IMAGE="$2"; shift 2 ;;
--keep-rootfs) KEEP_ROOTFS=1; shift ;;
-h|--help) usage; exit 0 ;;
*) echo "unknown option: $1" >&2; usage >&2; exit 2 ;;
esac
done
# shellcheck source=../config/device.env
source "$REPO_DIR/config/device.env"
: "${CONTAINER_IMAGE:=registry.fedoraproject.org/fedora:${FEDORA_RELEASE}}"
if [ ! -f "$REPO_DIR/config/packages/${VARIANT}.pkgs" ]; then
echo "no such variant: ${VARIANT}" >&2
echo "available: $(cd "$REPO_DIR/config/packages" && ls *.pkgs | sed 's/\.pkgs$//' | grep -v '^base$' | tr '\n' ' ')" >&2
exit 2
fi
# --- emulation check ----------------------------------------------------
#
# Building an aarch64 rootfs means running aarch64 rpm scriptlets, which needs
# a binfmt_misc handler registered in the *host* kernel. A container cannot
# provide that for itself.
if [ "$(uname -m)" != "$TARGET_ARCH" ]; then
handler=/proc/sys/fs/binfmt_misc/qemu-aarch64
if [ ! -e "$handler" ]; then
cat >&2 <<EOF
error: no binfmt_misc handler for aarch64.
This host is $(uname -m), so building an ${TARGET_ARCH} image needs qemu-user
emulation registered with the kernel. On a Fedora runner, one-time setup:
sudo dnf install -y qemu-user-static-aarch64
sudo systemctl restart systemd-binfmt
See docs/runner-setup.md.
EOF
exit 1
fi
# The handler must be flagged F (fix binary), or the interpreter is looked
# up inside the container's mount namespace, where it does not exist.
if ! grep -q '^flags:.*F' "$handler"; then
echo "error: $handler is registered without the 'F' flag; the qemu" >&2
echo " interpreter will not be visible inside the container." >&2
echo " Install qemu-user-static-aarch64 rather than qemu-user." >&2
exit 1
fi
fi
command -v podman >/dev/null || { echo "error: podman not found" >&2; exit 1; }
mkdir -p "$OUTPUT_DIR"
BUILD_REF="$(git -C "$REPO_DIR" rev-parse --short HEAD 2>/dev/null || echo unknown)"
BUILD_DATE="$(date -u +%Y-%m-%d)"
echo "==> variant=${VARIANT} release=${FEDORA_RELEASE} arch=${TARGET_ARCH} ref=${BUILD_REF}"
echo "==> build container: ${CONTAINER_IMAGE}"
# --privileged is what lets stage2 bind-mount /proc and /sys into the staged
# rootfs so dracut can run in a chroot. Rootless podman grants only the caps
# the invoking user already has inside their user namespace, so this is not
# the escalation it looks like.
exec podman run --rm \
--arch arm64 \
--privileged \
--security-opt label=disable \
-v "$REPO_DIR:/src:ro" \
-v "$OUTPUT_DIR:/out" \
-e VARIANT="$VARIANT" \
-e FEDORA_RELEASE="$FEDORA_RELEASE" \
-e IMAGE_SIZE_MIB="${IMAGE_SIZE_MIB}" \
-e BUILD_REF="$BUILD_REF" \
-e BUILD_DATE="$BUILD_DATE" \
-e KEEP_ROOTFS="$KEEP_ROOTFS" \
"$CONTAINER_IMAGE" \
/bin/bash /src/build/stage2.sh