mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-25 18:40:06 -04:00
The ISO's chroot hook created wrolpi as a login user (UID 1000, password "wrolpi", full sudo, LightDM autologin). The Debian installer creates the user it asks for only when no UID 1000-59999 user exists, so it silently skipped that user: the only login on an installed system was wrolpi/wrolpi, with SSH and sudo. wrolpi is now a system account with no password or login sudo, as on every other WROLPi deployment. The installer creates the user it asks for (with sudo, no autologin). The live session logs in as live-config's "user". Desktop shortcuts moved to /etc/skel so every user gets them. check-iso.sh now fails the build when the image has a UID 1000-59999 user. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
193 lines
7.9 KiB
Bash
Executable file
193 lines
7.9 KiB
Bash
Executable file
#!/bin/bash
|
|
# Build the WROLPi Debian Live + installer ISO.
|
|
#
|
|
# Output: an iso-hybrid image that boots into a full WROLPi system on any
|
|
# x86 PC. Users can run WROLPi directly off the USB (persistence partition
|
|
# created on first boot) or install to disk via the Calamares launcher on
|
|
# the desktop. Calamares wiring lands in phase 2; phase 1 ships the
|
|
# live-only flow.
|
|
#
|
|
# https://live-team.pages.debian.net/live-manual/html/live-manual/index.en.html
|
|
|
|
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-debian}"
|
|
|
|
Help() {
|
|
echo "Build WROLPi Debian Live ISO 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. Strip whitespace so stray newlines or
|
|
# spaces in version.txt do not corrupt the lb config metadata strings.
|
|
VERSION=$(curl -sL "https://raw.githubusercontent.com/lrnselfreliance/wrolpi/${BRANCH}/wrolpi/version.txt" | tr -d '[:space:]')
|
|
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
|
|
|
|
# Clear out old builds. Strip immutable flag from blob files first — the
|
|
# chroot hook applies chattr +i to /opt/wrolpi-blobs/*; without removing
|
|
# that here, rm -rf leaves them behind and the next lb build's
|
|
# chroot_includes_after_packages rsync fails with "Operation not permitted"
|
|
# trying to overwrite them.
|
|
if [ -d "${BUILD_DIR}" ]; then
|
|
find "${BUILD_DIR}" -path '*/opt/wrolpi-blobs/*' -type f -exec chattr -i {} + 2>/dev/null || true
|
|
rm -rf "${BUILD_DIR}"
|
|
fi
|
|
mkdir -p "${BUILD_DIR}"
|
|
cd "${BUILD_DIR}" || (echo "Work directory must exist" && exit 1)
|
|
set -e
|
|
|
|
# Live + Debian Installer. The installer is the stock Debian d-i with
|
|
# `--debian-installer live`, which copies the running squashfs onto the
|
|
# target disk (much faster than a network-based install) and presents the
|
|
# usual graphical/text installer flow. GRUB gets "Install" + "Graphical
|
|
# install" + "Live" entries. noswap prevents the live system from
|
|
# activating swap partitions found on the host machine's internal disks.
|
|
# ca-certificates must be in the bootstrap so apt can reach the HTTPS
|
|
# repositories in config/archives/ before the package lists are installed.
|
|
lb config \
|
|
--binary-images iso-hybrid \
|
|
--mode debian \
|
|
--architectures amd64 \
|
|
--linux-flavours amd64 \
|
|
--distribution trixie \
|
|
--debian-installer live \
|
|
--debian-installer-gui true \
|
|
--archive-areas "main contrib non-free non-free-firmware" \
|
|
--updates true \
|
|
--security true \
|
|
--apt-recommends false \
|
|
--debootstrap-options "--include=ca-certificates" \
|
|
--firmware-binary true \
|
|
--firmware-chroot true \
|
|
--win32-loader false \
|
|
--checksums sha512 \
|
|
--clean \
|
|
--color \
|
|
--linux-packages "linux-image linux-headers" \
|
|
--memtest memtest86+ \
|
|
--bootappend-live "boot=live components quiet splash noswap persistence persistence-storage=filesystem persistence-media=removable persistence-label=persistence live-config.username=user live-config.user-fullname=WROLPi live-config.hostname=wrolpi" \
|
|
--iso-volume "WROLPi v${VERSION}" \
|
|
--iso-application "WROLPi v${VERSION}" \
|
|
--iso-preparer WROLPi \
|
|
--iso-publisher https://wrolpi.org
|
|
|
|
rsync -a "${SCRIPT_DIR}/config" "${BUILD_DIR}/"
|
|
|
|
# Write branch config for hook script.
|
|
mkdir -p "${BUILD_DIR}/config/includes.chroot/opt/wrolpi-blobs"
|
|
echo "${BRANCH}" > "${BUILD_DIR}/config/includes.chroot/opt/wrolpi-branch"
|
|
|
|
# Cache directory for downloaded blobs (persists across builds).
|
|
BLOB_CACHE="${SCRIPT_DIR}/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)
|
|
|
|
# Copy cached blobs into the live image.
|
|
cp "${BLOB_CACHE}/map-fonts.tar.gz" "${BUILD_DIR}/config/includes.chroot/opt/wrolpi-blobs/"
|
|
cp "${BLOB_CACHE}/map-overview.pmtiles" "${BUILD_DIR}/config/includes.chroot/opt/wrolpi-blobs/"
|
|
|
|
# pipefail so an lb build failure isn't masked by tee's success exit code.
|
|
set -o pipefail
|
|
time nice -n 18 lb build 2>&1 | tee "${SCRIPT_DIR}/build.log"
|
|
set +o pipefail
|
|
|
|
grep "9999-wrolpi.hook.chroot completed" "${SCRIPT_DIR}/build.log" >/dev/null 2>&1 || (echo "build hook failed!" && exit 1)
|
|
|
|
# Copy the freshly built ISO directly to its final name. Take the single ISO
|
|
# out of BUILD_DIR (which is wiped each build, so it holds exactly one) rather
|
|
# than globbing SCRIPT_DIR -- a leftover ISO from a previous build would
|
|
# otherwise make a `*.iso` glob match multiple files and break the move.
|
|
DEST="${SCRIPT_DIR}/WROLPi-v${VERSION}-amd64.iso"
|
|
shopt -s nullglob
|
|
BUILT_ISOS=("${BUILD_DIR}"/*.iso)
|
|
shopt -u nullglob
|
|
if [ "${#BUILT_ISOS[@]}" -eq 0 ]; then
|
|
echo "Build failed. No ISOs were found!"
|
|
exit 1
|
|
fi
|
|
rm -f "${DEST}"
|
|
cp "${BUILT_ISOS[0]}" "${DEST}"
|
|
chmod 644 "${DEST}"
|
|
chown "${SUDO_UID:-1000}:${SUDO_GID:-1000}" "${DEST}"
|
|
|
|
# Sanity-check the resulting ISO before declaring success, so a truncated or
|
|
# corrupt build never ships.
|
|
MIN_ISO_BYTES=$((1024 * 1024 * 1024)) # 1 GiB floor; a real ISO is ~3 GiB.
|
|
ISO_BYTES=$(stat -c%s "${DEST}")
|
|
if [ "${ISO_BYTES}" -lt "${MIN_ISO_BYTES}" ]; then
|
|
echo "ERROR: ISO is only ${ISO_BYTES} bytes (< ${MIN_ISO_BYTES}); build likely truncated." >&2
|
|
exit 1
|
|
fi
|
|
# The ISO 9660 primary volume descriptor carries the "CD001" magic at byte
|
|
# offset 32769. It is present even in iso-hybrid images, where `file` reports
|
|
# the leading MBR instead.
|
|
if [ "$(dd if="${DEST}" bs=1 skip=32769 count=5 2>/dev/null)" != "CD001" ]; then
|
|
echo "ERROR: ${DEST} is not a valid ISO 9660 image (missing CD001 magic)." >&2
|
|
exit 1
|
|
fi
|
|
# Cross-check with blkid when available.
|
|
if command -v blkid >/dev/null 2>&1; then
|
|
ISO_FSTYPE=$(blkid -o value -s TYPE "${DEST}" 2>/dev/null || true)
|
|
if [ -n "${ISO_FSTYPE}" ] && [ "${ISO_FSTYPE}" != "iso9660" ]; then
|
|
echo "ERROR: blkid reports filesystem '${ISO_FSTYPE}', expected iso9660." >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
echo "ISO sanity checks passed ($(numfmt --to=iec "${ISO_BYTES}" 2>/dev/null || echo "${ISO_BYTES} bytes"), iso9660)."
|
|
|
|
# Problems that only appear once the Debian installer has run (e.g. packages it
|
|
# would purge from the installed system).
|
|
"${SCRIPT_DIR}/check-iso.sh" "${DEST}" || exit 1
|
|
|
|
echo "Build has completed. ISO output ${DEST}"
|