feat: build CouchOS Melee appliance
Some checks failed
ci / verify (push) Has been cancelled

This commit is contained in:
jackmhny
2026-08-03 02:52:28 +02:00
commit 04ef2a0d74
42 changed files with 3671 additions and 0 deletions

346
scripts/preflight.sh Executable file
View File

@@ -0,0 +1,346 @@
#!/usr/bin/env bash
set -Eeuo pipefail
MODE="${1:-quick}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
export PYTHONPATH="${REPO_ROOT}/src${PYTHONPATH:+:${PYTHONPATH}}"
ARTIFACT_DIR="${ARTIFACT_DIR:-${REPO_ROOT}/artifacts}"
mkdir -p "${ARTIFACT_DIR}"
LOG_FILE="${ARTIFACT_DIR}/preflight-${MODE}.log"
STATE_FILE="${ARTIFACT_DIR}/five-reboots.jsonl"
SOAK_FILE="${ARTIFACT_DIR}/soak-30m.json"
FAILURES=0
COUCH_USER="couch"
RUNTIME_CONFIG="/home/couch/.config/couchd/runtime.json"
record() {
local status="$1"
shift
printf '%s %s\n' "${status}" "$*" | tee -a "${LOG_FILE}"
if [[ "${status}" == "FAIL" ]]; then
FAILURES=$((FAILURES + 1))
fi
}
run_as_couch() {
if [[ "${EUID}" -eq 0 ]]; then
runuser -u "${COUCH_USER}" -- "$@"
elif [[ "$(id -un)" == "${COUCH_USER}" ]]; then
"$@"
else
return 1
fi
}
runtime_field() {
local field="$1"
python3 - <<'PY' "${RUNTIME_CONFIG}" "${field}"
import json
import sys
from pathlib import Path
path = Path(sys.argv[1])
field = sys.argv[2]
data = json.loads(path.read_text(encoding="utf-8"))
value = data.get(field)
if value is None:
raise SystemExit(1)
print(value)
PY
}
controller_snapshot() {
local address
address="$(runtime_field xbox_bluetooth_address 2>/dev/null || true)"
if [[ -z "${address}" ]]; then
printf '{"address": null, "paired": false, "trusted": false, "connected": false}'
return 0
fi
local output
output="$(bluetoothctl info "${address}" 2>/dev/null || true)"
python3 - <<'PY' "${address}" "${output}"
import json
import sys
address = sys.argv[1]
text = sys.argv[2]
print(json.dumps({
"address": address,
"paired": "Paired: yes" in text,
"trusted": "Trusted: yes" in text,
"connected": "Connected: yes" in text,
}))
PY
}
status_json() {
python3 -m couchd --config "${RUNTIME_CONFIG}" status
}
finish() {
if [[ "${FAILURES}" -gt 0 ]]; then
exit 1
fi
}
quick_mode() {
: >"${LOG_FILE}"
if id "${COUCH_USER}" >/dev/null 2>&1; then
record PASS "couch user exists"
else
record FAIL "couch user missing"
fi
if [[ "$(stat -c '%a' /home/jackmhny 2>/dev/null || true)" == "700" ]]; then
record PASS "/home/jackmhny mode is 0700"
else
record FAIL "/home/jackmhny mode is not 0700"
fi
if run_as_couch bash -lc 'test ! -r /home/jackmhny && test ! -x /home/jackmhny'; then
record PASS "couch cannot read or traverse /home/jackmhny"
else
record FAIL "couch can read or traverse /home/jackmhny or couch access test could not run"
fi
if id -nG "${COUCH_USER}" 2>/dev/null | tr ' ' '\n' | grep -Eq '^(sudo|adm)$'; then
record FAIL "couch is still in sudo/adm"
else
record PASS "couch is not in sudo/adm"
fi
grep -Fq 'autologin-user=couch' /etc/lightdm/lightdm.conf.d/50-couchos.conf 2>/dev/null && \
grep -Fq 'autologin-session=xfce' /etc/lightdm/lightdm.conf.d/50-couchos.conf 2>/dev/null \
&& record PASS "LightDM autologin is set to couch/XFCE" \
|| record FAIL "LightDM autologin is not set to couch/XFCE"
grep -Fq 'HandleLidSwitchExternalPower=ignore' /etc/systemd/logind.conf.d/50-couchos.conf 2>/dev/null \
&& record PASS "logind ignores lid close on AC" \
|| record FAIL "logind lid policy on AC is not configured"
grep -Fq 'use_compositing" type="bool" value="false' /home/couch/.config/xfce4/xfconf/xfce-perchannel-xml/xfwm4.xml 2>/dev/null \
&& record PASS "XFCE compositing disabled" \
|| record FAIL "XFCE compositing not disabled"
grep -Fq 'do-not-disturb" type="bool" value="true' /home/couch/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-notifyd.xml 2>/dev/null \
&& record PASS "notifications DND enabled" \
|| record FAIL "notifications DND not enabled"
grep -Fq 'dpms-enabled" type="bool" value="false' /home/couch/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-power-manager.xml 2>/dev/null \
&& record PASS "power blanking/suspend disabled" \
|| record FAIL "power blanking/suspend not disabled"
if [[ -f "${RUNTIME_CONFIG}" ]]; then
record PASS "runtime config present"
else
record FAIL "runtime config missing"
fi
if [[ -f /home/couch/.config/couchd/token && "$(stat -c '%a' /home/couch/.config/couchd/token)" == "600" ]]; then
if [[ "$(tr -d '\r\n' </home/couch/.config/couchd/token)" == "replace-with-long-random-token" ]]; then
record FAIL "token still uses the template placeholder"
else
record PASS "token mode is 0600 and not placeholder"
fi
else
record FAIL "token missing or not 0600"
fi
local bind_address
bind_address="$(runtime_field bind_address 2>/dev/null || true)"
if [[ "${bind_address}" == "100.64.0.15" ]]; then
record PASS "runtime bind address is 100.64.0.15"
else
record FAIL "runtime bind address is not 100.64.0.15"
fi
if ss -ltn sport = :8765 2>/dev/null | awk '{print $4}' | grep -Fxq '100.64.0.15:8765'; then
record PASS "couchd listens only on 100.64.0.15:8765"
else
record FAIL "couchd listener is not exactly 100.64.0.15:8765"
fi
local controller
controller="$(controller_snapshot)"
if python3 - <<'PY' "${controller}"
import json
import sys
snapshot = json.loads(sys.argv[1])
if snapshot["address"] and snapshot["paired"] and snapshot["trusted"] and snapshot["connected"]:
raise SystemExit(0)
raise SystemExit(1)
PY
then
record PASS "controller is paired/trusted/connected"
else
record FAIL "controller is not paired/trusted/connected"
fi
local status_payload
if status_payload="$(status_json 2>/dev/null)"; then
record PASS "couchd status command succeeded"
if python3 - <<'PY' "${status_payload}"
import json
import sys
payload = json.loads(sys.argv[1])
mode = payload.get("display", {}).get("mode")
name = payload.get("display", {}).get("name")
if name == "HDMI-1" and mode == "1920x1080@60.00":
raise SystemExit(0)
raise SystemExit(1)
PY
then
record PASS "HDMI-1 active mode is 1920x1080@60.00"
else
record FAIL "HDMI-1 active mode is not 1920x1080@60.00"
fi
else
record FAIL "couchd status command failed"
fi
if pactl info 2>/dev/null | grep -Eiq '^Default Sink: .*hdmi'; then
record PASS "default audio sink is HDMI"
else
record FAIL "default audio sink is not HDMI"
fi
local couch_uid
couch_uid="$(id -u "${COUCH_USER}" 2>/dev/null || true)"
if [[ -n "${couch_uid}" ]] && XDG_RUNTIME_DIR="/run/user/${couch_uid}" run_as_couch systemctl --user is-active couchd.service >/dev/null 2>&1; then
record PASS "couchd user service is active"
else
record FAIL "couchd user service is not active"
fi
if [[ -f /home/couch/.config/autostart/couchd.desktop && -f /home/couch/.config/autostart/couch-display-audio.desktop ]]; then
record PASS "XFCE autostart entries exist for couch session units"
else
record FAIL "XFCE autostart entries for couch session units are missing"
fi
finish
}
five_reboots_mode() {
: >"${LOG_FILE}"
local boot_id
boot_id="$(cat /proc/sys/kernel/random/boot_id)"
local controller
controller="$(controller_snapshot)"
local status_payload
status_payload="$(status_json 2>/dev/null || printf '{}')"
python3 - <<'PY' "${STATE_FILE}" "${boot_id}" "${controller}" "${status_payload}"
import json
import sys
from pathlib import Path
path = Path(sys.argv[1])
boot_id = sys.argv[2]
controller = json.loads(sys.argv[3])
status_payload = json.loads(sys.argv[4])
existing = []
if path.exists():
existing = [json.loads(line) for line in path.read_text(encoding="utf-8").splitlines() if line.strip()]
if not any(item.get("boot_id") == boot_id for item in existing):
existing.append({
"boot_id": boot_id,
"timestamp": __import__("datetime").datetime.now().isoformat(timespec="seconds"),
"controller": controller,
"display_mode": status_payload.get("display", {}).get("mode"),
"operator_login_observed": "SKIP",
"operator_fps_observed": "SKIP",
})
path.write_text("\n".join(json.dumps(item, sort_keys=True) for item in existing) + ("\n" if existing else ""), encoding="utf-8")
print(len(existing))
PY
local count
count="$(python3 - <<'PY' "${STATE_FILE}"
import json
import sys
from pathlib import Path
path = Path(sys.argv[1])
print(len([json.loads(line) for line in path.read_text(encoding="utf-8").splitlines() if line.strip()]))
PY
)"
record PASS "recorded reboot observation for boot ${boot_id} (${count}/5 unique boots)"
record SKIP "operator_login_observed remains SKIP until human session confirmation is recorded"
record SKIP "operator_fps_observed remains SKIP until human 60 FPS confirmation is recorded"
finish
}
soak_30m_start() {
: >"${LOG_FILE}"
python3 - <<'PY' "${SOAK_FILE}" "$(cat /proc/sys/kernel/random/boot_id)" "$(controller_snapshot)"
import json
import sys
from datetime import datetime
from pathlib import Path
Path(sys.argv[1]).write_text(json.dumps({
"start_boot_id": sys.argv[2],
"started_at": datetime.now().isoformat(timespec="seconds"),
"controller_start": json.loads(sys.argv[3]),
"operator_fps_observed": "SKIP",
}, sort_keys=True) + "\n", encoding="utf-8")
PY
record PASS "30-minute soak started"
record SKIP "operator_fps_observed remains SKIP until a human records it after the session"
finish
}
soak_30m_finish() {
: >"${LOG_FILE}"
if [[ ! -f "${SOAK_FILE}" ]]; then
record FAIL "soak-30m-start was not run"
finish
fi
python3 - <<'PY' "${SOAK_FILE}" "$(cat /proc/sys/kernel/random/boot_id)" "$(controller_snapshot)" "${ARTIFACT_DIR}/soak-30m-finish.json"
import json
import sys
from datetime import datetime
from pathlib import Path
start = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
finished_at = datetime.now()
started_at = datetime.fromisoformat(start["started_at"])
elapsed = int((finished_at - started_at).total_seconds())
payload = {
**start,
"finish_boot_id": sys.argv[2],
"finished_at": finished_at.isoformat(timespec="seconds"),
"elapsed_seconds": elapsed,
"controller_finish": json.loads(sys.argv[3]),
}
Path(sys.argv[4]).write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
print(elapsed)
PY
local elapsed
elapsed="$(python3 - <<'PY' "${ARTIFACT_DIR}/soak-30m-finish.json"
import json
import sys
from pathlib import Path
print(json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))["elapsed_seconds"])
PY
)"
if [[ "${elapsed}" -ge 1800 ]]; then
record PASS "30-minute soak elapsed ${elapsed} seconds"
else
record FAIL "30-minute soak only ran ${elapsed} seconds"
fi
record SKIP "operator_fps_observed remains SKIP until a human records the observed FPS stability"
finish
}
case "${MODE}" in
quick) quick_mode ;;
five-reboots) five_reboots_mode ;;
soak-30m-start) soak_30m_start ;;
soak-30m-finish) soak_30m_finish ;;
*)
printf 'unknown mode: %s\n' "${MODE}" >&2
exit 1
;;
esac