174 lines
7.7 KiB
Bash
Executable File
174 lines
7.7 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
|
|
[dock-add-app]=dock-pin
|
|
[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 taxonomy, so
|
|
# they are enumerated from it 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.
|
|
#
|
|
# One command per leaf, not per sidebar row: a launcher entry that opened a
|
|
# category would land on whichever tab that category happens to open first,
|
|
# which is not what somebody typing "firewall" asked for. Home is skipped
|
|
# because open-settings already goes there.
|
|
routes="$repo_dir/config/dot/quickshell/services/SettingsRoutes.qml"
|
|
while read -r page; do
|
|
[[ -n "$page" ]] || continue
|
|
[[ "$page" == "home" ]] && continue
|
|
expected[settings-$page]="settings-page $page"
|
|
#
|
|
# Hidden leaves count too. The manual has no tab of its own -- it is reference
|
|
# material rather than a control surface -- but "manual" is exactly the sort of
|
|
# thing somebody types into a launcher, and a leaf with no tab is the one leaf
|
|
# with nowhere else to be found by scanning.
|
|
done < <( {
|
|
grep -oE '\{ page: "[a-z-]+", label: "[^"]*", icon: "[^"]*", tabs: \[\] \}' "$routes"
|
|
grep -oE '\{ page: "[a-z-]+", label: "[^"]*" \}' "$routes"
|
|
sed -n '/property var hiddenLeaves:/,/\]/p' "$routes" | grep -oE 'page: "[a-z-]+"'
|
|
} | sed -E 's/.*page: "([a-z-]+)".*/\1/' | sort -u)
|
|
|
|
(( ${#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 keyboard-shortcuts show-welcome install-web-app
|
|
)
|
|
|
|
# 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"
|