test: rehearse the stable rooms' upgrade on every canary, and list Sunday's checks

update_canary.sh now also validates the exact package it is about to
publish as an upgrade from whatever stable names — 0.2.2 today, several
releases behind the laptop's canary. A package that upgrades the laptop
and breaks the desktops has to fail before it is published.

The lab print check runs in the Plasma lab as well as a plain container:
its rig lived in /tmp, which is a tmpfs there that docker cp cannot write
into, and its inline-PDF server shared a port with another test server.

docs/testing.md has the physical checks for 0.2.6.1 — paper through
Elmer, the real keyboard's zoom keys, the machine's graphics through
--diagnose, sign-out with a real account — before stable moves.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
ekklesia
2026-09-23 19:38:04 -05:00
co-authored by Claude Opus 5.5
parent 88ea3e86fd
commit 1d9da3bca0
5 changed files with 130 additions and 8 deletions
+87
View File
@@ -574,3 +574,90 @@ ifupdown stanza is already gone. **Do not run `--adopt` on it.**
- [ ] `nmcli -g 802-11-wireless.hidden connection show "ROGC Classroom"` - [ ] `nmcli -g 802-11-wireless.hidden connection show "ROGC Classroom"`
still reports `yes` still reports `yes`
- [ ] The laptop still reconnects after a reboot - [ ] The laptop still reconnects after a reboot
## Wonder Ink — 0.2.6.1, before it goes to stable
Everything below was done in the lab except what a lab cannot do: paper,
a classroom's own graphics, a real keyboard, its network and its screen.
Those are these checks. Stable stays on 0.2.2 until they pass.
Before starting, on each machine: `dpkg-query -W ekklesia-classroom`
must say `0.2.6.1`. The desktops only get it once stable is moved, so on
a desktop install it by hand for the test:
```
curl -fsSL -o c.deb https://updates.riverofglorymn.org/canary/latest.deb
sha256sum c.deb # must match .../canary/manifest.json
sudo apt-get install -y ./c.deb && sudo reboot
```
This does **not** move the machine to canary (see provisioning.md): its
next automatic update follows stable again, which is still 0.2.2 — so
after testing, either promote or reinstall 0.2.2 the same way.
### Little Waders — printing through Elmer
- [ ] Open Wonder Ink, sign in, open a lesson with a worksheet
- [ ] Click the worksheet; it opens as a tab named after the file, with a
document icon
- [ ] Ctrl+P (or right-click → Print…): the preview shows the worksheet's
own pages, page count right, ‹ › turn them
- [ ] Printer shows **Elmer**, preselected
- [ ] Print → a notification "Sent to the printer — … — Elmer", Wonder Ink
stays open on the lesson
- [ ] **The worksheet comes out of Elmer**, every page, right size, in colour
- [ ] Pages `2`, Copies `2`, Black and white → exactly two grey copies of
page 2 come out
- [ ] Print a normal lesson page the same way; it comes out, Letter
- [ ] Open the preview and press Cancel → nothing prints
- [ ] Print three times in a row → three printouts, no crash
- [ ] `lpstat -W completed -o` shows each job; `journalctl --user -b | grep
"printed '"` shows Wonder Ink's line for each
### Size and zoom
- [ ] A lesson in Wonder Ink is the same size as the same page in the
browser on that machine (it was smaller in 0.2.2)
- [ ] **Ctrl and +** (the real keyboard), **Ctrl and −**, **Ctrl and 0**
change it; a "Lesson size" badge appears
- [ ] Open a second tab: it is the same size; the − 100% + and Print
buttons are at the top right
- [ ] Close Wonder Ink and open it again: the size is remembered
- [ ] A worksheet tab's own − / + (in the PDF toolbar) zoom it
- [ ] A lesson video still goes fullscreen and comes back with the tabs
### Loading, and the machine's graphics
From the maintenance terminal (Meta+Ctrl+Alt+Shift+K), in the session:
```
classroom-wonderink --diagnose
```
- [ ] Note the **GPU line**. A driver's name is right. "none … drawing in
software" means QtWebEngine has no GPU on this machine — compare
`chrome://gpu` in the browser; if Chrome has one, that is the cause of
slow lessons and is the next thing to fix
- [ ] Note the sign-in time (the lab: about 2.9 s wired). Much slower here
with a driver named above points at the network
- [ ] `free -m`: Wonder Ink needs about 610 MB plus 115 MB per extra tab
### Signing out
- [ ] Sign in, close Wonder Ink, open it again → **signed out**. (Cookies
are session-only and measured so; the site's Local Storage is not
cleared, which is the one way this could fail and has never been
tested with a real account)
### After a reboot, and the other rooms
- [ ] Reboot Little Waders: it comes up as the classroom, Wonder Ink opens
- [ ] On one other desktop, 0.2.6.1 installed: the browser, the DVD player
where there is a drive, Parent Call where enabled, sound through the
television, the panel order — all as on 0.2.2
- [ ] **Presentations is not on the panel** of any desktop
(`grep PRESENTATIONS_ENABLED /etc/ekklesia-classroom/device.conf`
says `false`)
If anything here fails, leave the desktops on stable 0.2.2 and fix it in
0.2.6.2 on canary.
+3
View File
@@ -245,6 +245,9 @@ check("a spent version is refused before the build",
canary.index("release_check_new_version") < canary.index("build-release.sh")) canary.index("release_check_new_version") < canary.index("build-release.sh"))
check("and again after the gate", check("and again after the gate",
validation < canary.rindex("release_check_new_version") < first_upload) validation < canary.rindex("release_check_new_version") < first_upload)
check("the stable rooms' upgrade is rehearsed on the same file before publishing",
"VALIDATION_CHANNEL=stable" in canary
and canary.index("VALIDATION_CHANNEL=stable") < first_upload)
check("the published channel is read back afterwards", check("the published channel is read back afterwards",
canary.index("release_verify_channel canary") > canary.index("release_publish_manifest canary")) canary.index("release_verify_channel canary") > canary.index("release_publish_manifest canary"))
# The gate checks convergence, not just that the package installs: an # The gate checks convergence, not just that the package installs: an
+20 -7
View File
@@ -19,8 +19,16 @@ COMMAND="${WONDERINK:-/usr/local/bin/classroom-wonderink}"
lab() { docker exec -i "$NAME" bash -c "$1"; } lab() { docker exec -i "$NAME" bash -c "$1"; }
docker cp "$ROOT/tools/lab/wonderink-print-rig.py" "$NAME:/tmp/wonderink-print-rig.py" >/dev/null # Not /tmp: in the Plasma lab /tmp is a tmpfs, and docker cp writes into
lab 'chmod a+r /tmp/wonderink-print-rig.py' # the layer underneath it, where nothing can see the file. Measured — the
# first run there "failed" every scenario for exactly that reason.
RIG=/opt/wonderink-check/wonderink-print-rig.py
lab 'mkdir -p /opt/wonderink-check'
docker cp "$ROOT/tools/lab/wonderink-print-rig.py" "$NAME:$RIG" >/dev/null
lab "chmod -R a+rX /opt/wonderink-check && test -r $RIG" || {
echo "could not put the rig in the lab" >&2
exit 1
}
# The church's printer network is fenced off inside the lab. Checked, not # The church's printer network is fenced off inside the lab. Checked, not
# assumed: a lab job must never be able to reach the real Elmer. # assumed: a lab job must never be able to reach the real Elmer.
@@ -52,11 +60,16 @@ lpadmin -p Refusing -E -v file:///dev/null -m "$driver" -D "LAB: refuses jobs" 2
cupsreject Refusing cupsreject Refusing
id teacher >/dev/null 2>&1 || useradd -m teacher id teacher >/dev/null 2>&1 || useradd -m teacher
install -d -o teacher -m 700 /tmp/teacher-rt install -d -o teacher -m 700 /tmp/teacher-rt
# An inline PDF, served the way a site shows one rather than sends it. # An inline PDF, served the way a site shows one rather than sends it. Its
mkdir -p /tmp/www # own port and directory: a lab often has another test server on 8088.
pgrep -f "http.server 8088" >/dev/null || (cd /tmp/www && setsid python3 -m http.server 8088 >/dev/null 2>&1 &) mkdir -p /opt/wonderink-check/www
' || { echo "could not prepare the lab's CUPS" >&2; exit 1; } ' || { echo "could not prepare the lab's CUPS" >&2; exit 1; }
# Started detached. A background process started inside a docker exec
# dies with it in the Plasma lab, whose PID 1 is systemd.
lab 'pgrep -f "[h]ttp.server 8089" >/dev/null' || \
docker exec -d -w /opt/wonderink-check/www "$NAME" python3 -m http.server 8089
passed=0 passed=0
failed=0 failed=0
@@ -76,7 +89,7 @@ run() {
before=$(lab 'lpstat -W all -o 2>/dev/null | wc -l') before=$(lab 'lpstat -W all -o 2>/dev/null | wc -l')
out=$(lab "su teacher -c 'cd /tmp; XDG_RUNTIME_DIR=/tmp/teacher-rt QT_QPA_PLATFORM=offscreen \ out=$(lab "su teacher -c 'cd /tmp; XDG_RUNTIME_DIR=/tmp/teacher-rt QT_QPA_PLATFORM=offscreen \
QTWEBENGINE_CHROMIUM_FLAGS=\"--no-sandbox --disable-gpu\" \ QTWEBENGINE_CHROMIUM_FLAGS=\"--no-sandbox --disable-gpu\" \
timeout 120 python3 /tmp/wonderink-print-rig.py $COMMAND $1 2>&1'; echo \"exit=\$?\"") timeout 120 python3 $RIG $COMMAND $1 2>&1'; echo \"exit=\$?\"")
sleep 3 sleep 3
jobs=$(lab "lpstat -W all -o | tail -n +$((before + 1)) | awk '{print \$1}' | tr '\n' ' '") jobs=$(lab "lpstat -W all -o | tail -n +$((before + 1)) | awk '{print \$1}' | tr '\n' ' '")
job_options() { lab "grep -E '\\[Job ${1#*-}\\] argv\\[(4|5)\\]' /var/log/cups/error_log"; } job_options() { lab "grep -E '\\[Job ${1#*-}\\] argv\\[(4|5)\\]' /var/log/cups/error_log"; }
@@ -92,7 +105,7 @@ scenarios=("$@")
options bad-range broken cancel page-closes-itself close-window page-prints-again repeat) options bad-range broken cancel page-closes-itself close-window page-prints-again repeat)
# The inline scenario needs a PDF to serve; the worksheet scenario writes one. # The inline scenario needs a PDF to serve; the worksheet scenario writes one.
lab 'test -f /tmp/www/ark.pdf' || { run pdf >/dev/null; lab 'cp "/home/teacher/Downloads/Wonder Ink/Colour the Ark.pdf" /tmp/www/ark.pdf; chmod a+r /tmp/www/ark.pdf'; } lab 'test -f /opt/wonderink-check/www/ark.pdf' || { run pdf >/dev/null; lab 'cp "/home/teacher/Downloads/Wonder Ink/Colour the Ark.pdf" /opt/wonderink-check/www/ark.pdf; chmod a+r /opt/wonderink-check/www/ark.pdf'; }
for scenario in "${scenarios[@]}"; do for scenario in "${scenarios[@]}"; do
echo "$scenario" echo "$scenario"
+1 -1
View File
@@ -121,7 +121,7 @@ elif scenario == "pdf-viewer-button":
window.open_pdf(worksheet()); view = window.current() window.open_pdf(worksheet()); view = window.current()
QTimer.singleShot(2500, lambda: (actions["web"](view), view.page().printRequested.emit())) QTimer.singleShot(2500, lambda: (actions["web"](view), view.page().printRequested.emit()))
elif scenario == "inline": elif scenario == "inline":
view = window.open_tab(); view.load(QUrl("http://127.0.0.1:8088/ark.pdf")) view = window.open_tab(); view.load(QUrl("http://127.0.0.1:8089/ark.pdf"))
QTimer.singleShot(4000, lambda: (stamp(f"inline mime={window._mime.get(view)!r} shows_pdf={window._shows_pdf(view)}"), QTimer.singleShot(4000, lambda: (stamp(f"inline mime={window._mime.get(view)!r} shows_pdf={window._shows_pdf(view)}"),
QTimer.singleShot(0, lambda: window.print_view(view)), QTimer.singleShot(0, lambda: window.print_view(view)),
when(ready, lambda: (stamp(f"worksheet tab opened: tabs={window._tabs.count()} current_is_pdf={window.current() in window._pdf_views}"), press_print()), "inline preview"))) when(ready, lambda: (stamp(f"worksheet tab opened: tabs={window._tabs.count()} current_is_pdf={window.current() in window._pdf_views}"), press_print()), "inline preview")))
+19
View File
@@ -56,6 +56,25 @@ fi
"$ROOT/tools/validate-trixie-release.sh" "${validation_args[@]}" "$ROOT/tools/validate-trixie-release.sh" "${validation_args[@]}"
# And the stable rooms' own path, on the same file. Every canary is a
# stable release in waiting, and the desktops are not on the previous
# canary: they are on whatever stable names, which can be several
# releases back (0.2.2 under 0.2.6.1). A package that upgrades the
# laptop and breaks the desktops has to fail here, not on a Sunday.
if [[ -f "$RELEASE_LOCAL/stable/manifest.json" ]]; then
stable_package_name="$(python3 -c \
'import json, sys; print(json.load(open(sys.argv[1]))["package"].rsplit("/", 1)[-1])' \
"$RELEASE_LOCAL/stable/manifest.json")"
stable_package="$RELEASE_LOCAL/packages/$stable_package_name"
if [[ -f "$stable_package" && "$stable_package" != "${validation_args[1]:-}" ]]; then
echo
echo "Rehearsing the stable rooms' upgrade from $stable_package_name"
VALIDATION_CHANNEL=stable \
"$ROOT/tools/validate-trixie-release.sh" "$package_path" "$stable_package"
fi
fi
# Checked again after the gate, which takes long enough for somebody else # Checked again after the gate, which takes long enough for somebody else
# to have published in the meantime. # to have published in the meantime.
release_check_new_version "$version" release_check_new_version "$version"