Add Panama system health diagnostics
This commit is contained in:
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [[ "${PANAMA_DOCTOR_FIXTURE_BUS:-ready}" == "missing" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
printf '%s\n' \
|
||||
'org.freedesktop.portal.Desktop 1000 portal' \
|
||||
'org.kde.kdeconnect 1000 kdeconnect' \
|
||||
'fixture clipboard body AA:BB:CC:DD:EE:FF'
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
name="${!#}"
|
||||
case ",${PANAMA_DOCTOR_FIXTURE_PROCESSES:-}," in
|
||||
*",$name:duplicate,"*) printf '4101\n4102\n' ;;
|
||||
*",$name:missing,"*) exit 1 ;;
|
||||
*) printf '4101\n' ;;
|
||||
esac
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
case "${1:-}" in
|
||||
--version) printf '%s\n' "${PANAMA_DOCTOR_FIXTURE_QS_VERSION:-Quickshell 0.2.0}" ;;
|
||||
ipc)
|
||||
if [[ "${PANAMA_DOCTOR_FIXTURE_QS:-ready}" == "malformed" ]]; then
|
||||
printf 'fixture-secret-token AA:BB:CC:DD:EE:FF\n'
|
||||
else
|
||||
printf '%s\n' 'target notifications' 'target clipboard' 'target wallpaper' 'target capture'
|
||||
fi
|
||||
;;
|
||||
*) exit 2 ;;
|
||||
esac
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
service="${4:-}"
|
||||
case ",${PANAMA_DOCTOR_FIXTURE_STOPPED:-}," in
|
||||
*",$service,"*) exit 3 ;;
|
||||
esac
|
||||
printf 'fixture-secret-token\n'
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
uid="$(/usr/bin/id -u)"
|
||||
printf 'Panama %s fixture-user 4101 systemd-inhibit sleep:idle Caffeine block\n' "$uid"
|
||||
if [[ "${PANAMA_DOCTOR_FIXTURE_CAFFEINE:-single}" == "duplicate" ]]; then
|
||||
printf 'Panama %s fixture-user 4102 systemd-inhibit sleep:idle Caffeine block\n' "$uid"
|
||||
fi
|
||||
printf 'Other %s fixture-secret-token AA:BB:CC:DD:EE:FF fixture clipboard body ignore ignore\n' "$uid"
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
case "${1:-}" in
|
||||
--version) printf 'Vicinae 0.26.0 fixture-secret-token\n' ;;
|
||||
ping) exit 0 ;;
|
||||
*) exit 2 ;;
|
||||
esac
|
||||
Executable
+204
@@ -0,0 +1,204 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# The doctor is deliberately exercised through its command boundary. The
|
||||
# fixture commands include sensitive-looking output so this test proves the
|
||||
# report only retains explicitly parsed, non-sensitive observations.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
doctor="$repo_dir/config/dot/quickshell/scripts/panama-doctor"
|
||||
fixture_root="$repo_dir/tests/quickshell/fixtures/doctor"
|
||||
|
||||
fail() {
|
||||
printf 'panama doctor contract: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
fixture="$(mktemp -d /tmp/panama-doctor.XXXXXX)"
|
||||
trap 'rm -rf "$fixture"' EXIT
|
||||
|
||||
home="$fixture/home"
|
||||
config_home="$home/.config"
|
||||
state_home="$home/.local/state"
|
||||
runtime_dir="$fixture/runtime"
|
||||
bin_dir="$fixture/bin"
|
||||
data_home="$home/.local/share"
|
||||
|
||||
mkdir -p "$config_home" "$state_home" "$runtime_dir" "$bin_dir" "$data_home/vicinae"
|
||||
cp "$fixture_root/bin/"* "$bin_dir/"
|
||||
chmod +x "$bin_dir"/*
|
||||
|
||||
# These are intentionally tiny stand-ins for authored executable probes. The
|
||||
# named fixture scripts above cover probes whose output needs branch coverage.
|
||||
for tool in hyprctl wl-paste grim tesseract kitty nextcloud rustdesk kdeconnect-cli; do
|
||||
cat >"$bin_dir/$tool" <<'EOF'
|
||||
#!/usr/bin/bash
|
||||
case "${0##*/}" in
|
||||
hyprctl) printf 'Hyprland 0.50.0\n' ;;
|
||||
esac
|
||||
EOF
|
||||
chmod +x "$bin_dir/$tool"
|
||||
done
|
||||
|
||||
cat >"$bin_dir/flatpak" <<'EOF'
|
||||
#!/usr/bin/bash
|
||||
if [[ "${1:-}" == "info" && "${2:-}" == "app.bluebubbles.BlueBubbles" \
|
||||
&& "${PANAMA_DOCTOR_FIXTURE_BLUEBUBBLES:-installed}" == "installed" ]]; then
|
||||
printf 'BlueBubbles fixture-secret-token\n'
|
||||
exit 0
|
||||
fi
|
||||
exit 1
|
||||
EOF
|
||||
chmod +x "$bin_dir/flatpak"
|
||||
|
||||
cat >"$bin_dir/calendar-agenda" <<'EOF'
|
||||
#!/usr/bin/bash
|
||||
if [[ "${1:-}" != "probe" ]]; then
|
||||
exit 2
|
||||
fi
|
||||
case "${PANAMA_DOCTOR_FIXTURE_CALENDAR:-ready}" in
|
||||
ready) printf '{"eds":true,"sourceRegistry":true,"enabledSources":2,"event":"fixture clipboard body"}\n' ;;
|
||||
malformed) printf 'calendar AA:BB:CC:DD:EE:FF\n' ;;
|
||||
timeout) /usr/bin/sleep 2; printf '{"enabledSources":2}\n' ;;
|
||||
*) printf '{"eds":true,"sourceRegistry":true,"enabledSources":0}\n' ;;
|
||||
esac
|
||||
EOF
|
||||
chmod +x "$bin_dir/calendar-agenda"
|
||||
|
||||
cat >"$bin_dir/panama-brightness" <<'EOF'
|
||||
#!/usr/bin/bash
|
||||
case "${PANAMA_DOCTOR_FIXTURE_BRIGHTNESS:-ready}" in
|
||||
ready) printf '{"displays":[{"connector":"AA:BB:CC:DD:EE:FF"}],"error":""}\n' ;;
|
||||
denied) printf '{"displays":[],"error":"fixture-secret-token"}\n' ;;
|
||||
malformed) printf 'fixture clipboard body\n' ;;
|
||||
esac
|
||||
EOF
|
||||
chmod +x "$bin_dir/panama-brightness"
|
||||
|
||||
mkdir -p "$config_home/autostart"
|
||||
touch "$config_home/autostart/nextcloud.desktop"
|
||||
for name in hypr quickshell uwsm vicinae; do
|
||||
ln -s "$repo_dir/config/dot/$name" "$config_home/$name"
|
||||
done
|
||||
ln -s "$repo_dir/config/local/share/vicinae/scripts" "$data_home/vicinae/scripts"
|
||||
|
||||
run_doctor() {
|
||||
HOME="$home" \
|
||||
PATH="$bin_dir" \
|
||||
XDG_CURRENT_DESKTOP=Hyprland \
|
||||
PANAMA_HOME_ASSISTANT_URL='https://fixture.invalid' \
|
||||
PANAMA_HOME_ASSISTANT_TOKEN='fixture-secret-token' \
|
||||
PANAMA_DOCTOR_ROOT="$repo_dir" \
|
||||
PANAMA_DOCTOR_HOME="$home" \
|
||||
PANAMA_DOCTOR_CONFIG_HOME="$config_home" \
|
||||
PANAMA_DOCTOR_STATE_HOME="$state_home" \
|
||||
PANAMA_DOCTOR_RUNTIME_DIR="$runtime_dir" \
|
||||
PANAMA_DOCTOR_PATH="$bin_dir" \
|
||||
PANAMA_DOCTOR_TIMEOUT="${PANAMA_DOCTOR_TIMEOUT:-0.2}" \
|
||||
/usr/bin/python3 "$doctor" "$@"
|
||||
}
|
||||
|
||||
expected_order=$'desktop.hyprland\ndesktop.quickshell\ndesktop.notifications\ndesktop.portals\ndesktop.hyprpaper\ndesktop.hypridle\ndesktop.vicinae\ninput.pipewire\ninput.clipboard\ninput.wallpaper\ninput.capture\ninput.ocr\ninput.brightness\nintegration.nextcloud\nintegration.rustdesk\nintegration.kdeconnect\nintegration.bluebubbles\nintegration.home-assistant\nintegration.calendar\npanama.runtime-links\npanama.vicinae-commands\npanama.selected-terminal\npanama.selected-launcher\npanama.processes\npanama.caffeine'
|
||||
|
||||
assert_schema_and_redaction() {
|
||||
local snapshot="$1"
|
||||
jq -e '.schemaVersion == 1
|
||||
and (.generatedAt | type == "string")
|
||||
and (.summary.status | IN("healthy", "warning", "error"))
|
||||
and (.context.session | IN("hyprland", "other"))
|
||||
and (.context.versions | type == "array")
|
||||
and ([.checks[].id] | length == 25)
|
||||
and ([.checks[].id] | unique | length == 25)
|
||||
and ([.checks[].status] | all(IN("ok", "warning", "error", "unconfigured")))' \
|
||||
>/dev/null <<<"$snapshot" || fail "invalid schema: $snapshot"
|
||||
[[ "$(jq -r '.checks[].id' <<<"$snapshot")" == "$expected_order" ]] \
|
||||
|| fail "checks are not in the authored order"
|
||||
! grep -Fq 'fixture-secret-token' <<<"$snapshot" \
|
||||
|| fail 'report exposed a fixture secret'
|
||||
! grep -Fq 'fixture clipboard body' <<<"$snapshot" \
|
||||
|| fail 'report exposed clipboard or calendar content'
|
||||
! grep -Fq 'AA:BB:CC:DD:EE:FF' <<<"$snapshot" \
|
||||
|| fail 'report exposed a device address'
|
||||
}
|
||||
|
||||
check_status() {
|
||||
local snapshot="$1" id="$2" expected="$3"
|
||||
[[ "$(jq -r --arg id "$id" '.checks[] | select(.id == $id) | .status' <<<"$snapshot")" == "$expected" ]] \
|
||||
|| fail "$id did not report $expected: $snapshot"
|
||||
}
|
||||
|
||||
snapshot="$(run_doctor --json)"
|
||||
assert_schema_and_redaction "$snapshot"
|
||||
|
||||
# A healthy systemd-backed service stays healthy.
|
||||
check_status "$snapshot" desktop.hyprpaper ok
|
||||
|
||||
# A missing required executable is an error rather than a crash.
|
||||
mv "$bin_dir/qs" "$bin_dir/qs.off"
|
||||
missing_qs="$(run_doctor --json)"
|
||||
check_status "$missing_qs" desktop.quickshell error
|
||||
mv "$bin_dir/qs.off" "$bin_dir/qs"
|
||||
|
||||
# Optional integrations stay neutral until the user configures them.
|
||||
rm "$config_home/autostart/nextcloud.desktop"
|
||||
unconfigured_nextcloud="$(run_doctor --json)"
|
||||
check_status "$unconfigured_nextcloud" integration.nextcloud unconfigured
|
||||
touch "$config_home/autostart/nextcloud.desktop"
|
||||
|
||||
# A configured integration that stopped is actionable with an authored label,
|
||||
# never an application name or command derived from probe output.
|
||||
stopped_nextcloud="$(PANAMA_DOCTOR_FIXTURE_STOPPED=nextcloud.service run_doctor --json)"
|
||||
check_status "$stopped_nextcloud" integration.nextcloud warning
|
||||
jq -e '.checks[] | select(.id == "integration.nextcloud")
|
||||
| .action == {kind:"open", label:"Open Nextcloud", confirm:false}' \
|
||||
>/dev/null <<<"$stopped_nextcloud" || fail 'Nextcloud action was not authored'
|
||||
|
||||
# DDC errors are classified without retaining connectors or bus addresses.
|
||||
denied_brightness="$(PANAMA_DOCTOR_FIXTURE_BRIGHTNESS=denied run_doctor --json)"
|
||||
check_status "$denied_brightness" input.brightness warning
|
||||
jq -e '.checks[] | select(.id == "input.brightness")
|
||||
| .action == {kind:"instructions", label:"View setup instructions", confirm:false, target:"ddc-permissions"}' \
|
||||
>/dev/null <<<"$denied_brightness" || fail 'DDC instructions were not authored'
|
||||
|
||||
# A bounded probe timeout becomes a result, never a helper failure.
|
||||
timed_calendar="$(PANAMA_DOCTOR_FIXTURE_CALENDAR=timeout PANAMA_DOCTOR_TIMEOUT=0.05 run_doctor --json)"
|
||||
check_status "$timed_calendar" integration.calendar warning
|
||||
jq -e '.checks[] | select(.id == "integration.calendar")
|
||||
| .action == {kind:"open", label:"Open Date & Time", confirm:false, target:"datetime"}' \
|
||||
>/dev/null <<<"$timed_calendar" || fail 'calendar action was not authored'
|
||||
|
||||
# Exact Panama/Caffeine inhibitor rows detect duplicates without exposing PIDs.
|
||||
duplicated_caffeine="$(PANAMA_DOCTOR_FIXTURE_CAFFEINE=duplicate run_doctor --json)"
|
||||
check_status "$duplicated_caffeine" panama.caffeine warning
|
||||
jq -e '.checks[] | select(.id == "panama.caffeine")
|
||||
| .action == {kind:"repair", label:"Release duplicate inhibitors", confirm:false}' \
|
||||
>/dev/null <<<"$duplicated_caffeine" || fail 'Caffeine repair action was not authored'
|
||||
! jq -r '.checks[] | select(.id == "panama.caffeine") | .detail' <<<"$duplicated_caffeine" | grep -Eq '[0-9]{3,}' \
|
||||
|| fail 'Caffeine detail exposed inhibitor PIDs'
|
||||
|
||||
# Process counts use only exact authored names and never expose command lines or PIDs.
|
||||
duplicated_processes="$(PANAMA_DOCTOR_FIXTURE_PROCESSES=quickshell:duplicate run_doctor --json)"
|
||||
check_status "$duplicated_processes" panama.processes warning
|
||||
! jq -r '.checks[] | select(.id == "panama.processes") | .detail' <<<"$duplicated_processes" | grep -Eq '[0-9]{3,}' \
|
||||
|| fail 'process detail exposed a PID'
|
||||
|
||||
# Configured Home Assistant failures route to the exact authored Settings page.
|
||||
rm "$config_home/quickshell"
|
||||
mkdir -p "$config_home/quickshell/scripts"
|
||||
home_assistant_failure="$(run_doctor --json)"
|
||||
check_status "$home_assistant_failure" integration.home-assistant warning
|
||||
jq -e '.checks[] | select(.id == "integration.home-assistant")
|
||||
| .action == {kind:"open", label:"Open Home settings", confirm:false, target:"home-phone"}' \
|
||||
>/dev/null <<<"$home_assistant_failure" || fail 'Home Assistant action was not routed to home-phone'
|
||||
|
||||
# Invalid probe text is contained in its own check and never copied to JSON.
|
||||
malformed_calendar="$(PANAMA_DOCTOR_FIXTURE_CALENDAR=malformed run_doctor --json)"
|
||||
check_status "$malformed_calendar" integration.calendar warning
|
||||
assert_schema_and_redaction "$malformed_calendar"
|
||||
|
||||
summary="$(run_doctor --summary)"
|
||||
[[ "$summary" =~ ^Panama\ system\ health:\ (healthy|warning|error)\ \([0-9]+\ ok,\ [0-9]+\ warnings,\ [0-9]+\ errors,\ [0-9]+\ unconfigured\)$ ]] \
|
||||
|| fail "summary is not concise: $summary"
|
||||
|
||||
printf 'panama doctor contract: PASS\n'
|
||||
Reference in New Issue
Block a user