#!/usr/bin/env bash # The settings reference is generated, and must not be stale. # # Every other kind of documentation here has drifted at least once: routing that # pointed at a page not containing the setting, contracts that pinned the bug # they were meant to prevent, a comment that failed to stop the person who read # it from making the exact mistake it described. Prose describing 127 settings # would drift the day after it was written. # # So docs/settings.md is generated from the schema, and this fails the moment # the committed copy stops matching. That is the whole mechanism: the document # cannot be wrong for longer than it takes to run the suite. set -uo pipefail repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" generator="$repo_dir/config/dot/quickshell/scripts/panama-settings-docs" doc="$repo_dir/docs/settings.md" fail() { printf 'settings docs contract: %s\n' "$1" >&2 exit 1 } [[ -x "$generator" ]] || fail 'the generator is missing or not executable' scratch="$(mktemp -d /tmp/panama-docs.XXXXXX)" trap 'rm -rf "$scratch"' EXIT git -C "$repo_dir" diff -- docs/settings.md >"$scratch/docs.diff.before" \ || fail 'could not capture the initial docs/settings.md state' assert_doc_unchanged() { git -C "$repo_dir" diff -- docs/settings.md >"$scratch/docs.diff.after" \ && cmp -s "$scratch/docs.diff.before" "$scratch/docs.diff.after" } cleanup() { status=$? trap - EXIT HUP INT TERM if ! assert_doc_unchanged; then printf 'settings docs contract: the contract changed tracked docs/settings.md\n' >&2 status=1 fi rm -rf "$scratch" exit "$status" } trap cleanup EXIT trap 'exit 129' HUP trap 'exit 130' INT trap 'exit 143' TERM "$generator" --check || fail 'docs/settings.md is stale -- run quickshell/scripts/panama-settings-docs and commit the result' assert_doc_unchanged || fail 'the default --check changed tracked docs/settings.md' # A generator that silently emitted nothing would also pass --check against an # equally empty file, so the output is checked for substance too. [[ -s "$doc" ]] || fail 'docs/settings.md is empty' settings="$(grep -c '^| \*\*' "$doc")" schema_keys="$(grep -c 'key: "' "$repo_dir/config/dot/quickshell/config/PreferenceSchema.qml")" # Internal keys are deliberately omitted, so the document is smaller than the # schema -- but not by much. A large gap means the reader stopped recognising # entries and quietly documented a fraction of them. (( settings > schema_keys * 8 / 10 )) \ || fail "the reference documents only $settings of $schema_keys schema keys, which suggests the generator stopped parsing partway" # The compositor-backed settings are the ones worth naming precisely, since # that is the string someone would search the Hyprland docs for. grep -q 'cursor:zoom_factor' "$doc" \ || fail 'compositor option names are missing from the reference' # A stale copy must be detectable, not merely regenerable. Prove the check # actually compares content rather than always returning success. cp "$doc" "$scratch/settings.md" printf '\n\n' >>"$scratch/settings.md" if "$generator" --check --output "$scratch/settings.md" >/dev/null 2>&1; then fail '--check reported success on a modified file, so staleness would never be caught' fi assert_doc_unchanged || fail '--check --output changed tracked docs/settings.md' # An explicit relative destination belongs to the caller's working directory, # not the repository root. ( cd "$scratch" || exit 1 "$generator" --output generated.md >/dev/null ) || fail 'a relative --output path did not resolve from the current directory' assert_doc_unchanged || fail '--output changed tracked docs/settings.md' cmp -s "$doc" "$scratch/generated.md" \ || fail 'generation through a relative --output path produced different documentation' printf 'settings docs contract: PASS (%d settings documented)\n' "$settings"