#!/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" # Three parts for a feature release, four for a maintenance release of # one: 0.2.6, then 0.2.6.1. See tools/release-lib.sh. source "$ROOT/tools/release-lib.sh" release_require_version "$VERSION" || exit 1 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" "$ROOT/tests" \ -name '__pycache__' -type d -prune -exec rm -rf {} + 2>/dev/null || true # THE MANUAL IS PART OF THE RELEASE, NOT A JOB FOR AFTERWARDS. # # Built from docs/manual before the tests run, so that the test which # checks the digital and printed versions against each other is checking # what this package is about to ship rather than whatever was built last. # A page that would make the printed manual wrong fails the build here. echo "Building the teacher's manual" "$ROOT/tools/build-manual" 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-display-mode" \ "$STAGE/usr/local/bin/classroom-display-mode" 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" # An administrator's command. The church Wi-Fi has to be provisioned as a # system connection or the classroom account can read its password. install -Dm755 \ "$ROOT/scripts/classroom-wifi" \ "$STAGE/usr/local/bin/classroom-wifi" # 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_display.py" \ "$STAGE/usr/local/lib/ekklesia-classroom/classroom_display.py" install -Dm644 \ "$ROOT/lib/classroom_network.py" \ "$STAGE/usr/local/lib/ekklesia-classroom/classroom_network.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" install -Dm755 \ "$ROOT/scripts/classroom-reboot-when-idle" \ "$STAGE/usr/local/bin/classroom-reboot-when-idle" # 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" # After an update: the reboot, once nobody is using the computer. install -Dm644 \ "$ROOT/systemd/updater/ekklesia-classroom-reboot-when-idle.service" \ "$STAGE/etc/systemd/system/ekklesia-classroom-reboot-when-idle.service" 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 kwinrc kwinrulesrc kscreenlockerrc ksmserverrc \ krunnerrc plasmanotifyrc powermanagementprofilesrc plasmarc \ plasmaparc; do install -Dm644 \ "$ROOT/config/plasma/xdg/${setting}" \ "$STAGE/etc/ekklesia-classroom/xdg/${setting}" done # kdeglobals is composed rather than copied: the session policy plus the # dark palette itself. Naming a colour scheme does not produce one, and a # room whose user profile had never had a scheme applied came up light. install -d "$STAGE/etc/ekklesia-classroom/xdg" "$ROOT/tools/build-kdeglobals" > "$STAGE/etc/ekklesia-classroom/xdg/kdeglobals" chmod 0644 "$STAGE/etc/ekklesia-classroom/xdg/kdeglobals" install -Dm644 \ "$ROOT/config/environment/90-ekklesia-classroom.conf" \ "$STAGE/etc/environment.d/90-ekklesia-classroom.conf" # The session shortcuts, and deliberately NOT in the xdg directory above. # # kglobalacceld reads ~/.config/kglobalshortcutsrc as a SimpleConfig: no # cascade, no system directories. Measured — with this file in the # cascade, kreadconfig6 reported the chord unbound while the shortcut # server went on answering Ctrl+Alt+Del. classroom-desktop-reset copies # this into the teacher's home before the shortcut server starts, the # same way the panel is copied out of the package at every login. install -Dm644 \ "$ROOT/config/plasma/kglobalshortcutsrc" \ "$STAGE/etc/ekklesia-classroom/kglobalshortcutsrc" # The one that actually reaches a session started by a display manager. # startplasma sources this directory; PAM does not read environment.d, so # without this file none of the settings above are in the cascade at all # and the room comes up as stock Plasma. Measured, on a real machine. install -Dm644 \ "$ROOT/config/plasma/env/zz-ekklesia-classroom.sh" \ "$STAGE/etc/xdg/plasma-workspace/env/zz-ekklesia-classroom.sh" # 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" # NO SHELL PACKAGE OF OUR OWN. # # Plasma 6.3 loads the volume display from the shell package and from # nowhere else — the look and feel is not consulted, which was measured — # so a shell package is the only supported way to replace it, and a # classroom-sized one was built and tried. With it selected, plasmashell # builds no panel at all: not the stock one, not ours. Measured twice, and # the room's panel is deleted at every login on purpose, so every login # would have come up with nothing on the screen. # # The volume display is Plasma's own instead. UI_SCALE makes it the right # size on a television, which is what the room actually needed. # The room's Plasma style: the card a notification and the volume display # are drawn on. Only the pieces the classroom changes; everything else # comes from Breeze, so this can never leave a popup undrawn. STYLE="$STAGE/usr/share/plasma/desktoptheme/ekklesia" install -Dm644 \ "$ROOT/config/plasma/desktoptheme/metadata.json" \ "$STYLE/metadata.json" install -Dm644 \ "$ROOT/config/plasma/desktoptheme/dialogs/background.svg" \ "$STYLE/dialogs/background.svg" 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" # Shadows the old notification daemon's D-Bus activation. Deactivating its # systemd unit is not enough: D-Bus starts it on demand from a file # mako-notifier owns, and the crash-recovery test on a migrated machine # caught it doing exactly that while the panel was down. install -Dm644 \ "$ROOT/config/dbus/fr.emersion.mako.service" \ "$STAGE/usr/local/share/dbus-1/services/fr.emersion.mako.service" # Shadows Plasma's session screen. Ctrl+Alt+Delete asks D-Bus to start it; # with this in front of plasma-workspace's own file there is nothing to # start. See the file itself for why the unbound chord is not enough on # its own. install -Dm644 \ "$ROOT/config/dbus/org.kde.LogoutPrompt.service" \ "$STAGE/usr/local/share/dbus-1/services/org.kde.LogoutPrompt.service" # Three marks the classroom draws itself, because nothing in Breeze says # any of them: a lesson, fetching a child's grown-up, and tonight's # slides. for icon in ekklesia-wonderink ekklesia-parent-call ekklesia-presentations; 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" # Wonder Ink's printing: a PDF, shown, then handed to CUPS with lp. install -Dm644 \ "$ROOT/lib/classroom_printing.py" \ "$STAGE/usr/local/lib/ekklesia-classroom/classroom_printing.py" # Wonder Ink's recent tabs, restored within two hours of their last use. install -Dm644 \ "$ROOT/lib/classroom_wonderink_workspace.py" \ "$STAGE/usr/local/lib/ekklesia-classroom/classroom_wonderink_workspace.py" # PRESENTATIONS. # # The library a teacher sees, and the watcher that fills it from their # downloads. Both are installed on every machine; PRESENTATIONS_ENABLED # in device.conf is what decides whether either ever runs, and it # defaults to false so an existing classroom upgrades unchanged. # THE MANUAL. # # One HTML file carrying every page, and the command that opens it with # the pages this device has no use for taken out. Installed on every # machine including ones with no optional features at all, because the # common pages — the bar, the browser, the volume, what to do when # something is wrong — are the ones a new teacher needs most. install -Dm644 \ "$ROOT/docs/manual/build/classroom-manual.html" \ "$STAGE/usr/local/share/ekklesia-classroom/manual/classroom-manual.html" # The printable one goes on the machine too. A room whose printed copy has # gone missing can print another without anybody fetching a file from a # laptop, and its revision date is the same one the digital manual shows. install -Dm644 \ "$ROOT/docs/manual/build/classroom-manual.pdf" \ "$STAGE/usr/local/share/ekklesia-classroom/manual/classroom-manual.pdf" install -Dm755 \ "$ROOT/scripts/classroom-manual" \ "$STAGE/usr/local/bin/classroom-manual" install -Dm755 \ "$ROOT/scripts/classroom-presentations" \ "$STAGE/usr/local/bin/classroom-presentations" install -Dm755 \ "$ROOT/scripts/classroom-presentation-import" \ "$STAGE/usr/local/bin/classroom-presentation-import" install -Dm644 \ "$ROOT/lib/classroom_presentations.py" \ "$STAGE/usr/local/lib/ekklesia-classroom/classroom_presentations.py" install -Dm644 \ "$ROOT/systemd/user/ekklesia-classroom-presentations.service" \ "$STAGE/etc/systemd/user/ekklesia-classroom-presentations.service" 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" # Converges a machine that used to run something else. Called from # postinst on every install, and by hand by a technician. install -Dm755 \ "$ROOT/scripts/classroom-migrate" \ "$STAGE/usr/local/bin/classroom-migrate" # One number, one place to ask for it. install -Dm755 \ "$ROOT/scripts/classroom-ui-scale" \ "$STAGE/usr/local/bin/classroom-ui-scale" # The room's display size: a hidden dialog behind a chord, a writer that # asks for an administrator password, and the one thing that applies the # answer at every login. for command in classroom-display-settings classroom-set-scale; do install -Dm755 \ "$ROOT/scripts/${command}" \ "$STAGE/usr/local/bin/${command}" done install -Dm644 \ "$ROOT/config/polkit/org.ekklesia.classroom.display.policy" \ "$STAGE/usr/share/polkit-1/actions/org.ekklesia.classroom.display.policy" # The classroom user cannot suspend or hibernate the room. A vendor rule # in /usr/share, not /etc, so a technician can override it by dropping a # file of the same name into /etc/polkit-1/rules.d. install -Dm644 \ "$ROOT/config/polkit/org.ekklesia.classroom.sleep.rules" \ "$STAGE/usr/share/polkit-1/rules.d/49-ekklesia-classroom-sleep.rules" # The classroom user cannot be shown a saved Wi-Fi password. Same place # and the same override route as the sleep rule above. install -Dm644 \ "$ROOT/config/polkit/org.ekklesia.classroom.network.rules" \ "$STAGE/usr/share/polkit-1/rules.d/49-ekklesia-classroom-network.rules" install -Dm644 \ "$ROOT/config/plasma/autostart/ekklesia-classroom-scale.desktop" \ "$STAGE/etc/xdg/autostart/ekklesia-classroom-scale.desktop" # Which displays are on, decided before the scale is applied to them. install -Dm644 \ "$ROOT/config/plasma/autostart/ekklesia-classroom-display.desktop" \ "$STAGE/etc/xdg/autostart/ekklesia-classroom-display.desktop" # The panel's disc player follows this machine's hardware, and the # detection has to have happened before a session starts. install -Dm644 \ "$ROOT/systemd/layout/ekklesia-classroom-layout.service" \ "$STAGE/etc/systemd/system/ekklesia-classroom-layout.service" install -Dm644 \ "$ROOT/config/udev/60-ekklesia-classroom-optical.rules" \ "$STAGE/etc/udev/rules.d/60-ekklesia-classroom-optical.rules" 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" # The palette and the overlay measurements. The DVD control bar, Parent # Call and the Presentations library all draw themselves from this. 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" # The version dpkg reads out of the file is the one a classroom will # report, so it is checked rather than assumed to have survived the sed. built_version="$(release_package_version "$OUTPUT")" if [[ "$built_version" != "$VERSION" ]]; then echo "The package says it is $built_version, not $VERSION" >&2 rm -f "$OUTPUT" exit 1 fi echo echo "Built:" echo "$OUTPUT" sha256sum "$OUTPUT"