#!/usr/bin/env bash # Does a repo generated from this template pass its own gate? # # WHY THIS EXISTS # # `golangci-lint run ./...` and friends cannot see this template's entrypoint. # Go skips directories whose names begin with "_" or "." when expanding package # patterns, and the entrypoint lives in cmd/__PROJECT_NAME__: # # $ go list ./... # __MODULE_PATH__/internal/web <- no cmd package # # After substitution the directory has a real name, becomes visible, and any # defect in it appears — in the child, not here. That is how template-go-agent # shipped a broken entrypoint for three months while its own CI stayed green # (template-go-agent#1, #2). # # Linting the directory by name is not a fix: `golangci-lint run # ./cmd/__PROJECT_NAME__` works, but reports formatter findings that do not # exist in a generated repo, because gofumpt's import grouping depends on the # module path and this one is a placeholder. # # So this substitutes the tracked tree into a throwaway child and gates the # RESULT, which is the only artifact anyone actually runs. # # Usage: ./scripts/tests/generated-child_test.sh set -uo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" pass=0; fail=0 ok() { printf ' ok %s\n' "$1"; pass=$((pass+1)); } bad() { printf ' FAIL %s\n %s\n' "$1" "${2:-}"; fail=$((fail+1)); } echo "generated-child tests" for tool in golangci-lint templ; do if ! command -v "$tool" >/dev/null 2>&1; then echo " SKIP $tool not installed — cannot gate the generated child" >&2 exit 0 fi done tmp="$(mktemp -d)"; trap 'rm -rf "$tmp"' EXIT # Tracked files from the WORKING TREE, not `git archive HEAD`: this runs inside # `task check`, so it must gate what is about to be committed rather than the # previous commit. `git ls-files` keeps the file set honest while taking # current content. ( cd "$ROOT" && git ls-files -z ) | while IFS= read -r -d '' f; do mkdir -p "$tmp/$(dirname "$f")" cp "$ROOT/$f" "$tmp/$f" done if [ ! -d "$tmp/cmd/__PROJECT_NAME__" ]; then bad "the template still has a placeholder entrypoint directory" \ "cmd/__PROJECT_NAME__ not found — has the layout changed?" echo; echo "$pass passed, $fail failed"; exit 1 fi mv "$tmp/cmd/__PROJECT_NAME__" "$tmp/cmd/childproj" find "$tmp" -type f \( -name '*.go' -o -name '*.templ' -o -name '*.mod' -o -name '*.yml' -o -name '*.md' \) \ -exec sed -i \ -e 's|__MODULE_PATH__|git.d-ma.be/mathias/childproj|g' \ -e 's|__PROJECT_NAME__|childproj|g' {} + # Generated files are gitignored, so the child arrives without them — exactly as # a freshly scaffolded repo does. Generating here also proves the pinned # generator and the pinned library agree, which is template-go-web#8. out="$(cd "$tmp" && templ generate 2>&1)" if [ $? -eq 0 ]; then ok "templ generate succeeds in the generated child" else bad "templ generate succeeds in the generated child" "$(printf '%s' "$out" | tail -3)" fi # The whole point: the entrypoint must be visible to Go tooling in the child. # If this regresses, everything below it passes for the wrong reason. if ( cd "$tmp" && go list ./... 2>/dev/null ) | grep -q '/cmd/childproj$'; then ok "the generated child's entrypoint is visible to go list" else bad "the generated child's entrypoint is visible to go list" \ "cmd package missing from ./... — the child would be unchecked too" fi out="$(cd "$tmp" && go build ./... 2>&1)" [ -z "$out" ] && ok "the generated child builds" \ || bad "the generated child builds" "$(printf '%s' "$out" | head -3)" out="$(cd "$tmp" && go vet ./... 2>&1)" [ -z "$out" ] && ok "the generated child passes go vet" \ || bad "the generated child passes go vet" "$(printf '%s' "$out" | head -3)" out="$(cd "$tmp" && golangci-lint run ./... 2>&1)" if printf '%s' "$out" | grep -qE '^[0-9]+ issues:|issues:$'; then bad "the generated child passes golangci-lint" "$(printf '%s' "$out" | grep -E '\.go:[0-9]+' | head -3)" else ok "the generated child passes golangci-lint" fi echo echo "$pass passed, $fail failed" [ "$fail" -eq 0 ]