Release 0.1.37 deployment prep
This commit is contained in:
Executable
+104
@@ -0,0 +1,104 @@
|
||||
#!/bin/bash
|
||||
set -uo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
PYTHONPATH="$ROOT/lib" python3 - <<'PY'
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from classroom_config import dock_items, ensure_device_config, read_device_config
|
||||
|
||||
passed = failed = 0
|
||||
|
||||
|
||||
def check(label, condition, detail=""):
|
||||
global passed, failed
|
||||
if condition:
|
||||
print(f" ok {label}")
|
||||
passed += 1
|
||||
else:
|
||||
print(f" FAIL {label}")
|
||||
if detail:
|
||||
print(f" {detail}")
|
||||
failed += 1
|
||||
|
||||
|
||||
print("1. dock capabilities")
|
||||
cases = [
|
||||
({}, ("browser",)),
|
||||
({"DVD_ENABLED": "true"}, ("browser", "dvd")),
|
||||
({"LIVE_ENABLED": "true"}, ("browser", "live")),
|
||||
(
|
||||
{"DVD_ENABLED": "true", "LIVE_ENABLED": "true"},
|
||||
("browser", "dvd", "live"),
|
||||
),
|
||||
]
|
||||
|
||||
for config, expected in cases:
|
||||
actual = dock_items(config)
|
||||
check(f"{config or 'both disabled'} -> {expected}", actual == expected, actual)
|
||||
|
||||
check(
|
||||
"false spellings do not leave placeholder items",
|
||||
dock_items({"DVD_ENABLED": "false", "LIVE_ENABLED": "off"}) == ("browser",),
|
||||
)
|
||||
check(
|
||||
"existing dock order is browser, DVD, LIVE",
|
||||
dock_items({"DVD_ENABLED": "yes", "LIVE_ENABLED": "1"})
|
||||
== ("browser", "dvd", "live"),
|
||||
)
|
||||
|
||||
print()
|
||||
print("2. fresh device configuration")
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
path = Path(directory) / "etc" / "device.conf"
|
||||
ensure_device_config(path)
|
||||
config = read_device_config(path)
|
||||
check("fresh config is created", path.is_file())
|
||||
check("DVD defaults off", config.get("DVD_ENABLED") == "false")
|
||||
check("LIVE defaults off", config.get("LIVE_ENABLED") == "false")
|
||||
check("MQTT defaults off", config.get("MQTT_ENABLED") == "false")
|
||||
check("stable remains the fresh-install default", config.get("CHANNEL") == "stable")
|
||||
|
||||
print()
|
||||
print("3. upgrade from 0.1.36")
|
||||
old = """# endpoint configuration from 0.1.36
|
||||
UPDATE_URL=https://updates.example.test
|
||||
CHANNEL=canary
|
||||
DVD_ENABLED=true
|
||||
UI_SCALE=1.25
|
||||
MQTT_ENABLED=true
|
||||
MQTT_HOST=broker.example.test
|
||||
MQTT_PORT=1884
|
||||
MQTT_USERNAME=room
|
||||
MQTT_PASSWORD=secret
|
||||
CUSTOM_SETTING=keep-me
|
||||
"""
|
||||
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
path = Path(directory) / "device.conf"
|
||||
path.write_text(old)
|
||||
|
||||
ensure_device_config(path)
|
||||
first = path.read_text()
|
||||
config = read_device_config(path)
|
||||
|
||||
check("old bytes remain at the start", first.startswith(old))
|
||||
check("channel survives", config.get("CHANNEL") == "canary")
|
||||
check("DVD choice survives", config.get("DVD_ENABLED") == "true")
|
||||
check("UI scale survives", config.get("UI_SCALE") == "1.25")
|
||||
check("MQTT host survives", config.get("MQTT_HOST") == "broker.example.test")
|
||||
check("MQTT password survives", config.get("MQTT_PASSWORD") == "secret")
|
||||
check("unknown settings survive", config.get("CUSTOM_SETTING") == "keep-me")
|
||||
check("LIVE is appended once", first.count("LIVE_ENABLED=") == 1)
|
||||
|
||||
ensure_device_config(path)
|
||||
second = path.read_text()
|
||||
check("reinstall makes no further change", second == first)
|
||||
check("LIVE still occurs once", second.count("LIVE_ENABLED=") == 1)
|
||||
|
||||
print()
|
||||
print(f"passed: {passed} failed: {failed}")
|
||||
raise SystemExit(1 if failed else 0)
|
||||
PY
|
||||
Executable
+116
@@ -0,0 +1,116 @@
|
||||
#!/bin/bash
|
||||
set -uo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
SCRIPT="$ROOT/scripts/classroom-printer-setup"
|
||||
WORK="$(mktemp -d)"
|
||||
trap 'rm -rf "$WORK"' EXIT
|
||||
|
||||
passed=0
|
||||
failed=0
|
||||
|
||||
check() {
|
||||
local label="$1" expected="$2" actual="$3"
|
||||
if [[ "$actual" == *"$expected"* ]]; then
|
||||
echo " ok $label"
|
||||
passed=$((passed + 1))
|
||||
else
|
||||
echo " FAIL $label"
|
||||
echo " expected to contain: $expected"
|
||||
echo " got: $actual"
|
||||
failed=$((failed + 1))
|
||||
fi
|
||||
}
|
||||
|
||||
check_absent() {
|
||||
local label="$1" unexpected="$2" actual="$3"
|
||||
if [[ "$actual" != *"$unexpected"* ]]; then
|
||||
echo " ok $label"
|
||||
passed=$((passed + 1))
|
||||
else
|
||||
echo " FAIL $label"
|
||||
echo " expected not to contain: $unexpected"
|
||||
echo " got: $actual"
|
||||
failed=$((failed + 1))
|
||||
fi
|
||||
}
|
||||
|
||||
mkdir -p "$WORK/bin"
|
||||
|
||||
for command in lpadmin cupsenable cupsaccept; do
|
||||
cat > "$WORK/bin/$command" <<'SHIM'
|
||||
#!/bin/bash
|
||||
echo "$(basename "$0") $*" >> "$CUPS_LOG"
|
||||
SHIM
|
||||
done
|
||||
|
||||
cat > "$WORK/bin/lpstat" <<'SHIM'
|
||||
#!/bin/bash
|
||||
[[ "${FAKE_CUPS_RUNNING:-true}" == "true" ]]
|
||||
SHIM
|
||||
|
||||
cat > "$WORK/bin/lpinfo" <<'SHIM'
|
||||
#!/bin/bash
|
||||
if [[ "${FAKE_DRIVER_PRESENT:-true}" == "true" ]]; then
|
||||
echo "foomatic-db-compressed-ppds:0/ppd/foomatic-ppd/Generic-PCL_6_PCL_XL_Printer-pxlcolor.ppd Generic PCL 6/PCL XL Printer Foomatic/pxlcolor (recommended)"
|
||||
else
|
||||
echo "everywhere IPP Everywhere"
|
||||
fi
|
||||
SHIM
|
||||
|
||||
chmod +x "$WORK/bin"/*
|
||||
export PATH="$WORK/bin:$PATH"
|
||||
export CUPS_LOG="$WORK/cups.log"
|
||||
export CLASSROOM_PRINTER_SETUP_TESTING=true
|
||||
|
||||
run() {
|
||||
"$SCRIPT" "$@" 2>&1
|
||||
}
|
||||
|
||||
echo "1. creates the explicit classroom queue"
|
||||
: > "$CUPS_LOG"
|
||||
out="$(run 10.20.30.40)"
|
||||
log="$(cat "$CUPS_LOG")"
|
||||
check "uses AppSocket by IP" "-v socket://10.20.30.40:9100" "$log"
|
||||
check "uses Trixie's generic color PCL 6 driver" \
|
||||
"Generic-PCL_6_PCL_XL_Printer-pxlcolor.ppd" "$log"
|
||||
check "uses one stable queue name" "lpadmin -p Classroom_Color" "$log"
|
||||
check "enables the queue" "cupsenable Classroom_Color" "$log"
|
||||
check "accepts jobs" "cupsaccept Classroom_Color" "$log"
|
||||
check "defaults the queue" "lpadmin -d Classroom_Color" "$log"
|
||||
check "reports offline-safe behavior" "may remain offline" "$out"
|
||||
|
||||
echo
|
||||
echo "2. repeated setup updates the same queue"
|
||||
run 10.20.30.40 >/dev/null
|
||||
configs="$(grep -c '^lpadmin -p Classroom_Color' "$CUPS_LOG")"
|
||||
check "two runs configured the same queue twice" "2" "$configs"
|
||||
check_absent "no generated queue suffix" "Classroom_Color_" "$(cat "$CUPS_LOG")"
|
||||
|
||||
echo
|
||||
echo "3. explicit URIs and non-default setup"
|
||||
: > "$CUPS_LOG"
|
||||
run --no-default ipp://printer.example.test/ipp/print >/dev/null
|
||||
log="$(cat "$CUPS_LOG")"
|
||||
check "keeps an explicit IPP URI" "-v ipp://printer.example.test/ipp/print" "$log"
|
||||
check_absent "--no-default omits the default change" "lpadmin -d" "$log"
|
||||
|
||||
echo
|
||||
echo "4. errors are useful"
|
||||
if out="$(run smb://printer/share)"; then
|
||||
check "unsupported scheme fails" "failure" "unexpected success"
|
||||
else
|
||||
check "unsupported scheme is named" "unsupported printer URI scheme" "$out"
|
||||
fi
|
||||
|
||||
export FAKE_DRIVER_PRESENT=false
|
||||
if out="$(run 10.20.30.40)"; then
|
||||
check "missing driver fails" "failure" "unexpected success"
|
||||
else
|
||||
check "missing driver is explained" "generic color PCL 6 driver is not installed" "$out"
|
||||
fi
|
||||
unset FAKE_DRIVER_PRESENT
|
||||
|
||||
echo
|
||||
echo "passed: $passed failed: $failed"
|
||||
[[ "$failed" -eq 0 ]]
|
||||
Executable
+74
@@ -0,0 +1,74 @@
|
||||
#!/bin/bash
|
||||
set -uo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
python3 - "$ROOT" <<'PY'
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
root = Path(sys.argv[1])
|
||||
control = (root / "packaging/debian/control.template").read_text()
|
||||
postinst = (root / "packaging/debian/postinst").read_text()
|
||||
provision = (root / "scripts/classroom-provision").read_text()
|
||||
units = "\n".join(path.read_text() for path in (root / "systemd").rglob("*.service"))
|
||||
canary = (root / "update_canary.sh").read_text()
|
||||
policy = json.loads(
|
||||
(root / "config/chrome/policies/managed/ekklesia-classroom.json").read_text()
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
|
||||
depends = re.search(r"^Depends: (.*)$", control, re.MULTILINE).group(1).split(", ")
|
||||
|
||||
print("1. Trixie package contract")
|
||||
for package in (
|
||||
"network-manager",
|
||||
"cups",
|
||||
"cups-client",
|
||||
"cups-filters",
|
||||
"foomatic-db-compressed-ppds",
|
||||
):
|
||||
check(f"depends on {package}", package in depends)
|
||||
|
||||
print()
|
||||
print("2. networking service behavior")
|
||||
check("NetworkManager is explicitly enabled", "systemctl enable NetworkManager.service || true" in postinst)
|
||||
check("NetworkManager start is best effort", "systemctl start NetworkManager.service || true" in postinst)
|
||||
check("online waiter is disabled", "systemctl disable NetworkManager-wait-online.service || true" in postinst)
|
||||
check("no classroom NetworkManager config overrides ifupdown", not (root / "config/network").exists())
|
||||
|
||||
print()
|
||||
print("3. printing service behavior")
|
||||
check("CUPS service and socket are enabled", "systemctl enable cups.socket cups.service || true" in postinst)
|
||||
check("CUPS start is best effort", "systemctl start cups.socket || true" in postinst)
|
||||
check("automatic remote queue browsing is disabled", "systemctl disable --now cups-browsed.service || true" in postinst)
|
||||
check("printer setup is never a boot service", "classroom-printer-setup" not in units)
|
||||
check("printer setup is never provisioning", "classroom-printer-setup" not in provision)
|
||||
check("Chrome uses the system print dialog", policy.get("DisablePrintPreview") is True)
|
||||
|
||||
print()
|
||||
print("4. canary release gate")
|
||||
validation = canary.index('"$ROOT/tools/validate-trixie-release.sh"')
|
||||
package_copy = canary.index('cp "$package_path"')
|
||||
first_rsync = canary.index("rsync -av")
|
||||
check("Trixie validation runs before the repository copy", validation < package_copy)
|
||||
check("Trixie validation runs before any upload", validation < first_rsync)
|
||||
|
||||
print()
|
||||
print(f"passed: {passed} failed: {failed}")
|
||||
raise SystemExit(1 if failed else 0)
|
||||
PY
|
||||
Reference in New Issue
Block a user