#!/usr/bin/env bash # Run only the Bats tests affected by the staged paths. # # Usage: # scripts/test-changed.sh # inspect the staged commit # scripts/test-changed.sh --list # print selected test files # scripts/test-changed.sh --paths FILE ... # # --paths is intended for tests and local experimentation. The normal mode is # deliberately staged-only so a pre-commit hook cannot accidentally validate # an unrelated dirty working-tree file. set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" LIST_ONLY=0 USE_PATHS=0 declare -a CHANGED=() die() { printf 'ERROR: %s\n' "$*" >&2 exit 2 } while [ "$#" -gt 0 ]; do case "$1" in --list) LIST_ONLY=1 shift ;; --paths) USE_PATHS=1 shift while [ "$#" -gt 0 ] && [[ "$1" != --* ]]; do CHANGED+=("$1") shift done ;; --) shift CHANGED+=("$@") break ;; *) die "unknown option '$1' (use --list or --paths FILE ...)" ;; esac done cd "$REPO_ROOT" if [ "$USE_PATHS" -eq 0 ]; then while IFS= read -r -d '' path; do CHANGED+=("$path") done < <(git diff --cached --name-only -z --diff-filter=ACMRTUXB) fi [ "${#CHANGED[@]}" -gt 0 ] || { printf 'No staged paths; no focused tests to run.\n' exit 0 } declare -a TESTS=() add_test() { local test="$1" local existing for existing in "${TESTS[@]:-}"; do [ "$existing" != "$test" ] || return 0 done TESTS+=("$test") } for path in "${CHANGED[@]}"; do case "$path" in tests/*.bats) add_test "$path" ;; tests/helpers.bash|tests/run.sh) die "$path is shared test infrastructure; run ./tests/run.sh explicitly" ;; .githooks/*) add_test tests/changed_tests.bats ;; dankserver/compose.yaml|dankclaw/compose.yaml) add_test tests/compose_validity.bats add_test tests/env_consistency.bats ;; dankserver/Caddyfile|dankclaw/Caddyfile) add_test tests/caddyfile.bats ;; dankserver/.env.example|dankclaw/.env.example) add_test tests/env_consistency.bats ;; shared/homepage/*) add_test tests/homepage_caddy.bats add_test tests/homepage_config.bats add_test tests/homepage_deploy.bats add_test tests/homepage_pages.bats add_test tests/homepage_checker.bats ;; shared/searxng/*) add_test tests/searxng_limiter.bats add_test tests/searxng_quadlet.bats ;; dankclaw/*.container|dankclaw/*.network) add_test tests/open_webui_quadlet.bats add_test tests/pihole.bats add_test tests/searxng_quadlet.bats ;; scripts/mount-entropy.sh) add_test tests/mount_entropy.bats ;; scripts/frank-default-large.env|scripts/serve-frank-large.sh) add_test tests/frank_large_config.bats add_test tests/llm_secrets.bats add_test tests/scripts_syntax.bats ;; scripts/frank-default-small.env|scripts/serve-frank-small.sh) add_test tests/frank_small_config.bats add_test tests/llm_secrets.bats add_test tests/scripts_syntax.bats ;; scripts/*.service) add_test tests/llm_units.bats add_test tests/scripts_syntax.bats ;; scripts/*) add_test tests/scripts_syntax.bats ;; docs/*) add_test tests/docs_completeness.bats ;; dankclaw/hermes/*) add_test tests/docs_completeness.bats ;; AGENTS.md) # The authoring rulebook; validate the docs vault whenever it changes. add_test tests/docs_completeness.bats ;; *) die "no focused-test mapping for '$path'; add one before committing" ;; esac done if [ "$LIST_ONLY" -eq 1 ]; then printf '%s\n' "${TESTS[@]}" exit 0 fi command -v bats >/dev/null 2>&1 || die 'bats is not installed' printf 'Running focused tests for %d changed path(s):\n' "${#CHANGED[@]}" printf ' %s\n' "${TESTS[@]}" bats --print-output-on-failure "${TESTS[@]}"