Offline bundle now carries every addon that works on a network without internet: LMS (Lyrion's own .deb, from the addon's LMS_DEB_URL) + Squeezelite, Asterisk Intercom (baresip), VNC, WireGuard and the Emergency Hotspot packages - each list in a variable next to the code that installs it (SQUEEZELITE_APT_PACKAGES, ASTERISK_INTERCOM_APT_PACKAGES, VNC_APT_PACKAGES, WIREGUARD_APT_PACKAGES, EMERGENCY_HOTSPOT_APT_PACKAGES), read by build-bundle.sh. Each install goes through run_with_offline_fallback like CUPS, so they install offline from the normal menus / Web UI. LMS installs as a bundle package when offline (lms_offline_install_wanted); its online repo/direct-download paths are unchanged. Tailscale/Netbird deliberately not bundled (vendor scripts, vendor servers). Bundle: 524 MB (+87 MB). Emergency hotspot: the boot check pinged only 8.8.8.8, so on a LAN with no internet the hotspot started on every boot. Now chosen at setup: "network" (default - router or internet must answer; a router that ignores ping still counts if it answered ARP), "internet" (old check), or "host" (a specific address). Three tries 10s apart. Existing scripts keep the old check until Reconfigure. New Core Settings -> Time Server (NTP) (menus/time_sync.sh): point systemd-timesyncd at LAN time server(s) via a timesyncd.conf.d drop-in, back to Ubuntu's defaults, or set the clock by hand (and the RTC) for a network with no time server. Complete Uninstall resets it. Also: lms_service_name used `systemctl list-unit-files | grep -q`, the same pipefail/SIGPIPE pattern as the earlier cups_is_installed bug; now `systemctl cat`. Tested in a network-less chroot of the 26.04.1 server filesystem: kiosk base, then action_wireguard_install and action_install_lms (real functions) and the Squeezelite/Intercom/VNC/hotspot package sets all installed from the bundle, apt left on normal sources after each. Hotspot script generated for all three modes (plus an invalid address, which falls back to "network") and its check run against simulated networks: LAN-only with router answering ping or only ARP -> no hotspot in "network" mode; nothing reachable -> hotspot. Time Server actions driven with timedatectl stubbed: drop-in contents, input validation, manual set-time sequence, reset removes the drop-in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cjz8a58eB5P9sUweqd3NHm
134 lines
5.5 KiB
YAML
134 lines
5.5 KiB
YAML
# Builds the all-in-one Ubuntu Server 26.04 + kiosk install ISO
|
|
# (iso/build-iso.sh) on demand: Actions tab -> "Build kiosk ISO" ->
|
|
# Run workflow. The ISO (~2.8 GB) is attached to the run as a downloadable
|
|
# artifact for a few days - see the run's Summary page.
|
|
#
|
|
# Pre-setting the admin account (needed for "fully-automatic") uses the
|
|
# repository secret KIOSK_ADMIN_PASSWORD (Settings -> Secrets and
|
|
# variables -> Actions) - never a workflow input, since inputs are shown
|
|
# in the run log. Only a SHA-512 hash of it goes on the ISO, but anyone
|
|
# who downloads the ISO can read that hash, so treat the artifact as
|
|
# private and use a password you'll change after install.
|
|
name: Build kiosk ISO
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
mode:
|
|
description: >-
|
|
interactive: installer asks network/disk/user, kiosk install.sh
|
|
runs with prompts on first boot. unattended-kiosk: same installer,
|
|
kiosk installs with defaults and reboots into the kiosk.
|
|
fully-automatic: no questions at all - WIPES THE LARGEST DISK
|
|
(needs username + KIOSK_ADMIN_PASSWORD secret).
|
|
type: choice
|
|
default: interactive
|
|
options:
|
|
- interactive
|
|
- unattended-kiosk
|
|
- fully-automatic
|
|
username:
|
|
description: Admin username to pre-set (blank = asked during install). Needs the KIOSK_ADMIN_PASSWORD secret. Not "kiosk"/"admin".
|
|
type: string
|
|
default: ""
|
|
hostname:
|
|
description: Hostname (only used when username is set)
|
|
type: string
|
|
default: kiosk
|
|
ssh_public_key:
|
|
description: Optional SSH public key for the admin user (ssh-ed25519 AAAA...)
|
|
type: string
|
|
default: ""
|
|
offline:
|
|
description: >-
|
|
Bundle everything install.sh downloads (~520 MB: all packages incl.
|
|
the CUPS, LMS/Squeezelite, Intercom, VNC, WireGuard and hotspot
|
|
addons, Node.js, Electron) so the kiosk and those addons install
|
|
with no internet.
|
|
type: boolean
|
|
default: true
|
|
retention_days:
|
|
description: Days to keep the ISO artifact
|
|
type: number
|
|
default: 3
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 60
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Install build tools
|
|
run: |
|
|
sudo apt-get update -q
|
|
sudo apt-get install -y -q xorriso apt-utils ubuntu-keyring
|
|
|
|
- name: Build ISO
|
|
env:
|
|
MODE: ${{ inputs.mode }}
|
|
OFFLINE: ${{ inputs.offline }}
|
|
ADMIN_USERNAME: ${{ inputs.username }}
|
|
ADMIN_HOSTNAME: ${{ inputs.hostname }}
|
|
SSH_PUBLIC_KEY: ${{ inputs.ssh_public_key }}
|
|
KIOSK_ADMIN_PASSWORD: ${{ secrets.KIOSK_ADMIN_PASSWORD }}
|
|
run: |
|
|
mkdir -p "$RUNNER_TEMP/iso"
|
|
args=(--output "$RUNNER_TEMP/iso/ubuntu-kiosk-amd64.iso")
|
|
case "$MODE" in
|
|
unattended-kiosk) args+=(--unattended) ;;
|
|
fully-automatic) args+=(--fully-automatic) ;;
|
|
esac
|
|
if [[ -n "$ADMIN_USERNAME" ]]; then
|
|
if [[ -z "$KIOSK_ADMIN_PASSWORD" ]]; then
|
|
echo "::error::username given but the KIOSK_ADMIN_PASSWORD repository secret isn't set"
|
|
exit 1
|
|
fi
|
|
args+=(--username "$ADMIN_USERNAME" --hostname "$ADMIN_HOSTNAME")
|
|
fi
|
|
if [[ -n "$SSH_PUBLIC_KEY" ]]; then
|
|
printf '%s\n' "$SSH_PUBLIC_KEY" > "$RUNNER_TEMP/ssh_key.pub"
|
|
args+=(--ssh-key "$RUNNER_TEMP/ssh_key.pub")
|
|
fi
|
|
[[ "$OFFLINE" == "true" ]] && args+=(--offline)
|
|
iso/build-iso.sh "${args[@]}"
|
|
# Name the file after the Ubuntu point release the script picked.
|
|
src="$(find iso/build/cache -name "*.iso" -print -quit)"
|
|
kind="kiosk"; [[ "$OFFLINE" == "true" ]] && kind="kiosk-offline"
|
|
name="$(basename "$src" .iso | sed "s/-live-server/-$kind/")-${MODE}-${GITHUB_SHA::7}.iso"
|
|
mv "$RUNNER_TEMP/iso/ubuntu-kiosk-amd64.iso" "$RUNNER_TEMP/iso/$name"
|
|
# Free the ~3 GB source ISO before the upload step needs scratch space.
|
|
rm -rf iso/build/cache iso/build/offline-cache
|
|
(cd "$RUNNER_TEMP/iso" && sha256sum "$name" > "$name.sha256")
|
|
echo "ISO_NAME=$name" >> "$GITHUB_ENV"
|
|
{
|
|
echo "### $name"
|
|
echo
|
|
echo "- Mode: \`$MODE\`"
|
|
echo "- Offline bundle: $([[ "$OFFLINE" == "true" ]] && echo "yes - installs with no internet" || echo "no - kiosk needs internet on first boot")"
|
|
echo "- Admin user: ${ADMIN_USERNAME:-chosen during install}"
|
|
echo "- Built from: \`${GITHUB_REF_NAME}\` @ \`${GITHUB_SHA::7}\`"
|
|
echo "- SHA-256: \`$(cut -d' ' -f1 "$RUNNER_TEMP/iso/$name.sha256")\`"
|
|
echo
|
|
echo "Download it from **Artifacts** below (GitHub zips it; unzip, then write the .iso to a USB stick)."
|
|
if [[ "$MODE" == "fully-automatic" ]]; then
|
|
echo
|
|
echo "> **Warning:** this ISO wipes the target machine's largest disk without asking."
|
|
fi
|
|
} >> "$GITHUB_STEP_SUMMARY"
|
|
|
|
- name: Upload ISO
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: ${{ env.ISO_NAME }}
|
|
path: |
|
|
${{ runner.temp }}/iso/${{ env.ISO_NAME }}
|
|
${{ runner.temp }}/iso/${{ env.ISO_NAME }}.sha256
|
|
# An ISO is already compressed - zipping it again only costs time.
|
|
compression-level: 0
|
|
retention-days: ${{ inputs.retention_days }}
|
|
if-no-files-found: error
|