ISO: add --offline bundle so the kiosk installs with no internet
`iso/build-iso.sh --offline` (and the workflow's new `offline` box, on by default) adds ~420 MB to the ISO and copies it to /opt/kiosk-offline: - apt/: local repository with every package install.sh's provisioning and the CUPS addon install ask for, NodeSource's nodejs, and all dependencies a fresh server install doesn't already have. Resolved against the source ISO's own package manifest via a synthesized dpkg status (iso/offline/fake-dpkg-status.py), so apt downloads only what's missing and makes the same virtual/alternative choices as the target. - npm/: prebuilt kiosk-app (Electron 42, binary included - it no longer downloads at npm install time) and Web UI node_modules. On first boot, kiosk-firstboot installs online as before when there's internet; with none it waits KIOSK_OFFLINE_WAIT (90s), then runs `kiosk-offline-apt on` (apt.conf.d drop-in: bundle is the ONLY source) and turns it off again afterwards. install.sh notices offline mode via offline_mode_active (lib/config.sh) and installs nodejs from the bundle and unpacks prebuilt node_modules instead of npm install. Package lists moved into PROVISION_APT_PACKAGES / PROVISION_INTEL_APT_PACKAGES / CUPS_APT_PACKAGES so the bundler reads the same lists install.sh installs from. Tested: built against the 26.04.1 ISO, then in a network-less chroot of the ISO's own server filesystem installed every package set, ran provision_create_kiosk_user/install_nodejs/install_app and webui_install_app_files in offline mode (Electron runs, no missing libraries, chrome-sandbox setuid), and ran kiosk-firstboot through the offline switch with a stub install.sh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cjz8a58eB5P9sUweqd3NHm
This commit is contained in:
@@ -19,6 +19,9 @@
|
||||
# KIOSK_UNATTENDED=1 take every prompt's default, skip the Core
|
||||
# Settings menu, reboot into the kiosk when done.
|
||||
# KIOSK_UNATTENDED=0 (default) normal interactive install.sh on tty1.
|
||||
# KIOSK_OFFLINE_WAIT=N on an ISO built with --offline: seconds to wait
|
||||
# for internet before installing from the bundled
|
||||
# packages instead (default 90).
|
||||
################################################################################
|
||||
|
||||
set -uo pipefail
|
||||
@@ -31,13 +34,23 @@ SUDOERS_DROPIN=/etc/sudoers.d/90-kiosk-firstboot
|
||||
APT_DROPIN=/etc/apt/apt.conf.d/90-kiosk-firstboot
|
||||
REPO_SRC=/opt/ubuntu-based-kiosk
|
||||
LOG="$STATE_DIR/install.log"
|
||||
OFFLINE_BUNDLE=/opt/kiosk-offline
|
||||
OFFLINE_APT=/usr/local/sbin/kiosk-offline-apt
|
||||
OFFLINE_FLAG="$STATE_DIR/offline-mode"
|
||||
|
||||
KIOSK_UNATTENDED=0
|
||||
KIOSK_OFFLINE_WAIT=90
|
||||
# shellcheck source=/dev/null
|
||||
[[ -f "$CONF" ]] && source "$CONF"
|
||||
|
||||
cleanup() {
|
||||
rm -f "$SUDOERS_DROPIN" "$APT_DROPIN"
|
||||
# Offline mode was only for this install - hand apt back to the real
|
||||
# archive so normal updates work once the kiosk is online.
|
||||
if [[ -f "$OFFLINE_FLAG" ]]; then
|
||||
"$OFFLINE_APT" off >/dev/null 2>&1 || true
|
||||
rm -f "$OFFLINE_FLAG"
|
||||
fi
|
||||
if [[ -f "$DONE_MARKER" ]]; then
|
||||
rm -f "$PENDING"
|
||||
fi
|
||||
@@ -86,6 +99,8 @@ offer_retry() {
|
||||
done
|
||||
}
|
||||
|
||||
# Returns 0 once online, 2 to install from the offline bundle instead
|
||||
# (only when there is one), 1 to give up.
|
||||
wait_for_internet() {
|
||||
local waited=0
|
||||
until curl -fsS --max-time 10 -o /dev/null https://deb.nodesource.com/ 2>/dev/null \
|
||||
@@ -93,9 +108,17 @@ wait_for_internet() {
|
||||
if (( waited == 0 )); then
|
||||
echo "Waiting for an internet connection (needed for packages,"
|
||||
echo "Node.js and Electron)..."
|
||||
if [[ -f "$OFFLINE_BUNDLE/apt/Packages" ]]; then
|
||||
echo "This disk has the offline bundle: installing from it instead"
|
||||
echo "if there's still no internet after ${KIOSK_OFFLINE_WAIT}s."
|
||||
fi
|
||||
fi
|
||||
sleep 10
|
||||
waited=$((waited + 10))
|
||||
if [[ -f "$OFFLINE_BUNDLE/apt/Packages" ]] && (( waited >= KIOSK_OFFLINE_WAIT )); then
|
||||
echo "No internet after ${waited}s - installing from the offline bundle on this disk."
|
||||
return 2
|
||||
fi
|
||||
if (( waited % 120 == 0 )); then
|
||||
echo "Still no internet after ${waited}s. Network status:"
|
||||
networkctl list --no-pager 2>/dev/null || ip -brief addr
|
||||
@@ -153,7 +176,22 @@ fi
|
||||
mkdir -p "$STATE_DIR"
|
||||
printf 'DPkg::Lock::Timeout "900";\n' > "$APT_DROPIN"
|
||||
|
||||
wait_for_internet || { release_tty; exit 1; }
|
||||
wait_for_internet
|
||||
case $? in
|
||||
0) ;;
|
||||
2)
|
||||
# install.sh notices this by itself (lib/config.sh's
|
||||
# offline_mode_active) and unpacks prebuilt node_modules instead
|
||||
# of running npm; cleanup() switches it back off.
|
||||
touch "$OFFLINE_FLAG"
|
||||
if ! "$OFFLINE_APT" on; then
|
||||
echo "ERROR: couldn't switch apt to the offline bundle."
|
||||
release_tty
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*) release_tty; exit 1 ;;
|
||||
esac
|
||||
|
||||
# install.sh's own preflight needs jq; Advanced -> Upgrade needs git.
|
||||
echo "Installing installer prerequisites (jq, git)..."
|
||||
|
||||
Executable
+63
@@ -0,0 +1,63 @@
|
||||
#!/bin/bash
|
||||
################################################################################
|
||||
# kiosk-offline-apt - point apt at the offline install bundle, or back.
|
||||
#
|
||||
# sudo kiosk-offline-apt on apt uses ONLY /opt/kiosk-offline/apt
|
||||
# sudo kiosk-offline-apt off back to the normal Ubuntu archive
|
||||
# kiosk-offline-apt status
|
||||
#
|
||||
# Installed by the custom ISO (iso/build-iso.sh); the bundle itself is
|
||||
# only there on ISOs built with --offline. kiosk-firstboot turns this on
|
||||
# by itself when first boot finds no internet, and back off afterwards.
|
||||
# By hand it's for installing things later with no internet - e.g. the
|
||||
# CUPS addon: `sudo kiosk-offline-apt on`, ./install.sh -> Addons -> CUPS,
|
||||
# then `sudo kiosk-offline-apt off`. While it's on, install.sh also
|
||||
# unpacks the bundle's prebuilt node_modules instead of running npm
|
||||
# (lib/config.sh's offline_mode_active checks for the file this writes).
|
||||
#
|
||||
# "Only" matters: with the normal archive still listed, apt would pick
|
||||
# newer versions from it that it then can't download.
|
||||
################################################################################
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
BUNDLE_DIR=/opt/kiosk-offline
|
||||
APT_CONF=/etc/apt/apt.conf.d/90-kiosk-offline
|
||||
|
||||
case "${1:-status}" in
|
||||
on)
|
||||
[[ $EUID -eq 0 ]] || { echo "Run with sudo" >&2; exit 1; }
|
||||
if [[ ! -f "$BUNDLE_DIR/apt/Packages" ]]; then
|
||||
echo "No offline bundle at $BUNDLE_DIR (ISO not built with --offline)" >&2
|
||||
exit 1
|
||||
fi
|
||||
mkdir -p "$BUNDLE_DIR/sources.d"
|
||||
echo "deb [trusted=yes] file:$BUNDLE_DIR/apt ./" > "$BUNDLE_DIR/offline.list"
|
||||
cat > "$APT_CONF" <<EOF
|
||||
// Written by kiosk-offline-apt - remove with: sudo kiosk-offline-apt off
|
||||
Dir::Etc::SourceList "$BUNDLE_DIR/offline.list";
|
||||
Dir::Etc::SourceParts "$BUNDLE_DIR/sources.d";
|
||||
Acquire::Languages "none";
|
||||
EOF
|
||||
apt-get update -q >/dev/null
|
||||
echo "apt now installs from the offline bundle only ($BUNDLE_DIR)."
|
||||
;;
|
||||
off)
|
||||
[[ $EUID -eq 0 ]] || { echo "Run with sudo" >&2; exit 1; }
|
||||
rm -f "$APT_CONF"
|
||||
echo "apt is back on the normal Ubuntu sources."
|
||||
echo "Run 'sudo apt update' once there's internet."
|
||||
;;
|
||||
status)
|
||||
if [[ -f "$APT_CONF" ]]; then
|
||||
echo "on (apt uses $BUNDLE_DIR/apt only)"
|
||||
else
|
||||
echo "off"
|
||||
fi
|
||||
[[ -f "$BUNDLE_DIR/bundle-info.txt" ]] && sed -n '1,3p;5,6p' "$BUNDLE_DIR/bundle-info.txt"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: kiosk-offline-apt on|off|status" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user