#!/usr/bin/env bash # A default-application role owns a FAMILY of types, and every type in it must # agree. # # Each role used to map to a single representative type, so setting "images" # changed image/png and left image/jpeg wherever it happened to land. That is # how this desktop ended up opening PNGs in a pixel-art editor, MP3s in a video # transcoder and PDFs in an image editor -- nobody chose any of it, the # applications registered themselves and the roles governed one type each. # # The failure is invisible until someone double-clicks a file, which is the # worst possible moment to discover it. # # Read-only: this inspects the schema of the roles and the state of the machine. # It never changes a default, because doing so on the daily driver would be # rude and the point is to detect drift, not create it. set -uo pipefail repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" helper="$repo_dir/config/dot/quickshell/scripts/panama-default-apps" fail() { printf 'default apps family contract: %s\n' "$1" >&2 exit 1 } [[ -x "$helper" ]] || fail 'panama-default-apps is missing or not executable' # The role table, read from the helper itself so the two cannot disagree. families="$(python3 - "$helper" <<'PY' import ast, re, sys source = open(sys.argv[1]).read() match = re.search(r"ROLE_TARGETS = (\{.*?\n\})", source, re.S) if not match: raise SystemExit("ROLE_TARGETS not found") table = ast.literal_eval(match.group(1)) for role, (kind, targets) in table.items(): if kind != "mime": continue print(role + "\t" + ",".join(targets)) PY )" || fail 'could not read ROLE_TARGETS from the helper' [[ -n "$families" ]] || fail 'no mime-backed roles found' command -v xdg-mime >/dev/null 2>&1 || { printf 'default apps family contract: SKIP (no xdg-mime)\n'; exit 0; } checked=0 while IFS=$'\t' read -r role types; do [[ -n "$role" ]] || continue first="" disagreeing="" for mime in ${types//,/ }; do handler="$(xdg-mime query default "$mime" 2>/dev/null)" # A type nothing claims is not drift; it is simply unclaimed, and # forcing a handler for every exotic type is not this role's job. [[ -n "$handler" ]] || continue if [[ -z "$first" ]]; then first="$handler" continue fi [[ "$handler" == "$first" ]] || disagreeing+="$mime->$handler " done [[ -z "$disagreeing" ]] \ || fail "the \"$role\" role is split: its first type opens with $first but $disagreeing-- setting the role must write every type in the family" checked=$((checked + 1)) done <<<"$families" # Every role the helper can set must be reachable from the settings page, and # every row on the page must name a role the helper knows. A role that exists # only in the helper is unreachable from the UI; a row naming a role the helper # does not have does nothing when someone taps it. page="$repo_dir/config/dot/quickshell/modules/settings/ApplicationsPage.qml" [[ -r "$page" ]] || fail 'ApplicationsPage.qml is missing' helper_roles="$(python3 - "$helper" <<'ROLES' import ast, re, sys source = open(sys.argv[1]).read() table = ast.literal_eval(re.search(r"ROLE_TARGETS = (\{.*?\n\})", source, re.S).group(1)) print("\n".join(sorted(table))) ROLES )" page_roles="$(grep -o 'key: "[a-z]*"' "$page" | sed 's/key: "//; s/"//' | sort -u)" missing="$(comm -23 <(printf '%s\n' "$helper_roles") <(printf '%s\n' "$page_roles") | tr '\n' ' ')" extra="$(comm -13 <(printf '%s\n' "$helper_roles") <(printf '%s\n' "$page_roles") | tr '\n' ' ')" [[ -z "${missing// }" ]] || fail "the helper can set these roles but the settings page never offers them: $missing" [[ -z "${extra// }" ]] || fail "the settings page offers roles the helper cannot set: $extra" # The editor Panama ships must launch in Panama's terminal. The stock # nvim.desktop sets Terminal=true, which hands the launch to whatever the # system considers default -- not necessarily the terminal this desktop themes. entry="$repo_dir/config/local/share/applications/panama-nvim.desktop" [[ -r "$entry" ]] || fail 'panama-nvim.desktop is missing' grep -q '^Exec=kitty ' "$entry" || fail 'the shipped editor entry does not launch kitty explicitly' grep -q '^Terminal=false' "$entry" \ || fail 'the editor entry sets Terminal=true, which defers to the system terminal rather than kitty' printf 'default apps family contract: PASS (%d role families consistent)\n' "$checked"