main
  1#!/bin/sh
  2# Runs inside the Alpine container (aarch64 via qemu-user).
  3# Usage: /build/build-inner.sh <hostname>
  4set -e
  5
  6HOST="${1:?Usage: $0 <hostname>}"
  7BUILD_DIR="/build"
  8COMMON_DIR="$BUILD_DIR/common"
  9HOST_DIR="$BUILD_DIR/hosts/$HOST"
 10OUTPUT_DIR="$BUILD_DIR/images/$HOST"
 11
 12log() { printf "\n\033[1;32m==> %s\033[0m\n" "$1"; }
 13
 14log "Installing build dependencies"
 15apk add --no-cache cpio gzip findutils
 16
 17log "Preparing rootfs skeleton"
 18ROOTFS=$(mktemp -d)
 19
 20# Copy common root skeleton
 21if [ -d "$COMMON_DIR/root" ]; then
 22    cp -a "$COMMON_DIR/root/." "$ROOTFS/"
 23fi
 24# Overlay host-specific skeleton (overrides common)
 25if [ -d "$HOST_DIR/root" ]; then
 26    cp -a "$HOST_DIR/root/." "$ROOTFS/"
 27fi
 28
 29# Copy generated DNS zones if available
 30if [ -d "$BUILD_DIR/generated/zones" ]; then
 31    mkdir -p "$ROOTFS/etc/bind/zones"
 32    cp -aL "$BUILD_DIR/generated/zones/." "$ROOTFS/etc/bind/zones/"
 33fi
 34
 35# Copy generated WireGuard config for this host
 36if [ -f "$BUILD_DIR/generated/wireguard/wg0-${HOST}.conf" ]; then
 37    mkdir -p "$ROOTFS/etc/wireguard"
 38    cp -L "$BUILD_DIR/generated/wireguard/wg0-${HOST}.conf" "$ROOTFS/etc/wireguard/wg0.conf"
 39fi
 40
 41log "Merging package lists"
 42PACKAGES=""
 43[ -f "$COMMON_DIR/packages" ] && PACKAGES="$(grep -v '^#' "$COMMON_DIR/packages" | grep -v '^$' | tr '\n' ' ')"
 44[ -f "$HOST_DIR/packages" ] && PACKAGES="$PACKAGES $(grep -v '^#' "$HOST_DIR/packages" | grep -v '^$' | tr '\n' ' ')"
 45
 46# Always include RPi boot firmware and u-boot
 47PACKAGES="$PACKAGES raspberrypi-bootloader u-boot-raspberrypi"
 48
 49log "Installing packages: $PACKAGES"
 50# Set up APK repositories and keys for the rootfs
 51mkdir -p "$ROOTFS/etc/apk" "$ROOTFS/etc/apk/keys"
 52echo "https://dl-cdn.alpinelinux.org/alpine/v3.21/main" > "$ROOTFS/etc/apk/repositories"
 53echo "https://dl-cdn.alpinelinux.org/alpine/v3.21/community" >> "$ROOTFS/etc/apk/repositories"
 54# Copy host APK keys so signatures are trusted
 55cp /etc/apk/keys/* "$ROOTFS/etc/apk/keys/"
 56
 57# Initialize and install packages into rootfs
 58# shellcheck disable=SC2086 # intentional word splitting
 59apk add --root "$ROOTFS" --initdb --no-cache $PACKAGES
 60
 61log "Running setup scripts"
 62# Concatenate and run setup scripts in chroot
 63SETUP_SCRIPT=$(mktemp)
 64cat > "$SETUP_SCRIPT" << 'HEADER'
 65#!/bin/sh
 66set -e
 67HEADER
 68[ -f "$COMMON_DIR/setup.sh" ] && cat "$COMMON_DIR/setup.sh" >> "$SETUP_SCRIPT"
 69[ -f "$HOST_DIR/setup.sh" ] && cat "$HOST_DIR/setup.sh" >> "$SETUP_SCRIPT"
 70chmod +x "$SETUP_SCRIPT"
 71cp "$SETUP_SCRIPT" "$ROOTFS/tmp/setup.sh"
 72
 73# Ensure init scripts are executable
 74find "$ROOTFS/etc/init.d" -type f -exec chmod +x {} \; 2>/dev/null || true
 75
 76# Run setup in chroot
 77chroot "$ROOTFS" /tmp/setup.sh
 78rm -f "$ROOTFS/tmp/setup.sh"
 79
 80log "Assembling boot partition"
 81mkdir -p "$OUTPUT_DIR/boot"
 82
 83# RPi firmware files (from raspberrypi-bootloader)
 84for f in bootcode.bin fixup.dat fixup4.dat start.elf start4.elf; do
 85    [ -f "$ROOTFS/boot/$f" ] && cp "$ROOTFS/boot/$f" "$OUTPUT_DIR/boot/"
 86done
 87
 88# U-Boot binary
 89if [ -f "$ROOTFS/usr/share/u-boot/rpi_arm64/u-boot.bin" ]; then
 90    cp "$ROOTFS/usr/share/u-boot/rpi_arm64/u-boot.bin" "$OUTPUT_DIR/boot/"
 91fi
 92
 93# DTBs (RPi 4 specific + overlays)
 94for dtb in "$ROOTFS"/boot/bcm2711*.dtb "$ROOTFS"/boot/bcm2712*.dtb; do
 95    [ -f "$dtb" ] && cp "$dtb" "$OUTPUT_DIR/boot/"
 96done
 97if [ -d "$ROOTFS/boot/overlays" ]; then
 98    cp -r "$ROOTFS/boot/overlays" "$OUTPUT_DIR/boot/"
 99fi
100
101# Kernel
102[ -f "$ROOTFS/boot/vmlinuz-rpi" ] && cp "$ROOTFS/boot/vmlinuz-rpi" "$OUTPUT_DIR/boot/"
103
104# config.txt — use Alpine's default as base, add our customizations
105cat > "$OUTPUT_DIR/boot/config.txt" << 'EOF'
106[pi4]
107kernel=u-boot.bin
108enable_gic=1
109
110[pi5]
111kernel=u-boot.bin
112
113[all]
114arm_64bit=1
115enable_uart=1
116disable_overscan=1
117avoid_warnings=1
118
119# Boot kernel + initramfs via U-Boot
120# U-Boot reads extlinux/extlinux.conf automatically
121EOF
122
123# extlinux.conf for U-Boot
124mkdir -p "$OUTPUT_DIR/boot/extlinux"
125cat > "$OUTPUT_DIR/boot/extlinux/extlinux.conf" << EOF
126TIMEOUT 20
127DEFAULT alpine
128
129LABEL alpine
130  MENU LABEL Alpine Linux (diskless)
131  LINUX /vmlinuz-rpi
132  INITRD /initramfs
133  FDTDIR /
134  APPEND console=ttyS1,115200 console=tty1 rdinit=/sbin/init
135EOF
136
137log "Creating initramfs"
138# Remove boot files from rootfs (they go on the boot partition)
139rm -rf "${ROOTFS:?}/boot" "${ROOTFS:?}/usr/share/u-boot"
140
141cd "$ROOTFS"
142find . -print | cpio -o -H newc | gzip > "$OUTPUT_DIR/boot/initramfs"
143
144log "Build complete for $HOST"
145ls -lh "$OUTPUT_DIR/boot/"
146echo ""
147echo "Boot partition contents ready at: images/$HOST/boot/"