#!/bin/bash set -uo pipefail # What an upgrade does to a room's own settings file. # # device.conf is the one file in the classroom that belongs to the church # rather than to the package: the room's name, and the broker password. # An upgrade edits it, so an upgrade can also ruin it. These are the cases # that matter, run against the real code — the migration's own Python is # lifted out of the script rather than copied here, so a change to one # cannot pass by disagreeing with the other. ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" python3 - "$ROOT" <<'PY' import re import subprocess import sys import tempfile from pathlib import Path root = Path(sys.argv[1]) script = (root / "scripts/classroom-migrate").read_text() block = re.search(r'python3 - "\$DEVICE_CONFIG" <<\'PY\'\n(.*?)\nPY\n', script, re.S) passed = failed = 0 def check(label, condition): global passed, failed if condition: print(f" ok {label}") passed += 1 else: print(f" FAIL {label}") failed += 1 check("the migration's device.conf code was found", block is not None) if block is None: sys.exit(1) migration = block.group(1) def migrate(text): """Run the real migration over a device.conf and return it.""" with tempfile.TemporaryDirectory() as directory: source = Path(directory) / "migrate.py" source.write_text(migration) config = Path(directory) / "device.conf" config.write_text(text) result = subprocess.run( [sys.executable, str(source), str(config)], capture_output=True, text=True, check=False, ) return config.read_text(), result.stdout + result.stderr print("1. a room from the Hyprland experiment") BEFORE = """# Ekklesia Classroom device configuration UPDATE_URL=https://updates.riverofglorymn.org CHANNEL=hyprland LIVE_ENABLED=false AUDIO_OUTPUT=auto UI_SCALE=2.0 ROOM=little-waders MQTT_ENABLED=false MQTT_HOST= MQTT_PORT=1883 MQTT_USERNAME=classroom MQTT_PASSWORD=hunter2-not-a-real-one COMPOSITOR=hyprland HYPR_APPEARANCE=visual """ after, output = migrate(BEFORE) check("the broker is switched on", "MQTT_ENABLED=true" in after) check("and given the church address", "MQTT_HOST=10.40.0.2" in after) check("the port is left as it was", "MQTT_PORT=1883" in after) check("the room keeps its name", "ROOM=little-waders" in after) check("the username is untouched", "MQTT_USERNAME=classroom" in after) check("the password is untouched", "MQTT_PASSWORD=hunter2-not-a-real-one" in after) check("and is never printed", "hunter2" not in output) check("the compositor setting is gone", "COMPOSITOR" not in after) check("and the appearance setting with it", "HYPR_APPEARANCE" not in after) check("the channel moves off the experiment", "CHANNEL=classroom-v2" in after) check("the room's scale is preserved", "UI_SCALE=2.0" in after) check("and everything else is left alone", "UPDATE_URL=https://updates.riverofglorymn.org" in after and "AUDIO_OUTPUT=auto" in after) print() print("2. a room already pointed at a broker of its own") OWN = """CHANNEL=a-channel-somebody-named MQTT_ENABLED=true MQTT_HOST=broker.example.invalid MQTT_PORT=8883 MQTT_PASSWORD=leave-me """ after, output = migrate(OWN) check("a deliberate address is not overwritten", "MQTT_HOST=broker.example.invalid" in after) check("nor a deliberate port", "MQTT_PORT=8883" in after) check("a channel nobody here named is left alone", "CHANNEL=a-channel-somebody-named" in after) check("and its password is still there", "MQTT_PASSWORD=leave-me" in after) print() print("3. a room old enough to have no broker settings at all") OLD = """CHANNEL=canary UI_SCALE=1.0 """ after, output = migrate(OLD) check("the settings are added", "MQTT_ENABLED=true" in after and "MQTT_HOST=10.40.0.2" in after and "MQTT_PORT=1883" in after) check("no credentials are invented", "MQTT_USERNAME" not in after and "MQTT_PASSWORD" not in after) # stable and canary are both the sway line. A machine that has just taken # the Plasma package and is left pointed at one of them is handed the old # desktop back by the next release there: the upgrade undoing itself. check("canary moves onto the v2 channel", "CHANNEL=classroom-v2" in after) after, _ = migrate("CHANNEL=stable\nUI_SCALE=1.0\n") check("and so does stable", "CHANNEL=classroom-v2" in after) print() print("4. running it twice changes nothing the second time") once, _ = migrate(BEFORE) twice, _ = migrate(once) check("the second run is a no-op", once == twice) print() print(f"{passed} passed, {failed} failed") sys.exit(1 if failed else 0) PY