fix: make an existing classroom converge into the Plasma appliance
A real room upgraded to 0.1.45 and came up as stock Debian Plasma with the old Waybar across the top, mako still catching notifications, and a lock screen on an account that has no password. The cause was one line, and it was in the lab: the harness handed the session under test XDG_CONFIG_DIRS by hand, so every result it had ever produced was a result about a machine somebody had already configured. On a machine SDDM starts the session through PAM, PAM does not read /etc/environment.d, and Debian's own plasma-workspace env.sh then set the variable from scratch — so the classroom's settings directory was not in the cascade at all. No layout, no lock policy, no Kiosk restrictions. The settings now arrive the way Plasma delivers them, through a file in /etc/xdg/plasma-workspace/env. Around that: the look and feel is immutable on its own key so a stale profile cannot answer for it; the palette is composed into the package, because naming a colour scheme does not produce one and the dark bar had been coming from a technician's hand; the desktop reset converges another desktop's appearance state and keeps the room's own; and classroom-migrate stops, deactivates and masks the old shell, including the D-Bus activation that brought mako back while the panel was down — found only by killing the panel on a migrated machine. device.conf loses COMPOSITOR and comes off the hyprland channel, which would otherwise have handed the room the experiment back. The lab stops flattering the package: it restarts the machine, lets SDDM actually autologin, and asks a fresh install and a machine upgraded from the Hyprland package the same sixty questions. Both answer identically, twice, and the panel still comes back from eight SIGKILLs. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
6050144e5d
commit
926668dc9b
Executable
+236
@@ -0,0 +1,236 @@
|
||||
#!/bin/bash
|
||||
# The upgrade that failed, reproduced on purpose.
|
||||
#
|
||||
# A real classroom does not arrive new, and the clean-install test was
|
||||
# passing on a machine that had never been anything else. This builds a
|
||||
# machine that has: the Hyprland experiment package actually installed,
|
||||
# with sway, waybar and mako behind it, a Waybar activated through
|
||||
# graphical-session, an autostart entry, a device.conf still asking for a
|
||||
# compositor that is gone, and a Plasma profile belonging to a different
|
||||
# desktop. Then it installs the classroom package over the top and asks
|
||||
# whether the room converged.
|
||||
#
|
||||
# tools/lab/dirty-upgrade [NEW.deb] [OLD.deb]
|
||||
# CHECK_ARGS=--restart tools/lab/dirty-upgrade ask twice, either side
|
||||
# of a logout
|
||||
#
|
||||
# The container is restarted between the install and the session, because
|
||||
# a room takes its upgrade and reboots.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
LAB="$ROOT/tools/lab/lab"
|
||||
NAME="${LAB_NAME:-classroom-v2-lab}"
|
||||
|
||||
NEW="${1:-$(ls -t "$ROOT"/dist/ekklesia-classroom_*_amd64.deb | grep -v '~' | head -1)}"
|
||||
OLD="${2:-$ROOT/dist/ekklesia-classroom_0.1.45~hypr3_amd64.deb}"
|
||||
|
||||
step() { printf '\n\033[1m== %s\033[0m\n' "$1"; }
|
||||
|
||||
step "a fresh machine"
|
||||
"$LAB" up >/dev/null
|
||||
|
||||
docker exec "$NAME" sh -c '
|
||||
printf "#!/bin/sh\nexit 101\n" > /usr/sbin/policy-rc.d
|
||||
chmod +x /usr/sbin/policy-rc.d
|
||||
apt-get update -qq >/dev/null 2>&1
|
||||
# The old package schedules a provisioning run that would go to
|
||||
# backports for Hyprland. The lab is not testing that.
|
||||
systemctl mask ekklesia-classroom-provision.service >/dev/null 2>&1'
|
||||
|
||||
step "the old classroom: $(basename "$OLD")"
|
||||
docker exec "$NAME" sh -c "
|
||||
DEBIAN_FRONTEND=noninteractive dpkg -i '/srv/classroom/${OLD#$ROOT/}' >/dev/null 2>&1
|
||||
DEBIAN_FRONTEND=noninteractive apt-get -y -qq -f install >/dev/null 2>&1
|
||||
DEBIAN_FRONTEND=noninteractive dpkg --configure ekklesia-classroom >/dev/null 2>&1
|
||||
true" || true
|
||||
docker exec "$NAME" dpkg-query -W -f=' installed: ${Version}\n' ekklesia-classroom
|
||||
|
||||
step "and everything a used machine accumulates"
|
||||
docker exec -i "$NAME" bash <<'DIRT'
|
||||
set -e
|
||||
|
||||
# Waybar and mako, activated the way the real room had them. Neither
|
||||
# package ships a unit in trixie, so the units themselves are part of what
|
||||
# somebody added — and one of the three activation directories is the one
|
||||
# the broken room actually used.
|
||||
for unit in waybar mako; do
|
||||
cat > "/etc/systemd/user/$unit.service" <<UNIT
|
||||
[Unit]
|
||||
Description=$unit (left over from the old classroom shell)
|
||||
PartOf=graphical-session.target
|
||||
After=graphical-session.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/$unit
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=graphical-session.target
|
||||
UNIT
|
||||
done
|
||||
|
||||
for dir in /etc/systemd/user /etc/xdg/systemd/user \
|
||||
/home/classroom/.config/systemd/user; do
|
||||
mkdir -p "$dir/graphical-session.target.wants"
|
||||
ln -sf /etc/systemd/user/waybar.service \
|
||||
"$dir/graphical-session.target.wants/waybar.service"
|
||||
ln -sf /etc/systemd/user/mako.service \
|
||||
"$dir/graphical-session.target.wants/mako.service"
|
||||
done
|
||||
|
||||
# An autostart entry as well, for a bar that is started two ways.
|
||||
mkdir -p /home/classroom/.config/autostart
|
||||
cat > /home/classroom/.config/autostart/waybar.desktop <<'ENTRY'
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Waybar
|
||||
Exec=waybar
|
||||
ENTRY
|
||||
|
||||
# The room still asking for a compositor that is gone, with a broker
|
||||
# password in the same file to prove the migration does not rewrite it.
|
||||
sed -i 's/^CHANNEL=.*/CHANNEL=hyprland/' /etc/ekklesia-classroom/device.conf
|
||||
{
|
||||
echo "COMPOSITOR=hyprland"
|
||||
echo "HYPR_APPEARANCE=visual"
|
||||
} >> /etc/ekklesia-classroom/device.conf
|
||||
sed -i 's/^MQTT_HOST=.*/MQTT_HOST=broker.example.invalid/' /etc/ekklesia-classroom/device.conf
|
||||
sed -i 's/^MQTT_PASSWORD=.*/MQTT_PASSWORD=keep-me-exactly/' /etc/ekklesia-classroom/device.conf
|
||||
|
||||
# A Plasma profile from a different desktop: someone else's palette, in
|
||||
# both of the places that outrank the package.
|
||||
mkdir -p /home/classroom/.config/kdedefaults
|
||||
cat > /home/classroom/.config/kdedefaults/kdeglobals <<'KDE'
|
||||
[General]
|
||||
ColorScheme=BreezeLight
|
||||
|
||||
[Icons]
|
||||
Theme=breeze
|
||||
|
||||
[KDE]
|
||||
LookAndFeelPackage=org.kde.breeze.desktop
|
||||
KDE
|
||||
cat > /home/classroom/.config/kdeglobals <<'KDE'
|
||||
[General]
|
||||
ColorScheme=BreezeLight
|
||||
|
||||
[Colors:Window]
|
||||
BackgroundNormal=239,240,241
|
||||
ForegroundNormal=35,38,41
|
||||
|
||||
[KDE]
|
||||
LookAndFeelPackage=org.kde.breeze.desktop
|
||||
KDE
|
||||
|
||||
# And a stock panel, already built, of the kind the broken upgrade made.
|
||||
cat > /home/classroom/.config/plasma-org.kde.plasma.desktop-appletsrc <<'PANEL'
|
||||
[Containments][1]
|
||||
plugin=org.kde.plasma.folder
|
||||
|
||||
[Containments][2]
|
||||
plugin=org.kde.panel
|
||||
|
||||
[Containments][2][Applets][3]
|
||||
plugin=org.kde.plasma.kickoff
|
||||
|
||||
[Containments][2][Applets][4]
|
||||
plugin=org.kde.plasma.pager
|
||||
|
||||
[Containments][2][Applets][5]
|
||||
plugin=org.kde.plasma.icontasks
|
||||
|
||||
[Containments][2][Applets][5][Configuration][General]
|
||||
launchers=applications:systemsettings.desktop,applications:org.kde.discover.desktop,preferred://browser
|
||||
|
||||
[Containments][2][Applets][6]
|
||||
plugin=org.kde.plasma.systemtray
|
||||
PANEL
|
||||
|
||||
# A user-level power profile that would suspend the room.
|
||||
cat > /home/classroom/.config/powermanagementprofilesrc <<'POWER'
|
||||
[AC][SuspendSession]
|
||||
idleTime=1800000
|
||||
suspendType=1
|
||||
|
||||
[AC][DPMSControl]
|
||||
idleTime=600
|
||||
POWER
|
||||
|
||||
chown -R classroom:classroom /home/classroom/.config
|
||||
echo " dirt in place"
|
||||
DIRT
|
||||
|
||||
step "the new classroom: $(basename "$NEW")"
|
||||
docker exec "$NAME" sh -c '
|
||||
systemctl unmask ekklesia-classroom-provision.service >/dev/null 2>&1 || true'
|
||||
docker exec "$NAME" sh -c "
|
||||
DEBIAN_FRONTEND=noninteractive dpkg -i '/srv/classroom/${NEW#$ROOT/}' 2>&1 \
|
||||
| grep -E '^\[classroom-migrate\]' || true
|
||||
DEBIAN_FRONTEND=noninteractive apt-get -y -qq -f install >/dev/null 2>&1
|
||||
DEBIAN_FRONTEND=noninteractive dpkg --configure ekklesia-classroom >/dev/null 2>&1
|
||||
rm -f /usr/sbin/policy-rc.d
|
||||
true" || true
|
||||
docker exec "$NAME" dpkg-query -W -f=' installed: ${Version} ${db:Status-Abbrev}\n' ekklesia-classroom
|
||||
|
||||
step "and the room reboots"
|
||||
"$LAB" reboot
|
||||
|
||||
step "the session"
|
||||
"$LAB" session >/dev/null
|
||||
sleep 6
|
||||
|
||||
step "did it converge?"
|
||||
converged=0
|
||||
"$ROOT/tools/lab/converged-check" ${CHECK_ARGS:-} || converged=1
|
||||
|
||||
step "and again, installed a second time over itself"
|
||||
# An update that changes nothing must change nothing. The migration runs
|
||||
# again here, over a machine that is already converged.
|
||||
docker exec "$NAME" sh -c "
|
||||
DEBIAN_FRONTEND=noninteractive dpkg -i '/srv/classroom/${NEW#$ROOT/}' >/dev/null 2>&1
|
||||
true" || true
|
||||
before=$(docker exec "$NAME" sh -c 'cat /etc/ekklesia-classroom/device.conf | sha256sum')
|
||||
"$LAB" plasma-down >/dev/null
|
||||
"$LAB" plasma-up >/dev/null
|
||||
sleep 4
|
||||
after=$(docker exec "$NAME" sh -c 'cat /etc/ekklesia-classroom/device.conf | sha256sum')
|
||||
[[ "$before" == "$after" ]] \
|
||||
&& echo " device.conf unchanged by the second install" \
|
||||
|| { echo " DEVICE.CONF CHANGED ON REINSTALL"; converged=1; }
|
||||
"$ROOT/tools/lab/converged-check" || converged=1
|
||||
|
||||
step "and the room's own theme choice survives a logout"
|
||||
# Deleting the look and feel defaults every login was measured to make
|
||||
# Plasma re-apply the look and feel and throw the room's chosen palette
|
||||
# away with it. This is the check that stops that coming back.
|
||||
# With the session's own cascade. plasma-apply-colorscheme writes the
|
||||
# chosen name into ~/.config/kdedefaults, which is in XDG_CONFIG_DIRS for
|
||||
# a session and not for a bare command, so asking without it reports
|
||||
# nothing and means nothing.
|
||||
theme() {
|
||||
local dirs
|
||||
dirs=$(docker exec "$NAME" bash -c 'p=$(pgrep -x plasmashell | head -1); tr "\0" "\n" < /proc/$p/environ | grep "^XDG_CONFIG_DIRS=" | cut -d= -f2-')
|
||||
docker exec -u classroom -e XDG_RUNTIME_DIR=/run/user/1000 \
|
||||
-e DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus \
|
||||
-e WAYLAND_DISPLAY=wayland-0 -e "XDG_CONFIG_DIRS=$dirs" "$NAME" \
|
||||
/usr/local/bin/classroom-theme "$1" 2>/dev/null | tail -1
|
||||
}
|
||||
theme light >/dev/null
|
||||
"$LAB" plasma-down >/dev/null
|
||||
"$LAB" plasma-up >/dev/null
|
||||
sleep 4
|
||||
chosen=$(theme show)
|
||||
if [[ "$chosen" == EkklesiaLight ]]; then
|
||||
echo " light survived the logout"
|
||||
else
|
||||
echo " THE ROOM'S THEME CHOICE WAS LOST: $chosen"
|
||||
converged=1
|
||||
fi
|
||||
theme dark >/dev/null
|
||||
echo " back to $(theme show)"
|
||||
|
||||
step "and it still recovers"
|
||||
"$ROOT/tools/lab/recovery-check" || converged=1
|
||||
|
||||
exit "$converged"
|
||||
Reference in New Issue
Block a user