Merge remote-tracking branch 'origin/main' into feat/panama-health

# Conflicts:
#	config/dot/quickshell/modules/settings/HealthPage.qml
#	tests/quickshell/health-ui-contract.sh
This commit is contained in:
Gabriel Brown
2026-08-18 11:41:33 -04:00
10 changed files with 281 additions and 67 deletions
+122
View File
@@ -0,0 +1,122 @@
#!/usr/bin/env bash
# Every external command Panama's own scripts invoke must be installed by
# Panama's own package lists.
#
# This exists because the lists had drifted badly. jq is used by thirty-one call
# sites across the helpers and the contracts; kitty has a full shipped config
# and a dock pin; tmux and btop have shipped themes that the colour scheme
# switches. None of the four were declared. So a fresh machine that followed
# this repository's own install instructions would not have them.
#
# The failure is quiet by design, which is what makes it worth a test: the
# helpers are written to report "not installed" rather than crash, so a missing
# dependency presents as a feature that silently is not there.
#
# Commands from coreutils and the shell itself are not checked -- nothing
# installs those separately, and listing them would be noise.
set -uo pipefail
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
fail() {
printf 'declared dependencies contract: %s\n' "$1" >&2
exit 1
}
# Shell syntax and builtins. These are not commands anyone installs, and the
# first version of this contract reported `then`, `esac` and `done` as missing
# packages, which buried the four real findings in a hundred lines of noise.
SHELL_WORDS='^(if|then|else|elif|fi|for|while|until|do|done|case|esac|in|function|select|time|coproc|break|continue|return|exit|local|readonly|declare|export|unset|shift|eval|exec|source|trap|set|shopt|alias|unalias|builtin|command|enable|help|let|read|mapfile|printf|echo|test|true|false|wait|jobs|bg|fg|kill|pwd|cd|dirs|pushd|popd|umask|type|hash|getopts|split|sync)$'
# Provided by any Fedora install: coreutils, util-linux, the shell, and the
# systemd/session tooling. Nothing here is a choice Panama makes.
BASELINE='^(sh|bash|cat|cut|sed|awk|gawk|grep|egrep|head|tail|sort|uniq|tr|wc|find|xargs|basename|dirname|mkdir|rm|cp|mv|ln|chmod|chown|stat|df|du|date|sleep|env|id|tee|touch|mktemp|readlink|realpath|seq|comm|join|paste|od|file|nl|fold|column|tput|timeout|flock|install|sha256sum|md5sum|base64|nproc|uptime|free|uname|hostname|whoami|ps|pgrep|pkill|kill|killall|lsblk|mount|umount|sudo|su|rpm|dnf|flatpak|git|python3|ss|ip|lsof)$'
SESSION='^(systemctl|busctl|journalctl|loginctl|hostnamectl|localectl|systemd-inhibit|systemd-run|udevadm|gsettings|dconf|dbus-send|dbus-monitor|hyprctl|qs|quickshell|gnf|panama|wl-copy|wl-paste)$'
declared="$(cat "$repo_dir"/setup/packages/* 2>/dev/null | sed 's/#.*//' | tr -d ' ' | grep -v '^$' | sort -u)"
[[ -n "$declared" ]] || fail 'no package lists found'
# A package is not always named after its command. Only the genuine mismatches
# are mapped, so an unmapped command is a real omission rather than a lookup
# failure.
package_for() {
case "$1" in
zbarimg) printf 'zbar' ;;
fc-list|fc-match) printf 'fontconfig' ;;
lspci) printf 'pciutils' ;;
getenforce) printf 'libselinux-utils' ;;
nmcli) printf 'NetworkManager' ;;
wpctl) printf 'wireplumber' ;;
nvim) printf 'neovim' ;;
fwupdmgr) printf 'fwupd' ;;
dnf4) printf 'python3-dnf' ;;
notify-send) printf 'libnotify' ;;
wl-copy|wl-paste) printf 'wl-clipboard' ;;
rg) printf 'ripgrep' ;;
python3) printf 'python3' ;;
*) printf '%s' "$1" ;;
esac
}
missing=()
checked=0
while read -r script; do
[[ -n "$script" ]] || continue
head -1 "$script" | grep -qE 'bash|/sh' || continue
# Commands appearing at the start of a statement or after a pipe. Crude, but
# it is looking for undeclared dependencies, not building a call graph.
#
# No minimum length. An earlier version required three characters, which
# quietly excluded the most-used dependency in the repository -- jq, at
# thirty-one call sites -- along with rg, ss and ip. A dependency checker
# with a blind spot for short names is worse than none, because it reports
# PASS.
while read -r cmd; do
[[ -n "$cmd" ]] || continue
[[ "$cmd" =~ $SHELL_WORDS ]] && continue
[[ "$cmd" =~ $BASELINE ]] && continue
[[ "$cmd" =~ $SESSION ]] && continue
pkg="$(package_for "$cmd")"
grep -qx "$pkg" <<<"$declared" && continue
# Only report a command that actually exists on this machine. An
# invented name in a comment or a heredoc is a false positive; a real
# binary that nothing declares is the thing being looked for.
command -v "$cmd" >/dev/null 2>&1 || continue
missing+=("$cmd (from $(basename "$script"), package: $pkg)")
done < <({
# Statement-initial or after a pipe.
grep -oE '(^|[|;&]|\$\()[[:space:]]*[a-z][a-z0-9_-]+' "$script" \
| grep -oE '[a-z][a-z0-9_-]+$'
# Behind a wrapper. ddcutil is always invoked as `timeout 10 ddcutil`,
# so it never appears statement-initial and was missed entirely.
grep -oE '\b(timeout[[:space:]]+[0-9.]+|sudo|nohup|env)[[:space:]]+[a-z][a-z0-9_-]+' "$script" \
| grep -oE '[a-z][a-z0-9_-]+$'
# `command -v X` is how these helpers probe for a tool before using it,
# which makes it the clearest possible statement of a dependency.
grep -oE 'command -v[[:space:]]+[a-z][a-z0-9_-]+' "$script" \
| grep -oE '[a-z][a-z0-9_-]+$'
} | sort -u)
checked=$((checked + 1))
done < <(find "$repo_dir/config/dot/quickshell/scripts" \
"$repo_dir/config/local/share/vicinae/scripts" \
"$repo_dir/setup/scripts" "$repo_dir/bin" \
-type f 2>/dev/null)
if (( ${#missing[@]} > 0 )); then
printf 'declared dependencies contract: commands used but never installed:\n' >&2
printf ' %s\n' "${missing[@]}" | sort -u >&2
fail 'add each to a list in setup/packages/, or the feature silently will not exist on a fresh machine'
fi
printf 'declared dependencies contract: PASS (%d scripts)\n' "$checked"
+18
View File
@@ -68,6 +68,24 @@ rg -Fq 'SystemSettings.openGnomePanel("color")' "$settings_dir/HealthPage.qml" \
|| fail 'Fedora ownership boundary lost the Colour profiles handoff'
rg -Fq 'SystemSettings.openGnomePanel("wellbeing")' "$settings_dir/HealthPage.qml" \
|| fail 'Fedora ownership boundary lost the Digital wellbeing handoff'
# Exact authored handoffs are asserted above. Also prove every panel named by
# this boundary is accepted by SystemSettings, so a typo cannot ship a dead
# button even if its copy still looks correct.
rg -Fq 'title: "Fedora system settings"' "$settings_dir/HealthPage.qml" \
|| fail 'the Fedora ownership boundary card is gone'
allowed="$(rg -o '"[a-z-]+"' "$repo_dir/config/dot/quickshell/services/SystemSettings.qml" \
| sed -n '/"\(applications\|background\|bluetooth\|color\|display\|keyboard\|mouse\|multitasking\|network\|notifications\|online-accounts\|power\|printers\|privacy\|search\|sharing\|sound\|system\|universal-access\|wacom\|wellbeing\|wifi\|wwan\)"/p' \
| tr -d '"' | sort -u)"
while read -r panel; do
[[ -n "$panel" ]] || continue
grep -qx "$panel" <<<"$allowed" \
|| fail "the Fedora card opens \"$panel\", which openGnomePanel does not allow -- that button does nothing"
done < <(rg -o 'openGnomePanel\("([a-z-]+)"' -r '$1' "$settings_dir/HealthPage.qml" | sort -u)
rg -q 'openGnomePanel\(' "$settings_dir/HealthPage.qml" \
|| fail 'the Fedora ownership boundary does not open GNOME Settings at all'
rg -Fq 'Health.repair(check.id, false)' "$settings_dir/HealthPage.qml" \
|| fail 'Settings repair does not stay inline/non-external'
rg -Fq 'ShellState.openSettings(check.action.target)' "$settings_dir/HealthPage.qml" \
@@ -25,8 +25,16 @@ trap cleanup EXIT
if rg -q 'setup/scripts/link-vicinae-scripts' "$dotfile_installer"; then
fail 'link-dotfiles also invokes the command installer'
fi
rg -Fq 'do "$script"; done' "$top_level_installer" \
|| fail 'top-level installer sources setup scripts into one shared shell'
# Each setup stage must run in its OWN process, so strict-shell options and
# helper variables stay local to the script that owns them. What matters is
# that the stages are executed rather than sourced -- this previously matched
# the literal one-liner `do "$script"; done`, which failed the moment the loop
# gained error reporting and spanned more than one line, despite the property
# it cares about being unchanged.
rg -q '(^|[^a-z-])(\.|source)\s+[^;]*setup/scripts' "$top_level_installer" \
&& fail 'top-level installer sources setup scripts into one shared shell'
rg -q '"\$script"' "$top_level_installer" \
|| fail 'top-level installer does not execute the setup scripts'
mkdir -p "$data_dir/scripts/panama" "$fake_bin"
printf 'user-owned\n' >"$data_dir/scripts/panama/keep.sh"
@@ -137,10 +137,20 @@ last_error="$(qs_for_harness ipc call settings-system-test status | jq -r .lastE
# ── A rejected value must be refused, not silently accepted ──────────────────
qs_for_harness ipc call settings-system-test apply "$target_auto_hdr" 7 "$target_direct" >/dev/null
sleep 0.3
# The refusal is reported asynchronously, so wait for it rather than sleeping a
# fixed 0.3s and hoping. That sleep made this fail roughly one run in three,
# reporting "a rejected value did not surface an error" when the error simply
# had not arrived yet -- which reads as a missing guard rather than a slow one.
rejected=""
for _ in $(seq 1 60); do
rejected="$(qs_for_harness ipc call settings-system-test status | jq -r .lastError)"
[[ -n "$rejected" ]] && break
sleep 0.1
done
[[ "$(read_option misc:vrr)" == "$target_vrr" ]] || fail 'an out-of-allow-list VRR value reached the compositor'
[[ -n "$(qs_for_harness ipc call settings-system-test status | jq -r .lastError)" ]] \
|| fail 'a rejected VRR value did not surface an error'
[[ -n "$rejected" ]] || fail 'a rejected VRR value did not surface an error'
# ── Every getoption answer shape must be handled, not just integers ──────────
# The compositor reports each option in a different JSON field depending on its