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/"