mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 19:01:39 -04:00
All three build targets move from Debian 12/bookworm to Debian 13/trixie for fresh installs; existing Debian 12 systems remain supported in place. Docker: api/help on python:3.13-trixie, controller on 3.13-slim-trixie, app on node:20-trixie (matches trixie's nodejs 20), samba/zim on debian:13. Full backend suite passes on Python 3.13 in the api container. debian-live-config: --distribution trixie; postgres teardown for the SQLite cutover (pg_dropcluster hook block removed, wrolpi-bootstrap.sh no longer creates a cluster or calls the deleted initialize_api_db.sh — the API creates wrolpi.db itself on first start). README documents the SQLite layout and drops Etcher recommendations. pi-gen: upstream has no trixie branch — the arm64 branch now defaults to RELEASE=trixie; KEYBOARD_KEYMAP=en_US is rejected by trixie's console-setup (ckbcomp validates), use 'us'; stop stamping a Postgres .pgpass into /etc/skel. Package lists (both targets): postgresql-15/postgresql-doc/libpq-dev deleted; libtiff5-dev renamed to libtiff-dev; software-properties-common and cpufrequtils removed from trixie (Controller CPU throttle falls back to sysfs, dead cpufreq-set sudoers rule removed); locales and gnupg added (no longer arrive transitively under --apt-recommends false); sqlite3 CLI added for reset_tags.sh. help.sh: replace the Postgres service/port/socket/cluster/psql checks (all false FAILEDs after the SQLite cutover) with SQLite checks: wrolpi.db exists, PRAGMA quick_check, file_group table + row count, run as the wrolpi user so root never creates the -shm sidecar. Both build scripts take a WROLPI_BUILD_DIR override so development builds can run beside the release pipeline's build trees; the sudo re-exec passes it through explicitly (a plain sudo would drop it). test_common.py: the truncate tests hard-coded sys.getsizeof results from the 3.11 object layout (str/list shrank in 3.12); assert the contract (fits budget, prefix, monotonic in budget) so they pass on 3.11 and 3.13. Verified: trixie ISO (3.7G) and pi-gen desktop image (4.3G) both built from scratch and passed their sanity checks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
167 lines
6.7 KiB
Bash
Executable file
167 lines
6.7 KiB
Bash
Executable file
#!/bin/bash
|
||
# https://github.com/RPI-Distro/pi-gen
|
||
|
||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||
# Overridable so a development build can run beside the release pipeline's
|
||
# build tree without colliding with it.
|
||
BUILD_DIR="${WROLPI_BUILD_DIR:-/var/tmp/wrolpi-build-pi-gen}"
|
||
|
||
Help() {
|
||
echo "Build WROLPi Raspberry Pi OS image."
|
||
echo
|
||
echo "Syntax: build.sh [-h] [-b BRANCH]"
|
||
echo "options:"
|
||
echo "h Print this help."
|
||
echo "b Build from this git BRANCH (default: 'release')."
|
||
echo
|
||
echo "Set WROLPI_BUILD_DIR to build somewhere other than the default"
|
||
echo "(${BUILD_DIR}), e.g. beside the release pipeline's tree."
|
||
echo
|
||
}
|
||
|
||
BRANCH="release"
|
||
while getopts ":hb:" option; do
|
||
case $option in
|
||
h) Help; exit ;;
|
||
b) BRANCH="${OPTARG}" ;;
|
||
*) echo "Error: Invalid option"; exit 1 ;;
|
||
esac
|
||
done
|
||
|
||
# Get version from the target git branch (not local checkout) so output filename matches actual content.
|
||
VERSION=$(curl -sL "https://raw.githubusercontent.com/lrnselfreliance/wrolpi/${BRANCH}/wrolpi/version.txt")
|
||
if [ -z "${VERSION}" ]; then
|
||
echo "ERROR: Could not fetch version from branch '${BRANCH}'"
|
||
exit 1
|
||
fi
|
||
echo "Building WROLPi version: ${VERSION} from branch: ${BRANCH}"
|
||
|
||
# Re-execute this script if it wasn't called with sudo. Pass BUILD_DIR
|
||
# through explicitly — a plain `sudo "$0"` drops WROLPI_BUILD_DIR from the
|
||
# environment and the build would silently land on the default path.
|
||
if [ $EUID != 0 ]; then
|
||
sudo WROLPI_BUILD_DIR="${BUILD_DIR}" "$0" "$@"
|
||
exit $?
|
||
fi
|
||
|
||
CHROOTFS="${BUILD_DIR}/work/WROLPi/stage0/rootfs"
|
||
|
||
cleanup() {
|
||
[ -f /var/tmp/arm64.zip ] && rm /var/tmp/arm64.zip
|
||
|
||
# Only unmount if it is actually a mountpoint
|
||
for mp in \
|
||
"${CHROOTFS}/dev/pts" \
|
||
"${CHROOTFS}/sys" \
|
||
"${CHROOTFS}/proc" \
|
||
"${CHROOTFS}/run" \
|
||
"${CHROOTFS}/tmp" \
|
||
"${CHROOTFS}/dev"; do
|
||
|
||
if mountpoint -q "${mp}" 2>/dev/null; then
|
||
umount "${mp}" 2>/dev/null || umount -l "${mp}" 2>/dev/null
|
||
fi
|
||
done
|
||
|
||
# Remove immutable attribute from protected files before cleanup
|
||
chattr -i "${BUILD_DIR}"/work/*/stage*/rootfs/opt/wrolpi-blobs/* 2>/dev/null || :
|
||
|
||
# Final safety net – remove the whole build tree
|
||
rm -rf "${BUILD_DIR}"
|
||
}
|
||
|
||
trap cleanup EXIT
|
||
|
||
set -e
|
||
set -x
|
||
|
||
# Cache directory for downloaded blobs (persists across builds).
|
||
BLOB_CACHE="${SCRIPT_DIR}/stage2/04-wrolpi/files"
|
||
mkdir -p "${BLOB_CACHE}"
|
||
|
||
# Download map fonts (skip if cached).
|
||
if [ ! -f "${BLOB_CACHE}/map-fonts.tar.gz" ]; then
|
||
echo "Downloading map fonts..."
|
||
mkdir -p /tmp/map-fonts-dl
|
||
curl -fsSL https://github.com/protomaps/basemaps-assets/archive/refs/heads/main.tar.gz \
|
||
| tar -xz --strip-components=1 -C /tmp/map-fonts-dl basemaps-assets-main/fonts
|
||
tar -czf "${BLOB_CACHE}/map-fonts.tar.gz" -C /tmp/map-fonts-dl fonts
|
||
rm -rf /tmp/map-fonts-dl
|
||
fi
|
||
[[ -f "${BLOB_CACHE}/map-fonts.tar.gz" && -s "${BLOB_CACHE}/map-fonts.tar.gz" ]] || \
|
||
(echo "ERROR: Failed to download map fonts!" && exit 1)
|
||
|
||
# Download map overview (skip if cached).
|
||
if [ ! -f "${BLOB_CACHE}/map-overview.pmtiles" ]; then
|
||
echo "Downloading map overview..."
|
||
curl -fsSL https://wrolpi.nyc3.cdn.digitaloceanspaces.com/maps/map-overview.pmtiles \
|
||
-o "${BLOB_CACHE}/map-overview.pmtiles"
|
||
fi
|
||
[[ -f "${BLOB_CACHE}/map-overview.pmtiles" && -s "${BLOB_CACHE}/map-overview.pmtiles" ]] || \
|
||
(echo "ERROR: Failed to download map overview!" && exit 1)
|
||
|
||
# Clear leftovers from a previous build before unpacking. The EXIT trap
|
||
# normally cleans up, but a SIGTERM/kill (e.g. `systemctl stop`) skips it and
|
||
# leaves BUILD_DIR behind -- the `mv` below would then nest the new tree inside
|
||
# the stale one and break the build. Strip the immutable flag off blob files
|
||
# first (the chroot sets chattr +i), or rm -rf cannot remove them.
|
||
chattr -i "${BUILD_DIR}"/work/*/stage*/rootfs/opt/wrolpi-blobs/* 2>/dev/null || :
|
||
rm -rf "${BUILD_DIR}" /var/tmp/pi-gen-arm64 /var/tmp/arm64.zip
|
||
|
||
# Get the latest pi-gen code. The `arm64` branch builds trixie (Debian 13).
|
||
wget https://github.com/RPi-Distro/pi-gen/archive/refs/heads/arm64.zip -O /var/tmp/arm64.zip
|
||
unzip /var/tmp/arm64.zip -d /var/tmp
|
||
mv /var/tmp/pi-gen-arm64 "${BUILD_DIR}"
|
||
|
||
# Copy the configuration and build files into the pi-gen directory.
|
||
cp "${SCRIPT_DIR}/config.txt" "${BUILD_DIR}/config.txt"
|
||
echo "export WROLPI_BRANCH=${BRANCH}" >> "${BUILD_DIR}/config.txt"
|
||
rsync -a "${SCRIPT_DIR}"/stage2/* "${BUILD_DIR}/stage2/"
|
||
|
||
# We only need to build the Lite and Desktop images.
|
||
rm -f "${BUILD_DIR}"/stage*/EXPORT*
|
||
#echo 'IMG_SUFFIX="-lite"' > "${BUILD_DIR}/stage2/EXPORT_IMAGE"
|
||
echo 'IMG_SUFFIX="-desktop"' > "${BUILD_DIR}/stage5/EXPORT_IMAGE"
|
||
|
||
# Build the images.
|
||
(cd "${BUILD_DIR}" && time nice -n 18 "${BUILD_DIR}"/build.sh -c "${BUILD_DIR}"/config.txt | \
|
||
tee "${SCRIPT_DIR}"/build.log)
|
||
|
||
grep "00-run-chroot.sh completed" "${SCRIPT_DIR}/build.log" >/dev/null 2>&1 || (echo "script 0 failed!" && exit 1)
|
||
grep "03-run-chroot.sh completed" "${SCRIPT_DIR}/build.log" >/dev/null 2>&1 || (echo "script 3 failed!" && exit 1)
|
||
grep "03-run-chroot.sh completed" "${SCRIPT_DIR}/build.log" >/dev/null 2>&1 || (echo "script 3 failed!" && exit 1)
|
||
grep "04-run-chroot.sh completed" "${SCRIPT_DIR}/build.log" >/dev/null 2>&1 || (echo "script 4 failed!" && exit 1)
|
||
|
||
# Move the built images out of the build directory.
|
||
#mv "${BUILD_DIR}"/deploy/*lite*xz "${SCRIPT_DIR}"/WROLPi-v"${VERSION}"-aarch64-lite.img.xz
|
||
mv "${BUILD_DIR}"/deploy/*desktop*xz "${SCRIPT_DIR}"/WROLPi-v"${VERSION}"-aarch64-desktop.img.xz
|
||
chmod 644 "${SCRIPT_DIR}"/*xz
|
||
chown -R 1000:1000 "${SCRIPT_DIR}"
|
||
|
||
set +x
|
||
|
||
# Sanity-check the resulting image before declaring success, so a truncated or
|
||
# corrupt build never ships.
|
||
IMG="${SCRIPT_DIR}/WROLPi-v${VERSION}-aarch64-desktop.img.xz"
|
||
MIN_IMG_BYTES=$((1024 * 1024 * 1024)) # 1 GiB floor; compressed is ~5 GiB.
|
||
MIN_UNCOMPRESSED_BYTES=$((2 * 1024 * 1024 * 1024)) # 2 GiB floor; real is ~8 GiB.
|
||
|
||
IMG_BYTES=$(stat -c%s "${IMG}")
|
||
if [ "${IMG_BYTES}" -lt "${MIN_IMG_BYTES}" ]; then
|
||
echo "ERROR: image is only ${IMG_BYTES} bytes (< ${MIN_IMG_BYTES}); build likely truncated." >&2
|
||
exit 1
|
||
fi
|
||
# Full decompression test: catches any corruption in the .xz stream.
|
||
if ! xz -t "${IMG}"; then
|
||
echo "ERROR: ${IMG} failed xz integrity check." >&2
|
||
exit 1
|
||
fi
|
||
# Confirm the uncompressed image is a sane size (field 5 of the robot totals).
|
||
UNCOMPRESSED_BYTES=$(xz --robot -l "${IMG}" 2>/dev/null | awk -F'\t' '/^totals/{print $5}')
|
||
if [ -n "${UNCOMPRESSED_BYTES}" ] && [ "${UNCOMPRESSED_BYTES}" -lt "${MIN_UNCOMPRESSED_BYTES}" ]; then
|
||
echo "ERROR: uncompressed image is only ${UNCOMPRESSED_BYTES} bytes (< ${MIN_UNCOMPRESSED_BYTES})." >&2
|
||
exit 1
|
||
fi
|
||
echo "Image sanity checks passed ($(numfmt --to=iec "${IMG_BYTES}" 2>/dev/null || echo "${IMG_BYTES} bytes") compressed, xz OK)."
|
||
|
||
echo "Build completed successfully"
|