diff --git a/scripts/check-phase10.2.sh b/scripts/check-phase10.2.sh new file mode 100755 index 0000000..8281ccb --- /dev/null +++ b/scripts/check-phase10.2.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env bash +# Phase 10.2 fail-closed gate for the nested framework layout and onboarding. +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +APP="$(cd "$ROOT/../fonoteka.go" && pwd)" +NAMES=(backpack boardwalk bonfire bouncer cabana compass festival fetchguard lagoon pact party phrasebook postcard surf tide towel wire wristband) +IMPORT_PREFIX='git.golem15.com/golem15/summercms/' +STALE_STATUS='Pre-alpha. Planning and research. Nothing runs yet.' + +usage() { + cat >&2 <<'EOF' +usage: + check-phase10.2.sh --self-test + check-phase10.2.sh --layout + check-phase10.2.sh --imports + check-phase10.2.sh --readmes + check-phase10.2.sh --status + check-phase10.2.sh --go + check-phase10.2.sh --all +EOF + exit 2 +} + +refuse() { + echo "refuse: $*" >&2 + return 1 +} + +tracked_source_files() { + local repo="$1" + if git -C "$repo" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + git -C "$repo" ls-files -z -- '*.go' '*.tmpl' '*.sh' + return + fi + ( + cd "$repo" + find . -path './.planning' -prune -o -type f \( -name '*.go' -o -name '*.tmpl' -o -name '*.sh' \) -print0 + ) +} + +check_layout() { + local root="$1" name + for name in "${NAMES[@]}"; do + if [[ -d "$root/$name" ]]; then + refuse "leftover root dir: $name" + return 1 + fi + done +} + +check_imports_in() { + local repo="$1" file name + while IFS= read -r -d '' file; do + [[ "$file" != 'scripts/check-phase10.2.sh' ]] || continue + for name in "${NAMES[@]}"; do + if grep -Fq "${IMPORT_PREFIX}${name}" "$repo/$file"; then + refuse "old import: $repo/$file uses ${name} outside modules" + return 1 + fi + done + done < <(tracked_source_files "$repo") +} + +check_imports() { + check_imports_in "$1" || return 1 + check_imports_in "$2" +} + +check_readmes() { + local root="$1" name + for name in "${NAMES[@]}"; do + if [[ ! -f "$root/modules/$name/README.md" ]]; then + refuse "missing README: modules/$name/README.md" + return 1 + fi + done +} + +check_status() { + local root="$1" + if grep -Fq "$STALE_STATUS" "$root/README.md"; then + refuse "stale Status: README still claims nothing runs" + return 1 + fi +} + +check_hygiene() { + local root="$1" app="$2" + check_layout "$root" || return 1 + check_imports "$root" "$app" || return 1 + check_readmes "$root" || return 1 + check_status "$root" || return 1 +} + +run_go() { + (cd "$ROOT" && go vet ./...) + (cd "$ROOT" && go test ./...) + (cd "$APP" && go vet ./... ./plugins/golem15/fonoteka/... ./plugins/golem15/user/...) + (cd "$APP" && go test ./... ./plugins/golem15/fonoteka/... ./plugins/golem15/user/...) + echo "phase10.2 go passed" +} + +expect_refusal() { + local name="$1" want="$2" root="$3" app="$4" output + if output="$(check_hygiene "$root" "$app" 2>&1)"; then + echo "refuse: self-test accepted planted $name" >&2 + exit 1 + fi + if ! grep -Fq "$want" <<<"$output"; then + echo "refuse: self-test $name failed for the wrong rule: $output" >&2 + exit 1 + fi +} + +run_self_test() { + bash -n "${BASH_SOURCE[0]}" + local scratch app name + scratch="$(mktemp -d)" + app="$(mktemp -d)" + trap 'rm -rf "$scratch" "$app"' RETURN + cp "$ROOT/README.md" "$scratch/README.md" + for name in "${NAMES[@]}"; do + mkdir -p "$scratch/modules/$name" + cp "$ROOT/modules/$name/README.md" "$scratch/modules/$name/README.md" + done + + check_hygiene "$scratch" "$app" + + mkdir "$scratch/backpack" + expect_refusal 'leftover root dir' 'leftover root dir' "$scratch" "$app" + rmdir "$scratch/backpack" + + printf '%s%s\n' "$IMPORT_PREFIX" backpack >"$scratch/old_import.go" + expect_refusal 'old import' 'old import' "$scratch" "$app" + rm "$scratch/old_import.go" + + mv "$scratch/modules/backpack/README.md" "$scratch/modules/backpack/README.saved" + expect_refusal 'missing README' 'missing README' "$scratch" "$app" + mv "$scratch/modules/backpack/README.saved" "$scratch/modules/backpack/README.md" + + printf '\n%s\n' "$STALE_STATUS" >>"$scratch/README.md" + expect_refusal 'stale Status' 'stale Status' "$scratch" "$app" + + echo "phase10.2 self-test passed" +} + +case "${1:-}" in +--self-test) run_self_test ;; +--layout) check_layout "$ROOT" ;; +--imports) check_imports "$ROOT" "$APP" ;; +--readmes) check_readmes "$ROOT" ;; +--status) check_status "$ROOT" ;; +--go) run_go ;; +--all) + check_hygiene "$ROOT" "$APP" + run_go + echo "phase10.2 check passed" + ;; +*) usage ;; +esac