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
117 lines
4.1 KiB
Bash
Executable File
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
|