#!/usr/bin/env bash # Authoritative deterministic QA for the desfoto.de static site. # # ./qa -> build the site, prove the build is reproducible, validate the # nginx configuration, and run the structural test suite. # # Exit code 0 means the repository is releasable as-is. set -euo pipefail cd "$(dirname "$0")/.." root="$(pwd)" tmp="$(mktemp -d)" trap 'rm -rf "$tmp"' EXIT step() { printf '\n== %s\n' "$1"; } step "build" python3 scripts/build-site.py step "build is reproducible" # sitemap.xml and .well-known/security.txt embed the build date on purpose and # are therefore excluded from the byte-for-byte comparison. hash_tree() { (cd "$root/site" && find . -type f \ ! -name sitemap.xml ! -path './.well-known/*' -print0 | sort -z | xargs -0 sha256sum) } hash_tree >"$tmp/before.txt" python3 scripts/build-site.py >/dev/null hash_tree >"$tmp/after.txt" if ! diff -u "$tmp/before.txt" "$tmp/after.txt"; then echo "FAIL: the build output is not reproducible" >&2 exit 1 fi echo "rebuild produced byte-identical output for $(wc -l <"$tmp/after.txt") files" step "nginx configuration" if command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then docker run --rm \ -v "$root/nginx.conf:/etc/nginx/conf.d/default.conf:ro" \ -v "$root/site:/usr/share/nginx/html:ro" \ nginx:1.28-alpine nginx -t else echo "SKIP: no usable docker daemon; nginx -t cannot be verified here" >&2 exit 1 fi step "compose configuration" if docker compose version >/dev/null 2>&1; then DESFOTO_CHECK_ONLY=1 docker compose -f compose.yml -f compose.vps.yml config --quiet echo "compose.yml + compose.vps.yml are valid" fi step "structural tests" python3 -m unittest discover -s tests -p 'test_*.py' -v step "shellcheck" if command -v shellcheck >/dev/null 2>&1; then mapfile -t scripts < <(git ls-files '*.sh' '.ocauto/*') if [ "${#scripts[@]}" -gt 0 ]; then shellcheck "${scripts[@]}" fi fi printf '\nQA PASS\n'