Panama had no idea whether it was running on a laptop. No upower, no battery, no lid, no AC: hypridle.conf says "This is a desktop" in its own header, and that was true of the code as well as the machine. panama-hw answers hardware questions one at a time, exits 0 or 1, and prints nothing, so scripts, services and contracts all ask the same way. The definition the rest of the laptop work hangs on is one line: clamshell is lid-closed AND an external monitor. A machine with no mains supply at all reports as being on wall power, because a desktop cannot run out of it. The battery service follows Vitals: sysfs through FileView, an availability flag, and no subprocess on the timer. Globbing is the one thing QML cannot do -- a battery is BAT0 or BAT1 or CMB0, mains is AC or ADP1 or ACAD -- so panama-battery resolves the names once and the shell reads the files directly after. Nothing falls back to a plausible zero: a desktop shows no indicator, no card, and no charge limit control where the firmware has no ceiling. Also repairs two contracts that were already failing and had not been noticed, because only the full suite runs them. The dependency scanner treated line-initial variable assignments, case labels, comments and heredoc bodies as commands, and `count`, `host`, `cancel` and `import` are all real binaries on Fedora, so `command -v` could not filter them out. It now drops comments and heredoc bodies and requires a command to be followed by whitespace. Verified it still catches a genuinely undeclared dependency rather than passing quietly. The launcher command contract had not been told about the fourteen commands added earlier today.
159 lines
6.9 KiB
Bash
Executable File
159 lines
6.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
commands_dir="$repo_dir/config/local/share/vicinae/scripts"
|
|
work="$(mktemp -d /tmp/panama-commands.XXXXXX)"
|
|
dispatch_log="$work/dispatch.log"
|
|
|
|
fail() {
|
|
printf 'Panama commands contract: %s\n' "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
cleanup() {
|
|
rm -rf "$work"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
declare -A expected=(
|
|
[open-control-center]=control-center
|
|
[open-notifications]=notifications
|
|
[open-calendar]=calendar
|
|
[open-clipboard]=clipboard
|
|
[open-mission-control]=overview
|
|
[open-settings]=settings
|
|
[check-system-health]=health
|
|
[toggle-dnd]=dnd
|
|
[toggle-caffeine]=caffeine
|
|
[toggle-night-light]=night-light
|
|
[start-focus]=focus-start
|
|
[end-focus]=focus-end
|
|
[capture]=capture
|
|
[screen-intelligence]=intelligence
|
|
[quick-screenshot]=screenshot
|
|
[toggle-microphone]=microphone
|
|
[open-prism-gallery]=gallery
|
|
[restart-shell]=restart-shell
|
|
)
|
|
|
|
# The per-page settings commands are generated from the settings page list, so
|
|
# they are enumerated from that list rather than restated here -- a hand-written
|
|
# copy would have to be edited every time a page is added, which is exactly the
|
|
# kind of second list this generator exists to avoid.
|
|
sidebar="$repo_dir/config/dot/quickshell/modules/settings/SettingsSidebar.qml"
|
|
while read -r page; do
|
|
[[ -n "$page" ]] || continue
|
|
[[ "$page" == "home" ]] && continue
|
|
expected[settings-$page]="settings-page $page"
|
|
done < <(grep -oE '\{ page: "[a-z-]+"' "$sidebar" | sed 's/.*"\([a-z-]*\)"/\1/')
|
|
|
|
(( ${#expected[@]} > 18 )) || fail 'no generated per-page commands were found; run scripts/panama-settings-commands'
|
|
|
|
# Commands that do not go through panama-action, and should not.
|
|
#
|
|
# Every command above asks the shell to do something, so routing them through
|
|
# one dispatcher is what keeps that surface small. search-web is a different
|
|
# animal: it takes a query and opens a browser, and neither half needs the
|
|
# shell. Sending it through panama-action would mean a web search stops working
|
|
# when Quickshell is down -- which is exactly when somebody is reaching for the
|
|
# launcher to look up what went wrong.
|
|
#
|
|
# The project commands are the same shape: they record or restore a window
|
|
# layout, which is panama-project's job and involves the shell not at all.
|
|
#
|
|
# They are still commands, so everything else below applies to them: a title,
|
|
# a description, search vocabulary, the Panama icon, and closing quietly.
|
|
#
|
|
# The OS-parity commands are standalone for the same reason. A power menu that
|
|
# needs the shell running is a power menu you cannot reach when the shell is
|
|
# what broke; the pick-lists render through `vicinae dmenu` and act through
|
|
# hyprctl; reminders are systemd timers. None of them has anything to ask the
|
|
# shell for, and routing them through panama-action would only add a way for
|
|
# them to stop working.
|
|
declare -a standalone=(
|
|
search-web save-project open-project
|
|
lock-screen suspend-system log-out reboot-system power-off
|
|
remind-me list-reminders pick-color
|
|
switch-window force-quit-window kill-process ssh-hosts recent-files
|
|
copy-password
|
|
)
|
|
|
|
# Generated commands must match their source. A stale command dispatches to a
|
|
# page that has been renamed or removed, and the launcher reports nothing wrong.
|
|
"$repo_dir/config/dot/quickshell/scripts/panama-settings-commands" --check >/dev/null \
|
|
|| fail 'the generated per-page commands are stale; run scripts/panama-settings-commands'
|
|
|
|
mkdir -p "$work/home/.config/quickshell/scripts"
|
|
cat >"$work/home/.config/quickshell/scripts/panama-action" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
printf '%s\n' "$*" >>"$PANAMA_COMMAND_TEST_LOG"
|
|
EOF
|
|
chmod +x "$work/home/.config/quickshell/scripts/panama-action"
|
|
|
|
[[ -d "$commands_dir" ]] || fail 'Vicinae command directory is missing'
|
|
|
|
# Every file in the directory, not a glob on an extension. These scripts carry
|
|
# none: a shebang and the executable bit already select the interpreter, and an
|
|
# extension is one more thing that has to stay in sync -- which it did not.
|
|
mapfile -t actual_files < <(find "$commands_dir" -maxdepth 1 -type f -printf '%f\n' | sort)
|
|
declared=$(( ${#expected[@]} + ${#standalone[@]} ))
|
|
[[ ${#actual_files[@]} -eq $declared ]] \
|
|
|| fail "expected $declared commands, found ${#actual_files[@]}"
|
|
|
|
declare -A seen_titles=()
|
|
for script_name in "${!expected[@]}" "${standalone[@]}"; do
|
|
script="$commands_dir/$script_name"
|
|
[[ -x "$script" ]] || fail "$script_name is missing or not executable"
|
|
|
|
vicinae script check "$script" >/dev/null \
|
|
|| fail "$script_name is rejected by Vicinae"
|
|
|
|
title="$(sed -n 's/^# @vicinae.title //p' "$script")"
|
|
# No product prefix: this is the desktop's own settings, not a third-party
|
|
# add-on announcing itself in someone else's launcher. Uniqueness still
|
|
# matters, because two commands with one title are indistinguishable there.
|
|
[[ -n $title ]] || fail "$script_name has no title"
|
|
[[ $title != Panama* ]] || fail "$script_name still carries the product prefix: $title"
|
|
[[ -z ${seen_titles[$title]+x} ]] || fail "duplicate launcher title: $title"
|
|
seen_titles[$title]=1
|
|
|
|
grep -Fxq '# @vicinae.mode silent' "$script" \
|
|
|| fail "$script_name does not close quietly after success"
|
|
grep -Fq '# @vicinae.description ' "$script" \
|
|
|| fail "$script_name has no searchable description"
|
|
grep -Fq '# @vicinae.keywords ' "$script" \
|
|
|| fail "$script_name has no search vocabulary"
|
|
grep -Fxq '# @vicinae.icon ../../icons/hicolor/scalable/apps/panama-settings.svg' "$script" \
|
|
|| fail "$script_name does not use the Panama application identity"
|
|
|
|
if [[ $script_name == check-system-health ]]; then
|
|
[[ $title == 'Check System Health' ]] \
|
|
|| fail "health command has the wrong title: $title"
|
|
grep -Fxq '# @vicinae.schemaVersion 1' "$script" \
|
|
|| fail 'health command does not use schema version 1'
|
|
grep -Fxq '# @vicinae.keywords ["health", "doctor", "repair", "services"]' "$script" \
|
|
|| fail 'health command has the wrong search vocabulary'
|
|
grep -Fxq 'exec "$HOME/.config/quickshell/scripts/panama-action" health' "$script" \
|
|
|| fail 'health command bypasses the stable dispatcher path'
|
|
fi
|
|
|
|
# A standalone command has nothing to dispatch, and running it would open a
|
|
# browser at whoever is running the tests.
|
|
if [[ -z ${expected[$script_name]+x} ]]; then
|
|
if grep -Fq 'panama-action' "$script"; then
|
|
fail "$script_name is listed as standalone but goes through panama-action"
|
|
fi
|
|
continue
|
|
fi
|
|
|
|
: >"$dispatch_log"
|
|
HOME="$work/home" PANAMA_COMMAND_TEST_LOG="$dispatch_log" "$script"
|
|
dispatched="$(cat "$dispatch_log")"
|
|
[[ $dispatched == "${expected[$script_name]}" ]] \
|
|
|| fail "$script_name dispatched [$dispatched], expected [${expected[$script_name]}]"
|
|
done
|
|
|
|
printf 'Panama commands contract: PASS (%d commands)\n' "$declared"
|