#!/usr/bin/env bash # Every QML component in a module directory must be listed in that directory's # qmldir. # # An unregistered component is not a quiet problem. Quickshell fails the whole # configuration with "X is not a type" -- so the settings window dies, and with # it the bar, the dock and the rest of the shell. It has happened twice: once # adding ContainersPage, once adding PickerRow and SettingsTabs. Both times the # file was written, the live shell hot-reloaded, and the desktop went down while # somebody was using it. # # The failure is trivial to prevent and expensive to discover, which is exactly # what a contract is for. This one is pure file inspection: no shell is started, # so it can be run before the change ever reaches the running desktop. set -uo pipefail repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" shell_dir="$repo_dir/config/dot/quickshell" fail() { printf 'qmldir registration contract: %s\n' "$1" >&2 exit 1 } [[ -d "$shell_dir" ]] || fail "missing $shell_dir" checked=0 missing=() while IFS= read -r qmldir; do directory="$(dirname "$qmldir")" for component in "$directory"/*.qml; do [[ -e "$component" ]] || continue name="$(basename "$component" .qml)" checked=$((checked + 1)) # A singleton declares itself with `singleton NAME`; everything else is # `NAME VERSION FILE`. Either form counts as registered. grep -qE "^(singleton +)?${name}( |$)" "$qmldir" \ || missing+=("${directory#"$repo_dir"/}/$name.qml") done done < <(find "$shell_dir" -name qmldir) if (( ${#missing[@]} > 0 )); then printf 'qmldir registration contract: %d component(s) are not registered:\n' "${#missing[@]}" >&2 printf ' %s\n' "${missing[@]}" >&2 printf 'Quickshell fails the entire configuration on these, taking the shell down with it.\n' >&2 exit 1 fi (( checked > 0 )) || fail 'no components were checked, so this proves nothing' printf 'qmldir registration contract: ok (%d components registered)\n' "$checked"