Two layers for the button, because one is not enough: while a session is running PowerDevil holds logind's handle-power-key inhibitor and acts on the button itself, so a logind drop-in alone would be overruled. logind covers the machine before the session starts, after it ends, and if PowerDevil is not running. Verified: after the drop-in is installed and logind reloaded, it reports ignore for the power, suspend, hibernate and lid keys, and the running session was not disturbed by the reload. Holding the button still cuts the power. That is firmware, below any operating system, and it is deliberately left alone as the only recovery a person standing at a wedged machine has. The maintenance terminal is Meta+Ctrl+Alt+Shift+K, declared in the package rather than seeded into a teacher's configuration, where an update could not reach it. KGlobalAccel read it and registered the component; invoking it opened a working shell. It needed a wrapper. The classroom's Kiosk policy sets shell_access=false and Konsole honours it — measured: with the classroom configuration in its cascade Konsole starts and says "Program to run not set." The restriction is worth keeping everywhere else, so it is lifted for this one process by leaving the classroom's configuration directory out of its cascade. It grants no privilege: the classroom user has no sudo rule, and that restriction was always a barrier in the interface rather than a privilege boundary. Konsole's own entry is shadowed and hidden from /usr/local/share, so nothing is patched and Konsole's package stays maintainable. Nothing is pinned to the panel; a task button appears only while a terminal is open, and closing it returns the bar to three launchers. Co-Authored-By: Claude Opus 5 <[email protected]>
375 lines
11 KiB
Bash
Executable File
375 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "$ROOT"
|
|
|
|
if [[ $# -ne 1 ]]; then
|
|
echo "Usage: $0 VERSION"
|
|
exit 1
|
|
fi
|
|
|
|
VERSION="$1"
|
|
|
|
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
echo "Invalid version: $VERSION"
|
|
echo "Expected MAJOR.MINOR.PATCH, for example 0.1.6"
|
|
exit 1
|
|
fi
|
|
|
|
STAGE="$ROOT/packaging/root"
|
|
OUTPUT="$ROOT/dist/ekklesia-classroom_${VERSION}_amd64.deb"
|
|
|
|
# Syntax check everything the package ships, by shebang, so this cannot
|
|
# drift out of date as classroom commands are added.
|
|
echo "Validating sources"
|
|
|
|
for script in "$ROOT/packaging/debian/postinst" "$ROOT"/scripts/* "$ROOT"/tests/*; do
|
|
[[ -f "$script" ]] || continue
|
|
|
|
case "$(head -n 1 "$script")" in
|
|
*python3*)
|
|
python3 -m py_compile "$script"
|
|
;;
|
|
*bash*|*bin/sh*)
|
|
bash -n "$script"
|
|
;;
|
|
esac
|
|
done
|
|
|
|
for module in "$ROOT"/lib/*.py; do
|
|
[[ -f "$module" ]] || continue
|
|
|
|
python3 -m py_compile "$module"
|
|
done
|
|
|
|
for policy in "$ROOT"/config/chrome/policies/*/*.json; do
|
|
[[ -f "$policy" ]] || continue
|
|
|
|
python3 -c 'import json, sys; json.load(open(sys.argv[1]))' "$policy"
|
|
done
|
|
|
|
find "$ROOT/scripts" "$ROOT/lib" \
|
|
-name '__pycache__' -type d -prune -exec rm -rf {} + 2>/dev/null || true
|
|
|
|
for test in "$ROOT"/tests/test-*.sh; do
|
|
[[ -x "$test" ]] || continue
|
|
|
|
echo "Running $(basename "$test")"
|
|
"$test" > /dev/null || {
|
|
echo "Tests failed. Run it directly to see why:"
|
|
echo " $test"
|
|
exit 1
|
|
}
|
|
done
|
|
|
|
echo "Building Ekklesia Classroom ${VERSION}"
|
|
|
|
# Always construct the package from clean canonical sources.
|
|
rm -rf "$STAGE"
|
|
mkdir -p "$STAGE/DEBIAN" "$ROOT/dist"
|
|
|
|
# Debian package metadata.
|
|
sed \
|
|
"s/^Version: @VERSION@$/Version: ${VERSION}/" \
|
|
"$ROOT/packaging/debian/control.template" \
|
|
> "$STAGE/DEBIAN/control"
|
|
|
|
install -m755 \
|
|
"$ROOT/packaging/debian/postinst" \
|
|
"$STAGE/DEBIAN/postinst"
|
|
|
|
|
|
|
|
|
|
# Classroom commands.
|
|
install -Dm755 \
|
|
"$ROOT/scripts/classroom-browser" \
|
|
"$STAGE/usr/local/bin/classroom-browser"
|
|
|
|
|
|
|
|
install -Dm755 \
|
|
"$ROOT/scripts/classroom-dvd" \
|
|
"$STAGE/usr/local/bin/classroom-dvd"
|
|
|
|
install -Dm755 \
|
|
"$ROOT/scripts/classroom-dvd-session" \
|
|
"$STAGE/usr/local/bin/classroom-dvd-session"
|
|
|
|
install -Dm755 \
|
|
"$ROOT/scripts/classroom-dvd-css-setup" \
|
|
"$STAGE/usr/local/bin/classroom-dvd-css-setup"
|
|
|
|
|
|
|
|
install -Dm755 \
|
|
"$ROOT/scripts/classroom-mqtt" \
|
|
"$STAGE/usr/local/bin/classroom-mqtt"
|
|
|
|
|
|
install -Dm755 \
|
|
"$ROOT/scripts/classroom-scale" \
|
|
"$STAGE/usr/local/bin/classroom-scale"
|
|
|
|
install -Dm755 \
|
|
"$ROOT/scripts/classroom-shutdown" \
|
|
"$STAGE/usr/local/bin/classroom-shutdown"
|
|
|
|
install -Dm755 \
|
|
"$ROOT/scripts/classroom-chrome-setup" \
|
|
"$STAGE/usr/local/bin/classroom-chrome-setup"
|
|
|
|
install -Dm755 \
|
|
"$ROOT/scripts/classroom-provision" \
|
|
"$STAGE/usr/local/bin/classroom-provision"
|
|
|
|
install -Dm755 \
|
|
"$ROOT/scripts/classroom-printer-setup" \
|
|
"$STAGE/usr/local/bin/classroom-printer-setup"
|
|
|
|
# Shared helpers imported by the classroom commands.
|
|
install -Dm644 \
|
|
"$ROOT/lib/classroom_config.py" \
|
|
"$STAGE/usr/local/lib/ekklesia-classroom/classroom_config.py"
|
|
|
|
|
|
|
|
install -Dm644 \
|
|
"$ROOT/lib/classroom_mqtt.py" \
|
|
"$STAGE/usr/local/lib/ekklesia-classroom/classroom_mqtt.py"
|
|
|
|
|
|
|
|
install -Dm644 \
|
|
"$ROOT/lib/classroom_vlc.py" \
|
|
"$STAGE/usr/local/lib/ekklesia-classroom/classroom_vlc.py"
|
|
|
|
|
|
install -Dm644 \
|
|
"$ROOT/config/wallpapers/classroom-calm.png" \
|
|
"$STAGE/usr/share/backgrounds/ekklesia/classroom-calm.png"
|
|
|
|
|
|
|
|
|
|
# Chrome enterprise policy: suppress first-run and sign-in nags, and pin
|
|
# Home to Wonder Ink. Nothing here restricts where teachers can browse.
|
|
install -Dm644 \
|
|
"$ROOT/config/chrome/policies/managed/ekklesia-classroom.json" \
|
|
"$STAGE/etc/opt/chrome/policies/managed/ekklesia-classroom.json"
|
|
|
|
install -Dm644 \
|
|
"$ROOT/config/chrome/policies/recommended/ekklesia-classroom.json" \
|
|
"$STAGE/etc/opt/chrome/policies/recommended/ekklesia-classroom.json"
|
|
|
|
install -Dm755 \
|
|
"$ROOT/scripts/ekklesia-classroom-update" \
|
|
"$STAGE/usr/local/bin/ekklesia-classroom-update"
|
|
|
|
# System configuration.
|
|
install -Dm644 \
|
|
"$ROOT/config/sddm/ekklesia-classroom.conf" \
|
|
"$STAGE/etc/sddm.conf.d/ekklesia-classroom.conf"
|
|
|
|
install -Dm644 \
|
|
"$ROOT/systemd/updater/ekklesia-classroom-update.service" \
|
|
"$STAGE/etc/systemd/system/ekklesia-classroom-update.service"
|
|
|
|
install -Dm644 \
|
|
"$ROOT/systemd/updater/ekklesia-classroom-update.timer" \
|
|
"$STAGE/etc/systemd/system/ekklesia-classroom-update.timer"
|
|
|
|
install -Dm644 \
|
|
"$ROOT/systemd/provision/ekklesia-classroom-provision.service" \
|
|
"$STAGE/etc/systemd/system/ekklesia-classroom-provision.service"
|
|
|
|
install -Dm644 \
|
|
"$ROOT/systemd/shutdown/ekklesia-classroom-shutdown.service" \
|
|
"$STAGE/etc/systemd/system/ekklesia-classroom-shutdown.service"
|
|
|
|
install -Dm644 \
|
|
"$ROOT/systemd/mqtt/ekklesia-classroom-mqtt.service" \
|
|
"$STAGE/etc/systemd/system/ekklesia-classroom-mqtt.service"
|
|
|
|
|
|
|
|
|
|
|
|
# The classroom desktop.
|
|
#
|
|
# Not a shell, and not a daemon: a Plasma session described in files Plasma
|
|
# already reads. The panel, the window behaviour, the notifications, the
|
|
# lockdown and the palette are all in here, which is why there is no longer
|
|
# a dock, a window helper, a notification wrapper or a compositor config in
|
|
# the list above.
|
|
|
|
# Not /etc/xdg: plasma-workspace already owns files by these names there.
|
|
# The classroom's directory goes in front of it in XDG_CONFIG_DIRS instead.
|
|
for setting in kdeglobals kwinrc kwinrulesrc kscreenlockerrc ksmserverrc \
|
|
krunnerrc plasmanotifyrc powermanagementprofilesrc; do
|
|
install -Dm644 \
|
|
"$ROOT/config/plasma/xdg/${setting}" \
|
|
"$STAGE/etc/ekklesia-classroom/xdg/${setting}"
|
|
done
|
|
|
|
install -Dm644 \
|
|
"$ROOT/config/environment/90-ekklesia-classroom.conf" \
|
|
"$STAGE/etc/environment.d/90-ekklesia-classroom.conf"
|
|
|
|
# The power button does nothing. Two layers, because while a session is
|
|
# running PowerDevil holds logind's inhibitor and would overrule it.
|
|
install -Dm644 \
|
|
"$ROOT/config/systemd/logind-ekklesia-classroom.conf" \
|
|
"$STAGE/etc/systemd/logind.conf.d/ekklesia-classroom.conf"
|
|
|
|
for scheme in EkklesiaDark EkklesiaLight; do
|
|
install -Dm644 \
|
|
"$ROOT/config/plasma/colors/${scheme}.colors" \
|
|
"$STAGE/usr/share/color-schemes/${scheme}.colors"
|
|
done
|
|
|
|
LOOK="$STAGE/usr/share/plasma/look-and-feel/org.ekklesia.classroom.desktop"
|
|
|
|
install -Dm644 \
|
|
"$ROOT/config/plasma/lookandfeel/metadata.json" \
|
|
"$LOOK/metadata.json"
|
|
|
|
install -Dm644 \
|
|
"$ROOT/config/plasma/lookandfeel/contents/defaults" \
|
|
"$LOOK/contents/defaults"
|
|
|
|
install -Dm644 \
|
|
"$ROOT/config/plasma/lookandfeel/contents/layouts/org.kde.plasma.desktop-layout.js" \
|
|
"$LOOK/contents/layouts/org.kde.plasma.desktop-layout.js"
|
|
|
|
for entry in "$ROOT"/config/plasma/applications/*.desktop; do
|
|
install -Dm644 "$entry" \
|
|
"$STAGE/usr/share/applications/$(basename "$entry")"
|
|
done
|
|
|
|
# Shadows Chrome's own entry. XDG_DATA_DIRS searches /usr/local/share first,
|
|
# so the panel matches a Chrome window to this launcher instead of to
|
|
# Chrome's, and the room gets one button rather than two.
|
|
for entry in "$ROOT"/config/plasma/applications-local/*.desktop; do
|
|
install -Dm644 "$entry" \
|
|
"$STAGE/usr/local/share/applications/$(basename "$entry")"
|
|
done
|
|
|
|
install -Dm644 \
|
|
"$ROOT/config/plasma/solid/ekklesia-dvd.desktop" \
|
|
"$STAGE/usr/share/solid/actions/ekklesia-dvd.desktop"
|
|
|
|
# Two marks the classroom draws itself, because nothing in Breeze says
|
|
# either thing: a lesson, and fetching a child's grown-up.
|
|
for icon in ekklesia-wonderink ekklesia-parent-call; do
|
|
install -Dm644 \
|
|
"$ROOT/config/icons/${icon}.svg" \
|
|
"$STAGE/usr/share/icons/hicolor/scalable/apps/${icon}.svg"
|
|
done
|
|
|
|
# The panel is the only way a teacher has of getting anywhere. Plasma gives
|
|
# up restarting it after three crashes in a minute; this does not.
|
|
install -Dm644 \
|
|
"$ROOT/systemd/user/plasma-plasmashell.service.d/ekklesia-classroom.conf" \
|
|
"$STAGE/etc/systemd/user/plasma-plasmashell.service.d/ekklesia-classroom.conf"
|
|
|
|
install -Dm644 \
|
|
"$ROOT/systemd/user/ekklesia-classroom-audio.service" \
|
|
"$STAGE/etc/systemd/user/ekklesia-classroom-audio.service"
|
|
|
|
install -Dm644 \
|
|
"$ROOT/systemd/user/ekklesia-classroom-desktop.service" \
|
|
"$STAGE/etc/systemd/user/ekklesia-classroom-desktop.service"
|
|
|
|
install -Dm755 \
|
|
"$ROOT/scripts/classroom-wonderink" \
|
|
"$STAGE/usr/local/bin/classroom-wonderink"
|
|
|
|
install -Dm755 \
|
|
"$ROOT/scripts/classroom-audio" \
|
|
"$STAGE/usr/local/bin/classroom-audio"
|
|
|
|
install -Dm755 \
|
|
"$ROOT/scripts/classroom-parent-call" \
|
|
"$STAGE/usr/local/bin/classroom-parent-call"
|
|
|
|
install -Dm755 \
|
|
"$ROOT/scripts/classroom-desktop-layout" \
|
|
"$STAGE/usr/local/bin/classroom-desktop-layout"
|
|
|
|
install -Dm755 \
|
|
"$ROOT/scripts/classroom-admin-terminal" \
|
|
"$STAGE/usr/local/bin/classroom-admin-terminal"
|
|
|
|
install -Dm755 \
|
|
"$ROOT/scripts/classroom-desktop-reset" \
|
|
"$STAGE/usr/local/bin/classroom-desktop-reset"
|
|
|
|
install -Dm755 \
|
|
"$ROOT/scripts/classroom-theme" \
|
|
"$STAGE/usr/local/bin/classroom-theme"
|
|
|
|
install -Dm644 \
|
|
"$ROOT/lib/classroom_audio.py" \
|
|
"$STAGE/usr/local/lib/ekklesia-classroom/classroom_audio.py"
|
|
|
|
install -Dm644 \
|
|
"$ROOT/lib/classroom_parent_call.py" \
|
|
"$STAGE/usr/local/lib/ekklesia-classroom/classroom_parent_call.py"
|
|
|
|
# The notifier the MQTT service uses to put a church message on screen.
|
|
install -Dm755 \
|
|
"$ROOT/scripts/classroom-notify" \
|
|
"$STAGE/usr/local/bin/classroom-notify"
|
|
|
|
install -Dm755 \
|
|
"$ROOT/scripts/classroom-notify-test" \
|
|
"$STAGE/usr/local/bin/classroom-notify-test"
|
|
|
|
|
|
install -Dm644 \
|
|
"$ROOT/lib/classroom_optical.py" \
|
|
"$STAGE/usr/local/lib/ekklesia-classroom/classroom_optical.py"
|
|
|
|
# The DVD overlay draws its own control bar as a layer-shell surface, so it
|
|
# still needs the icon and stylesheet helpers the dock used to share.
|
|
install -Dm644 \
|
|
"$ROOT/lib/classroom_icons.py" \
|
|
"$STAGE/usr/local/lib/ekklesia-classroom/classroom_icons.py"
|
|
|
|
install -Dm644 \
|
|
"$ROOT/lib/classroom_style.py" \
|
|
"$STAGE/usr/local/lib/ekklesia-classroom/classroom_style.py"
|
|
|
|
install -Dm644 \
|
|
"$ROOT/config/icons/dvd.svg" \
|
|
"$STAGE/etc/ekklesia-classroom/icons/dvd.svg"
|
|
|
|
install -Dm644 \
|
|
"$ROOT/config/overlay/style.css.in" \
|
|
"$STAGE/etc/ekklesia-classroom/overlay/style.css.in"
|
|
|
|
# DVD mode control bar.
|
|
for icon in media-play media-pause chapter-prev chapter-next \
|
|
disc-menu eject close; do
|
|
install -Dm644 \
|
|
"$ROOT/config/icons/${icon}.svg" \
|
|
"$STAGE/etc/ekklesia-classroom/icons/${icon}.svg"
|
|
done
|
|
|
|
printf '%s\n' "$VERSION" > "$ROOT/VERSION"
|
|
|
|
install -Dm644 \
|
|
"$ROOT/VERSION" \
|
|
"$STAGE/usr/local/lib/ekklesia-classroom/VERSION"
|
|
|
|
dpkg-deb \
|
|
--root-owner-group \
|
|
--build "$STAGE" "$OUTPUT"
|
|
|
|
echo
|
|
echo "Built:"
|
|
echo "$OUTPUT"
|
|
sha256sum "$OUTPUT"
|