generated from mathias/template-go-web
Initial commit
This commit is contained in:
Executable
+114
@@ -0,0 +1,114 @@
|
||||
#!/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"
|
||||
|
||||
# A missing tool is a FAILURE, not a skip. An `exit 0` with zero assertions is
|
||||
# indistinguishable from a real pass to anything reading the exit code -- which
|
||||
# is the whole failure mode this harness exists to close.
|
||||
for tool in golangci-lint templ; do
|
||||
if ! command -v "$tool" >/dev/null 2>&1; then
|
||||
bad "$tool is available" "not on PATH — the generated child cannot be gated"
|
||||
echo; echo "$pass passed, $fail failed"; exit 1
|
||||
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' -o -name 'Dockerfile' \) \
|
||||
-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)"; rc=$?
|
||||
if [ "$rc" -eq 0 ]; then
|
||||
ok "templ generate succeeds in the generated child"
|
||||
else
|
||||
bad "templ generate succeeds in the generated child" "exit $rc: $(printf '%s' "$out" | tail -2)"
|
||||
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)"
|
||||
|
||||
# Graded on the EXIT CODE, never on grepping the output. golangci-lint exits
|
||||
# 0 clean / 1 issues found / >1 the linter itself failed. Matching its
|
||||
# human-readable summary instead scores a CRASHED linter as a pass -- bad
|
||||
# config, unwritable TMPDIR, OOM, or simply a future change to its output
|
||||
# format all produce non-matching text. Since this harness is the only gate
|
||||
# covering the entrypoint, that would leave cmd/__PROJECT_NAME__ unchecked with
|
||||
# CI green, which is precisely the defect class this file exists to end.
|
||||
out="$(cd "$tmp" && golangci-lint run ./... 2>&1)"; rc=$?
|
||||
case "$rc" in
|
||||
0) ok "the generated child passes golangci-lint" ;;
|
||||
1) bad "the generated child passes golangci-lint" \
|
||||
"$(printf '%s' "$out" | grep -E '\.go:[0-9]+' | head -3)" ;;
|
||||
*) bad "golangci-lint ran successfully" \
|
||||
"linter exited $rc — the child is UNCHECKED, not clean: $(printf '%s' "$out" | tail -2)" ;;
|
||||
esac
|
||||
|
||||
echo
|
||||
echo "$pass passed, $fail failed"
|
||||
[ "$fail" -eq 0 ]
|
||||
Reference in New Issue
Block a user