Adding WROLPi Portable build — bootable amd64 Live USB.

New debian-live-usb/ directory produces a live ISO that boots into a full
WROLPi desktop on any x86 PC without installing to the host disk. Distinct
from debian-live-config/ (installer ISO). Phase 1: live boot only; Phase 2
will add persistence partition for the Postgres DB and media directory.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Roland 2026-05-18 18:41:39 -06:00
parent 2806673e25
commit ed30ed832c
No known key found for this signature in database
GPG key ID: 1BA620CE6C9A4680
5 changed files with 366 additions and 0 deletions

127
debian-live-usb/build.sh Executable file
View file

@ -0,0 +1,127 @@
#!/bin/bash
# Build WROLPi Portable — a bootable amd64 Live USB image.
#
# Output: an iso-hybrid image written to this directory. Users flash it to a
# USB drive (Etcher / Rufus / dd / Raspberry Pi Imager) and boot it on any
# x86 PC. Unlike ../debian-live-config/, this build is NOT an installer — it
# runs the full WROLPi stack live from the USB. Phase 2 will add a persistence
# partition so the Postgres DB and media directory survive reboots.
#
# https://live-team.pages.debian.net/live-manual/html/live-manual/index.en.html
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
BUILD_DIR=/var/tmp/wrolpi-build-portable
Help() {
echo "Build WROLPi Portable Live USB 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
}
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 Portable version: ${VERSION} from branch: ${BRANCH}"
# Re-execute this script if it wasn't called with sudo.
if [ $EUID != 0 ]; then
sudo "$0" "$@"
exit $?
fi
# Clear out old builds.
[ -d "${BUILD_DIR}" ] && rm -rf "${BUILD_DIR}"
mkdir "${BUILD_DIR}"
cd "${BUILD_DIR}" || (echo "Work directory must exist" && exit 1)
set -e
# Pure live config — no --debian-installer flags. The resulting image boots
# directly into a running WROLPi system, never offering to install to disk.
# noswap prevents the live system from activating swap partitions found on
# the host machine's internal disks (ethereal hygiene).
lb config \
--binary-images iso-hybrid \
--mode debian \
--architectures amd64 \
--linux-flavours amd64 \
--distribution bookworm \
--archive-areas "main contrib non-free non-free-firmware" \
--updates true \
--security true \
--apt-recommends false \
--firmware-binary true \
--firmware-chroot true \
--win32-loader false \
--checksums sha512 \
--clean \
--color \
--linux-packages "linux-image linux-headers" \
--bootappend-live "boot=live components quiet splash noswap" \
--iso-volume "WROLPi Portable v${VERSION}" \
--iso-application "WROLPi Portable 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/"
time nice -n 18 lb build 2>&1 | tee "${SCRIPT_DIR}/build.log"
grep "9999-wrolpi.hook.chroot completed" "${SCRIPT_DIR}/build.log" >/dev/null 2>&1 || (echo "build hook failed!" && exit 1)
cp "${BUILD_DIR}"/*iso "${SCRIPT_DIR}/" || (echo "Build failed. No ISOs were found!" && exit 1)
chmod 644 "${SCRIPT_DIR}"/*iso
chown 1000:1000 "${SCRIPT_DIR}"/*iso
DEST="${SCRIPT_DIR}/WROLPi-Portable-v${VERSION}-amd64.iso"
[ -f "${DEST}" ] && (echo "Removing conflicting ISO" && rm "${DEST}")
mv "${SCRIPT_DIR}"/*.iso "${DEST}"
echo "Build has completed. ISO output ${DEST}"

View file

@ -0,0 +1,153 @@
#!/usr/bin/bash
# Chroot hook for WROLPi Portable — composes the full WROLPi stack into the
# live system during live-build's chroot phase. Closely mirrors the installer
# ISO hook in ../../../debian-live-config/config/hooks/normal/, with two
# Portable-specific additions: bake in pmtiles/tippecanoe (offline-first
# first-boot) and drop the default Postgres cluster (Phase 2 firstboot will
# recreate it on the persistence partition).
set -e
set -x
# Generate and set default locale to avoid perl warnings.
# Users can change this later via: sudo dpkg-reconfigure locales
sed -i 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
locale-gen
echo 'LANG=en_US.UTF-8' > /etc/default/locale
export LANG=en_US.UTF-8
cat >>/etc/profile <<'EOF'
alias ll='ls -lh'
alias la='ll -a'
EOF
git --version
WROLPI_BRANCH="release"
[ -f /opt/wrolpi-branch ] && WROLPI_BRANCH=$(cat /opt/wrolpi-branch)
git clone -b "${WROLPI_BRANCH}" https://github.com/lrnselfreliance/wrolpi.git /opt/wrolpi
rm -f /opt/wrolpi-branch
# Install webapp.
cd /opt/wrolpi/app
npm --version
node --version
npm install
# Install Node console commands.
npm i -g serve@12.0.1 single-file-cli@2.0.73 readability-extractor@0.0.6
# Install Deno runtime.
DENO_VERSION="v2.2.4"
curl -fsSL "https://github.com/denoland/deno/releases/download/${DENO_VERSION}/deno-x86_64-unknown-linux-gnu.zip" -o /tmp/deno.zip
unzip /tmp/deno.zip -d /usr/local/bin/
chmod +x /usr/local/bin/deno
rm /tmp/deno.zip
deno --version
# Install Python requirements. Try multiple times because pypi may stop responding.
python3 --version
python3 -m venv /opt/wrolpi/venv
/opt/wrolpi/venv/bin/pip3 install -r /opt/wrolpi/requirements.txt ||
/opt/wrolpi/venv/bin/pip3 install -r /opt/wrolpi/requirements.txt ||
/opt/wrolpi/venv/bin/pip3 install -r /opt/wrolpi/requirements.txt
# Install Controller with separate venv.
echo "Pre-installing WROLPi Controller..."
python3 -m venv /opt/wrolpi/controller/venv
/opt/wrolpi/controller/venv/bin/pip install --upgrade pip
/opt/wrolpi/controller/venv/bin/pip install -r /opt/wrolpi/controller/requirements.txt ||
/opt/wrolpi/controller/venv/bin/pip install -r /opt/wrolpi/controller/requirements.txt ||
/opt/wrolpi/controller/venv/bin/pip install -r /opt/wrolpi/controller/requirements.txt
echo "Controller pre-installed"
# Install Caddy from official apt repo.
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
apt-get update
apt-get install -y caddy
# Configure Caddy.
mkdir -p /etc/caddy
cp /opt/wrolpi/etc/raspberrypios/Caddyfile /etc/caddy/Caddyfile
mkdir -p /var/www
cp /opt/wrolpi/etc/raspberrypios/50x.html /var/www/50x.html
# WROLPi needs a few privileged commands.
cp /opt/wrolpi/etc/raspberrypios/90-wrolpi /etc/sudoers.d/90-wrolpi
chmod 0440 /etc/sudoers.d/90-wrolpi
# Verify this new file is valid.
visudo -c -f /etc/sudoers.d/90-wrolpi
mkdir /media/wrolpi
# Verify blob files were copied in by includes.chroot (downloaded by build.sh).
[[ -f /opt/wrolpi-blobs/map-fonts.tar.gz && -s /opt/wrolpi-blobs/map-fonts.tar.gz ]] || \
(echo "ERROR: map-fonts.tar.gz missing from /opt/wrolpi-blobs!" && exit 1)
[[ -f /opt/wrolpi-blobs/map-overview.pmtiles && -s /opt/wrolpi-blobs/map-overview.pmtiles ]] || \
(echo "ERROR: map-overview.pmtiles missing from /opt/wrolpi-blobs!" && exit 1)
# Bake in pmtiles CLI so first boot does not need internet.
# Mirrors scripts/install_protomaps.sh; duplicated here because that script's
# migration logic relies on a running systemd, which is not present in chroot.
PMTILES_VERSION="v1.30.1"
curl -fsSL "https://github.com/protomaps/go-pmtiles/releases/download/${PMTILES_VERSION}/go-pmtiles_${PMTILES_VERSION#v}_Linux_amd64.tar.gz" \
| tar -xz -C /usr/local/bin/ pmtiles
chmod +x /usr/local/bin/pmtiles
pmtiles version
# Build and bake in tippecanoe (used to build map search indexes).
TIPPECANOE_VERSION="2.79.0"
TIPPECANOE_BUILD_DIR=$(mktemp -d)
curl -fsSL "https://github.com/felt/tippecanoe/archive/refs/tags/${TIPPECANOE_VERSION}.tar.gz" \
| tar -xz -C "${TIPPECANOE_BUILD_DIR}" --strip-components=1
(cd "${TIPPECANOE_BUILD_DIR}" && make -j"$(nproc)" && make install)
rm -rf "${TIPPECANOE_BUILD_DIR}"
tippecanoe --version
# Install WROLPi Help.
/opt/wrolpi/scripts/install_help_service.sh
cp /opt/wrolpi/etc/raspberrypios/wrolpi-*.service /etc/systemd/system/
cp /opt/wrolpi/etc/raspberrypios/wrolpi-*.timer /etc/systemd/system/
cp /opt/wrolpi/etc/raspberrypios/wrolpi.target /etc/systemd/system/
# Copy MOTD scripts, delete original.
cp /opt/wrolpi/etc/raspberrypios/motd/20-wrolpi.motd /etc/update-motd.d/20-wrolpi
cp /opt/wrolpi/etc/raspberrypios/motd/30-wrolpi-first.motd /etc/update-motd.d/30-wrolpi
chmod +x /etc/update-motd.d/*
truncate -s 0 /etc/motd
# NetworkManager for the hotspot.
systemctl enable NetworkManager
# Enable iperf3 for speed testing.
systemctl enable iperf3
# Enable first startup script to generate certificates and start Caddy.
systemctl enable wrolpi-first-startup.service
# Enable Controller service to start on boot.
systemctl enable wrolpi-controller.service
# Enable weekly certificate renewal check.
systemctl enable wrolpi-cert-renew.timer
# Drop the default Postgres cluster created by the postgresql-15 package.
# On first boot, the persistence partition will be bind-mounted over
# /var/lib/postgresql; with the default cluster baked into the squashfs, the
# mount would mask it and Postgres would fail to start against an empty data
# dir. Phase 2's firstboot service creates a fresh cluster directly on the
# persistence partition.
pg_dropcluster --stop 15 main
# Protect blob files from accidental modification or deletion.
if [ -d /opt/wrolpi-blobs ] && ls /opt/wrolpi-blobs/* >/dev/null 2>&1; then
chown -R root:root /opt/wrolpi-blobs
chmod 444 /opt/wrolpi-blobs/*
chattr +i /opt/wrolpi-blobs/*
fi
set +x
echo
echo "======================================================================"
echo "9999-wrolpi.hook.chroot completed"
echo "======================================================================"
echo

View file

@ -0,0 +1,2 @@
task-xfce-desktop
task-ssh-server

View file

@ -0,0 +1,82 @@
apt-transport-https
aria2
autoconf
bind9-dnsutils
build-essential
bzip2
ca-certificates
calibre
cargo
catdoc
chromium
cpufrequtils
curl
ethtool
exfat-fuse
fdupes
ffmpeg
firefox-esr
fonts-noto-cjk
fonts-noto-hinted
fonts-noto-unhinted
gcc
gcc-doc
git
git-doc
gnupg-agent
gpsd
hostapd
htop
iotop
iperf3
kiwix
kiwix-tools
libboost-all-dev
libbz2-dev
libcairo2-dev
libcurl4-gnutls-dev
libffi-dev
libfreetype6-dev
libgeos++-dev
libgeos-dev
libicu-dev
libpq-dev
libproj-dev
libreoffice
libsqlite3-dev
libssl-dev
libtiff5-dev
libtool
libxml2-dev
netcat-openbsd
network-manager
nicstat
nodejs
npm
ntfs-3g
pkg-config
postgresql-15
postgresql-doc
pv
python3-dev
python3-doc
python3-full
python3-pip
python3-venv
rsync
samba
smartmontools
software-properties-common
sudo
sysstat
tar
tcpdump
tmux
unrar-free
unzip
vim
vlc
wget
wpasupplicant
zim-tools
zlib1g-dev

2
debian-live-usb/files/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*.pmtiles
*.tar.gz