Cutover of the six surviving dankserver services from compose-era bare
containers to boot-persistent Quadlet units (WantedBy=default.target; podman
5.x v1 generator honors it at user-manager start).
Units are faithful to live inspect ground truth (image pins = running tags,
not compose inventory):
- romm trio shares one data root: /home/dank/Containers/romm mounted whole at
/romm; mariadb datadir and redis data dir live inside it. Aliases mariadb/
redis preserved on dankserver_appnet via Network=...:alias=. Secrets stay in
the host-local env file (EnvironmentFile + ${VAR} expansion in the generated
ExecStart, gitea precedent). copyparty passes its admin account via
Exec=-a ${COPAY_ACCOUNT}:${COPAY_PASSWORD} (Exec= not Command=: the v1
generator hard-fails on Command=); entropy stays rw only there.
- jellyfin keeps every creation-time env as Environment= lines (zero drift);
Device=/dev/dri + GroupAdd=keep-groups preserve hardware transcoding.
- calibre-web pins the exact running image tag (id 986277563e76 =
0.6.27-ls397), pasta network preserved.
Deployer: first-cutover handlers retire the old container, rescue unshadowed
anonymous-volume content (podman volume export + tar --skip-old-files), mv
data roots on the same fs, then start units in dependency order with health
gates. Re-deploys restart only the selected unit. deploy-plan.py maps compose
keys mariadb/redis to the new unit names; allowlists updated. New seam test
tests/dankserver_quadlet.bats pins boot-persistence + v1-generator contract;
test-changed.sh maps the new dirs to it. Full suite green (209 tests).
238 lines
8.6 KiB
Bash
Executable File
238 lines
8.6 KiB
Bash
Executable File
#!/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
|
|
add_test tests/deployment_scoping.bats
|
|
;;
|
|
dankserver/Caddyfile|dankclaw/Caddyfile)
|
|
add_test tests/caddyfile.bats
|
|
add_test tests/deployment_scoping.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
|
|
add_test tests/deployment_scoping.bats
|
|
;;
|
|
shared/searxng/*)
|
|
add_test tests/searxng_limiter.bats
|
|
add_test tests/searxng_quadlet.bats
|
|
add_test tests/deployment_scoping.bats
|
|
;;
|
|
# Caddy left compose.yaml on 2026-08-26 for a quadlet; caddyfile.bats
|
|
# now asserts against the quadlet itself. Route every change under
|
|
# dankclaw/caddy/ to both seams (this block must precede the
|
|
# dankclaw/*.container glob, which would otherwise swallow the unit).
|
|
dankclaw/caddy/*)
|
|
add_test tests/caddy_quadlet.bats
|
|
add_test tests/caddyfile.bats
|
|
add_test tests/docs_completeness.bats
|
|
add_test tests/deployment_scoping.bats
|
|
;;
|
|
dankclaw/*.container|dankclaw/*.network)
|
|
add_test tests/open_webui_quadlet.bats
|
|
add_test tests/pihole.bats
|
|
add_test tests/searxng_quadlet.bats
|
|
add_test tests/deployment_scoping.bats
|
|
;;
|
|
# Anubis is a quadlet + Caddyfile pair: the chain (public listener ->
|
|
# anubis:8923 -> internal :8016 listener) spans both, so any change
|
|
# under the service dir must re-run the caddyfile seam too. This case
|
|
# precedes the dankclaw/*/quadlet/*.container fallback below, which
|
|
# would otherwise swallow it into unrelated seams.
|
|
dankclaw/anubis/*)
|
|
add_test tests/anubis.bats
|
|
add_test tests/caddyfile.bats
|
|
add_test tests/homepage_caddy.bats
|
|
add_test tests/deployment_scoping.bats
|
|
;;
|
|
# Quadlets live under dankclaw/<svc>/quadlet/ (reorganized out of the
|
|
# top-level dankclaw/*.container globs above). Map the whole subtree so
|
|
# changes to the pihole, open-webui, searxng, and gitea quadlets hit
|
|
# their seam tests. The pihole-specific block must come first: shell
|
|
# case matches the first pattern, and dankclaw/pihole/quadlet/*.container
|
|
# also satisfies the dankclaw/*/quadlet/*.container fallback.
|
|
dankclaw/pihole/*)
|
|
add_test tests/pihole.bats
|
|
add_test tests/docs_completeness.bats
|
|
add_test tests/deployment_scoping.bats
|
|
;;
|
|
dankclaw/*/quadlet/*.container|dankclaw/*/quadlet/*.network)
|
|
add_test tests/open_webui_quadlet.bats
|
|
add_test tests/pihole.bats
|
|
add_test tests/searxng_quadlet.bats
|
|
add_test tests/docs_completeness.bats
|
|
add_test tests/deployment_scoping.bats
|
|
;;
|
|
scripts/deploy.sh|scripts/deploy-remote.sh|scripts/deploy-plan.py|scripts/deploy-*-quadlet.sh)
|
|
add_test tests/deployment_flow.bats
|
|
add_test tests/deployment_scoping.bats
|
|
add_test tests/scripts_syntax.bats
|
|
;;
|
|
scripts/mount-entropy.sh)
|
|
add_test tests/mount_entropy.bats
|
|
;;
|
|
scripts/frank-default-large.env|scripts/serve-frank-large.sh)
|
|
add_test tests/llm_units.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/llm_units.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
|
|
add_test tests/deployment_scoping.bats
|
|
;;
|
|
scripts/*)
|
|
add_test tests/scripts_syntax.bats
|
|
;;
|
|
serverAppConfigs-docs/*)
|
|
add_test tests/docs_completeness.bats
|
|
add_test tests/deployment_scoping.bats
|
|
;;
|
|
dankclaw/hermes/*)
|
|
add_test tests/docs_completeness.bats
|
|
;;
|
|
README.md|AGENTS.md)
|
|
# Project guidance changes can affect the documented deployment
|
|
# contract; validate the docs vault and scoped deployment seams.
|
|
add_test tests/docs_completeness.bats
|
|
add_test tests/deployment_scoping.bats
|
|
;;
|
|
dankclaw/comfyui/*)
|
|
add_test tests/docs_completeness.bats
|
|
add_test tests/deployment_scoping.bats
|
|
;;
|
|
dankclaw/udev/*)
|
|
# Host-level udev rules are applied by manual sudo install + udevadm
|
|
# trigger, not dispatched as a service; nothing here is bats-testable.
|
|
;;
|
|
.AGENTTRASH/*)
|
|
# Trash moves are not testable; skip.
|
|
;;
|
|
.gitignore|.env.example)
|
|
# Ignore/ignore-list changes change nothing at runtime; not testable.
|
|
;;
|
|
old/scripts/*|old/scripts/**)
|
|
add_test tests/scripts_syntax.bats
|
|
;;
|
|
# Cutover-era dankserver quadlet units (romm trio, jellyfin, calibre-web,
|
|
# copyparty): the seam checks boot-persistence + v1-generator contract;
|
|
# deploy-flow seams guard the deployer routing that installs them.
|
|
dankserver/romm/*|dankserver/jellyfin/*|dankserver/calibre-web/*|dankserver/copyparty/*)
|
|
add_test tests/dankserver_quadlet.bats
|
|
add_test tests/deployment_flow.bats
|
|
add_test tests/deployment_scoping.bats
|
|
;;
|
|
report/*)
|
|
# Frozen audit reports are point-in-time artifacts; not testable.
|
|
;;
|
|
*)
|
|
die "no focused-test mapping for '$path'; add one before committing"
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Mapped-but-untestable paths (udev rules, trash moves) legitimately produce an
|
|
# empty selection; that is a pass, not a bats usage error.
|
|
[ "${#TESTS[@]}" -gt 0 ] || exit 0
|
|
|
|
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[@]}"
|