VoxIP Kiosk OS 1.0.0: Raspberry Pi kiosk with web management
Chromium kiosk without a desktop for the Raspberry Pi 3, a Flask web interface on port 8080 (kiosk, system and network management), a restricted root helper, network changes with automatic rollback, a healthcheck timer, install/update/uninstall scripts and a pi-gen image build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
a33c4822bf
96 files changed
+7535
No files matched your search
Executable
+123
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Build a flashable VoxIP Kiosk OS image (.img.xz) with pi-gen.
|
||||
#
|
||||
# ./image/build.sh # build in Docker (recommended)
|
||||
# sudo ./image/build.sh --native # build directly on a Debian/Ubuntu host
|
||||
#
|
||||
# Environment (all optional):
|
||||
# PI_GEN_REF pi-gen tag/branch (32-bit armhf). Default: pinned Bookworm release.
|
||||
# RELEASE bookworm | trixie (must match PI_GEN_REF). Default: bookworm.
|
||||
# VOXIP_DEFAULT_URL kiosk URL baked into the default config.
|
||||
# VOXIP_DEVICE_NAME default device name.
|
||||
# TARGET_HOSTNAME default hostname (the admin UI is at http://<hostname>.local:8080).
|
||||
# ALLOW_DIRTY=1 build from the working tree instead of the committed HEAD.
|
||||
#
|
||||
# Output: image/deploy/<date>-voxip-kiosk-os-<version>.img.xz (+ .sha256)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
WORK="${WORK_DIR:-$ROOT/image/work}"
|
||||
OUT="$ROOT/image/deploy"
|
||||
PI_GEN_REPO="${PI_GEN_REPO:-https://github.com/RPi-Distro/pi-gen.git}"
|
||||
PI_GEN_REF="${PI_GEN_REF:-2024-11-19-raspios-bookworm-armhf}"
|
||||
RELEASE="${RELEASE:-bookworm}"
|
||||
VOXIP_DEFAULT_URL="${VOXIP_DEFAULT_URL:-https://schermen-ijsselflat.voxip.nl}"
|
||||
VOXIP_DEVICE_NAME="${VOXIP_DEVICE_NAME:-VoxIP Kiosk}"
|
||||
TARGET_HOSTNAME="${TARGET_HOSTNAME:-kiosk}"
|
||||
NATIVE=0
|
||||
[[ ${1:-} == "--native" ]] && NATIVE=1
|
||||
[[ ${1:-} == "-h" || ${1:-} == "--help" ]] && { sed -n '3,18p' "$0" | sed 's/^# \{0,1\}//'; exit 0; }
|
||||
|
||||
VERSION="$(tr -d '[:space:]' < "$ROOT/VERSION")"
|
||||
command -v git >/dev/null || { echo "git is vereist" >&2; exit 1; }
|
||||
if [[ $NATIVE -eq 0 ]]; then
|
||||
command -v docker >/dev/null || { echo "Docker is vereist (of gebruik --native op Debian/Ubuntu)" >&2; exit 1; }
|
||||
else
|
||||
[[ $EUID -eq 0 ]] || { echo "--native moet met sudo" >&2; exit 1; }
|
||||
fi
|
||||
|
||||
echo "==> pi-gen $PI_GEN_REF ophalen"
|
||||
mkdir -p "$WORK" "$OUT"
|
||||
rm -rf "$WORK/pi-gen"
|
||||
git clone --quiet --depth 1 --branch "$PI_GEN_REF" "$PI_GEN_REPO" "$WORK/pi-gen"
|
||||
PI_GEN_COMMIT="$(git -C "$WORK/pi-gen" rev-parse HEAD)"
|
||||
PIGEN="$WORK/pi-gen"
|
||||
|
||||
# Do not export the plain Lite image from stage2; ours is exported after stage-voxip.
|
||||
touch "$PIGEN/stage2/SKIP_IMAGES"
|
||||
rm -f "$PIGEN/stage2/EXPORT_NOOBS"
|
||||
|
||||
echo "==> VoxIP-stage voorbereiden"
|
||||
cp -a "$ROOT/image/stage-voxip" "$PIGEN/stage-voxip"
|
||||
PAYLOAD="$PIGEN/stage-voxip/00-voxip/files/voxip-kiosk"
|
||||
mkdir -p "$PAYLOAD"
|
||||
if [[ ${ALLOW_DIRTY:-0} == 1 ]] || ! git -C "$ROOT" rev-parse --git-dir >/dev/null 2>&1; then
|
||||
echo " (werkmap wordt gebruikt, niet de laatste commit)"
|
||||
tar -C "$ROOT" --exclude=./image/work --exclude=./image/deploy --exclude=./.git --exclude=./.venv \
|
||||
--exclude='__pycache__' --exclude=.pytest_cache -cf - . | tar -C "$PAYLOAD" -xf -
|
||||
SOURCE_REV="werkmap"
|
||||
else
|
||||
if [[ -n "$(git -C "$ROOT" status --porcelain)" ]]; then
|
||||
echo " Let op: niet-gecommitte wijzigingen worden NIET meegenomen (ALLOW_DIRTY=1 om dat wel te doen)."
|
||||
fi
|
||||
git -C "$ROOT" archive HEAD | tar -C "$PAYLOAD" -xf -
|
||||
SOURCE_REV="$(git -C "$ROOT" rev-parse HEAD)"
|
||||
fi
|
||||
|
||||
BUILD_DATE="$(date -u +%Y-%m-%d)"
|
||||
cat > "$PIGEN/stage-voxip/00-voxip/files/build.env" <<EOF
|
||||
VOXIP_DEFAULT_URL=$(printf %q "$VOXIP_DEFAULT_URL")
|
||||
VOXIP_DEVICE_NAME=$(printf %q "$VOXIP_DEVICE_NAME")
|
||||
EOF
|
||||
cat > "$PAYLOAD/BUILD-INFO" <<EOF
|
||||
voxip_kiosk_os=$VERSION
|
||||
source_revision=$SOURCE_REV
|
||||
pi_gen_ref=$PI_GEN_REF
|
||||
pi_gen_commit=$PI_GEN_COMMIT
|
||||
release=$RELEASE
|
||||
build_date=$BUILD_DATE
|
||||
EOF
|
||||
|
||||
# The image has no usable default password: the first user is locked and is
|
||||
# only unlocked by credentials from Raspberry Pi Imager (or SSH keys placed
|
||||
# on the boot partition, see README).
|
||||
FIRST_USER_PASS="$(openssl rand -base64 33 | tr -d '\n/+=')"
|
||||
|
||||
cat > "$PIGEN/config" <<EOF
|
||||
IMG_NAME=voxip-kiosk-os-$VERSION
|
||||
IMG_DATE=$BUILD_DATE
|
||||
RELEASE=$RELEASE
|
||||
DEPLOY_COMPRESSION=xz
|
||||
COMPRESSION_LEVEL=6
|
||||
LOCALE_DEFAULT=nl_NL.UTF-8
|
||||
KEYBOARD_KEYMAP=us
|
||||
KEYBOARD_LAYOUT="English (US)"
|
||||
TIMEZONE_DEFAULT=Europe/Amsterdam
|
||||
WPA_COUNTRY=NL
|
||||
TARGET_HOSTNAME=$TARGET_HOSTNAME
|
||||
FIRST_USER_NAME=voxipadmin
|
||||
FIRST_USER_PASS=$FIRST_USER_PASS
|
||||
DISABLE_FIRST_BOOT_USER_RENAME=1
|
||||
ENABLE_SSH=0
|
||||
STAGE_LIST="stage0 stage1 stage2 stage-voxip"
|
||||
EOF
|
||||
|
||||
echo "==> Image bouwen (dit duurt 30–90 minuten)"
|
||||
cd "$PIGEN"
|
||||
if [[ $NATIVE -eq 1 ]]; then
|
||||
./build.sh
|
||||
else
|
||||
CONTINUE=0 PRESERVE_CONTAINER=0 ./build-docker.sh
|
||||
fi
|
||||
|
||||
shopt -s nullglob
|
||||
images=("$PIGEN"/deploy/*.img.xz)
|
||||
[[ ${#images[@]} -gt 0 ]] || { echo "Geen image gevonden in $PIGEN/deploy" >&2; exit 1; }
|
||||
for img in "${images[@]}"; do
|
||||
cp "$img" "$OUT/"
|
||||
(cd "$OUT" && sha256sum "$(basename "$img")" > "$(basename "$img").sha256")
|
||||
echo "==> Klaar: $OUT/$(basename "$img")"
|
||||
done
|
||||
cp "$PAYLOAD/BUILD-INFO" "$OUT/BUILD-INFO-$VERSION.txt"
|
||||
Reference in new issue
Block a user