Files
2026-04-28 20:57:45 +02:00

182 lines
6.1 KiB
Bash
Executable File

#!/bin/bash
set -ex
DISTRO="${DISTRO:-ubuntu2604}"
IMG_SIZE="${IMG_SIZE:-12000}"
SKIP_CHROOT="${SKIP_CHROOT:-false}"
STAGING="/tmp/build-staging"
ROOT_LABEL="${DISTRO}"
EFI_LABEL="boot"
CHROOT="/build/chroot"
IMG="/output/ps5-${DISTRO}.img"
if [ "$SKIP_CHROOT" = "true" ] && [ -d "$CHROOT/bin" ]; then
echo "=== Reusing cached $DISTRO rootfs ==="
else
echo "=== Building $DISTRO rootfs ==="
# --- Stage files for distrobuilder's copy generators ---
rm -rf "$STAGING"
mkdir -p "$STAGING/debs"
cp /repo/distros/shared/zz-update-boot "$STAGING/"
# Generate per-distro fstab with partition labels
cat <<EOF > "$STAGING/fstab"
# /etc/fstab: static file system information.
#
# <file system> <mount point> <type> <options> <dump> <pass>
LABEL=$ROOT_LABEL / ext4 defaults 0 1
LABEL=$EFI_LABEL /boot/efi vfat defaults 0 1
EOF
cp /repo/distros/${DISTRO}/grow-rootfs "$STAGING/"
cp /repo/distros/${DISTRO}/nm-dns.conf "$STAGING/" 2>/dev/null || true
case "$DISTRO" in
ubuntu*)
cp /repo/distros/${DISTRO}/grow-rootfs.service "$STAGING/"
cp /kernel-debs/*.deb "$STAGING/debs/"
;;
alpine)
cp /repo/distros/alpine/grow-rootfs.openrc "$STAGING/"
;;
arch)
cp /repo/distros/arch/grow-rootfs.service "$STAGING/"
cp /repo/distros/arch/first-boot-setup "$STAGING/"
mkdir -p "$STAGING/pkgs"
cp /kernel-debs/*.pkg.tar.zst "$STAGING/pkgs/"
;;
esac
# --- Build rootfs ---
rm -rf "$CHROOT"/* "$CHROOT"/.[!.]* 2>/dev/null || true
YAML="/repo/distros/${DISTRO}/image.yaml"
distrobuilder build-dir "$YAML" "$CHROOT" --with-post-files --cache-dir /build/cache --cleanup=false
fi
# --- Post-distrobuilder fixups ---
case "$DISTRO" in
ubuntu*)
rm -f "$CHROOT/etc/resolv.conf"
ln -sf /run/systemd/resolve/stub-resolv.conf "$CHROOT/etc/resolv.conf"
;;
esac
# --- Alpine kernel gap: no kernel installed via image.yaml ---
# Extract kernel from .deb, copy modules + bzImage, then chroot to run mkinitfs
if [ "$DISTRO" = "alpine" ]; then
echo "=== Alpine: installing kernel from .deb artifacts ==="
ALPINE_STAGING="/tmp/alpine-kernel-staging"
rm -rf "$ALPINE_STAGING"
mkdir -p "$ALPINE_STAGING"
for deb in /kernel-debs/linux-image-*.deb; do
[ -f "$deb" ] || continue
dpkg-deb -x "$deb" "$ALPINE_STAGING"
done
KVER=$(ls -1 "$ALPINE_STAGING/lib/modules" 2>/dev/null | head -1)
if [ -n "$KVER" ]; then
# Resolve the real modules path (Alpine may use usr-merge: /lib -> usr/lib)
if [ -L "$CHROOT/lib" ]; then
MODDIR="$CHROOT/usr/lib/modules"
else
MODDIR="$CHROOT/lib/modules"
fi
mkdir -p "$MODDIR"
rm -rf "$MODDIR/$KVER"
cp -a "$ALPINE_STAGING/lib/modules/$KVER" "$MODDIR/"
mkdir -p "$CHROOT/boot"
cp "$ALPINE_STAGING/boot/vmlinuz-$KVER" "$CHROOT/boot/vmlinuz-$KVER"
echo ">> Alpine: modules copied to $MODDIR/$KVER"
fi
rm -rf "$ALPINE_STAGING"
if [ -n "$KVER" ]; then
chroot "$CHROOT" depmod -a "$KVER" 2>/dev/null || true
mount --bind /dev "$CHROOT/dev"
mount --bind /proc "$CHROOT/proc"
mount --bind /sys "$CHROOT/sys"
chroot "$CHROOT" mkinitfs -k "$KVER" -o "/boot/initrd.img-$KVER" "$KVER" || true
umount "$CHROOT/sys" "$CHROOT/proc" "$CHROOT/dev"
# Populate /boot/efi/ for boot partition assembly
mkdir -p "$CHROOT/boot/efi"
cp "$CHROOT/boot/vmlinuz-$KVER" "$CHROOT/boot/efi/bzImage"
if [ -f "$CHROOT/boot/initrd.img-$KVER" ]; then
cp "$CHROOT/boot/initrd.img-$KVER" "$CHROOT/boot/efi/initrd.img"
else
INITRD=$(ls -1t "$CHROOT"/boot/initramfs-* "$CHROOT"/boot/initrd* 2>/dev/null | head -1)
if [ -n "$INITRD" ]; then
cp "$INITRD" "$CHROOT/boot/efi/initrd.img"
else
echo "WARNING: No initrd found for alpine after mkinitfs"
fi
fi
echo ">> Alpine: kernel $KVER staged to boot/efi/"
else
echo "WARNING: No kernel modules found in .deb for alpine"
fi
fi
# --- Create GPT disk image ---
echo "=== Creating ${IMG_SIZE}MB disk image ==="
TMPIMG="/build/ps5-${DISTRO}.img"
dd if=/dev/zero of="$TMPIMG" bs=1M count=$IMG_SIZE conv=fsync status=progress
parted -s "$TMPIMG" mklabel gpt
parted -s "$TMPIMG" mkpart primary ext4 500MiB 100%
parted -s "$TMPIMG" mkpart primary fat32 1MiB 500MiB
parted -s "$TMPIMG" set 2 esp on
# Ensure the free loop device node exists (udev doesn't run inside containers,
# so when the kernel allocates a new loop number it may lack a /dev node)
LOOP_PATH=$(losetup -f)
if [ ! -e "$LOOP_PATH" ]; then
LOOP_NUM=${LOOP_PATH#/dev/loop}
mknod "$LOOP_PATH" b 7 "$LOOP_NUM"
fi
LOOPDEV=$(losetup -f --show "$TMPIMG")
# Use kpartx to create partition device mappings (more reliable in containers)
kpartx -av "$LOOPDEV"
sleep 1
# kpartx creates /dev/mapper/loopXp1, /dev/mapper/loopXp2
LOOP_BASE=$(basename "$LOOPDEV")
PART1="/dev/mapper/${LOOP_BASE}p1"
PART2="/dev/mapper/${LOOP_BASE}p2"
echo "=== Formatting partitions ==="
mkfs.ext4 -L "$ROOT_LABEL" -m 1 "$PART1"
mkfs.vfat -n "$EFI_LABEL" -F32 "$PART2"
mkdir -p /tmp/usb_root /tmp/usb_efi
mount "$PART1" /tmp/usb_root
mount "$PART2" /tmp/usb_efi
echo "=== Copying rootfs to image ==="
cp -a "$CHROOT"/* /tmp/usb_root/
sync
echo "=== Assembling boot partition ==="
mv /tmp/usb_root/boot/efi/* /tmp/usb_efi/ 2>/dev/null || true
sed "s|__DISTRO__|$ROOT_LABEL|" /repo/boot/cmdline.txt > /tmp/usb_efi/cmdline.txt
cp /repo/boot/vram.txt /tmp/usb_efi/
cp /repo/boot/kexec.sh /tmp/usb_efi/
sync
umount /tmp/usb_root /tmp/usb_efi
rmdir /tmp/usb_root /tmp/usb_efi
kpartx -dv "$LOOPDEV"
losetup -d "$LOOPDEV"
# Move finished image to output volume
mv "$TMPIMG" "$IMG"
sync
echo "========================================"
echo "Done! $IMG (${IMG_SIZE}MB)"
echo "Flash: sudo dd if=$IMG of=/dev/sdX bs=4M status=progress"
echo "========================================"