Skip to content

Verify_scaffold

FieldValue
TypeSkill Resource
Source~/.copilot/skills/backend/scripts/verify_scaffold.sh
DescriptionNot specified

Source Content

#!/usr/bin/env bash
# Verify a repo scaffolded by the backend skill actually delivers what SKILL.md
# promises: it builds, it has the standard layout, Helm/Dockerfile/CI exist,
# tests pass, and OpenAPI codegen output is present when a spec exists.
#
# Usage:
# verify_scaffold.sh [repo-dir] # defaults to .
#
# Exit code is non-zero if any hard check fails. Missing external tools
# (go, task, docker) degrade that check to a skipped warning rather than a
# hard failure, so the house-rule / file-existence checks still run in a
# bare CI shell.
set -uo pipefail
REPO_DIR="${1:-.}"
cd "$REPO_DIR" || { echo "Usage: $0 [repo-dir] — directory not found" >&2; exit 2; }
REPO_DIR="$(pwd)"
fail=0
warn() { printf '⚠️ %s\n' "$1"; }
error() { printf '❌ %s\n' "$1"; fail=1; }
ok() { printf '✅ %s\n' "$1"; }
echo "== Verifying scaffold at $REPO_DIR =="
echo
echo "== go.mod present =="
if [[ -f go.mod ]]; then
ok "go.mod present"
else
error "no go.mod at repo root — not a Go module"
fi
echo
echo "== go build ./... =="
if command -v go >/dev/null 2>&1; then
if [[ -f go.mod ]]; then
if go build ./... 2>/tmp/gfb_build_err.$$; then
ok "go build ./... clean"
else
error "go build ./... failed:"
sed 's/^/ /' /tmp/gfb_build_err.$$ >&2
fi
rm -f /tmp/gfb_build_err.$$
else
warn "no go.mod — skipped build"
fi
else
warn "go not on PATH — skipped build"
fi
echo
echo "== clean-architecture layout under internal/ =="
required_layers=(handler service repository domain config observability)
missing_layers=""
for layer in "${required_layers[@]}"; do
if [[ ! -d "internal/$layer" ]]; then
missing_layers+="internal/$layer "
fi
done
if [[ -z "$missing_layers" ]]; then
ok "all six internal/ layers present (handler, service, repository, domain, config, observability)"
else
error "missing internal/ layer directories: $missing_layers"
fi
echo
echo "== cmd entrypoint =="
if find cmd -type f -name '*.go' 2>/dev/null | grep -q .; then
ok "cmd/ entrypoint present"
else
error "no cmd/**/*.go entrypoint found"
fi
echo
echo "== Helm chart under .p3/helm/ =="
if [[ -f .p3/helm/Chart.yaml ]]; then
ok ".p3/helm/Chart.yaml present"
else
error ".p3/helm/Chart.yaml not found — Helm chart is required output"
fi
echo
echo "== Dockerfile (distroless, non-root) =="
if [[ -f Dockerfile ]]; then
ok "Dockerfile present"
if grep -qiE 'distroless' Dockerfile; then
ok "Dockerfile references a distroless base image"
else
error "Dockerfile does not reference a distroless base image (expected gcr.io/distroless/...)"
fi
if grep -qiE ':latest' Dockerfile; then
error "Dockerfile pins an image tag to :latest — must be an exact version or git SHA"
else
ok "no :latest tag in Dockerfile"
fi
else
error "no Dockerfile at repo root"
fi
echo
echo "== CI workflow files =="
if find .github/workflows -type f \( -name '*.yml' -o -name '*.yaml' \) 2>/dev/null | grep -q .; then
ok ".github/workflows/ has at least one workflow file"
if grep -rqiE 'cosign' .github/workflows/ 2>/dev/null; then
ok "CI references cosign (image signing)"
else
warn "CI does not reference cosign — image signing may be missing"
fi
if grep -rqiE 'trivy' .github/workflows/ 2>/dev/null; then
ok "CI references Trivy (image scanning)"
else
warn "CI does not reference Trivy — image scanning may be missing"
fi
else
error "no .github/workflows/*.yml found"
fi
echo
echo "== Taskfile.yml present =="
if [[ -f Taskfile.yml ]]; then
ok "Taskfile.yml present"
else
error "no Taskfile.yml at repo root"
fi
echo
echo "== OpenAPI codegen output (only if api/openapi.yaml exists) =="
if [[ -f api/openapi.yaml ]]; then
ok "api/openapi.yaml present"
if find . -type f -name '*.gen.go' -not -path '*/vendor/*' 2>/dev/null | grep -q .; then
ok "oapi-codegen output (*.gen.go) present"
else
error "api/openapi.yaml exists but no generated *.gen.go output found — run the oapi-codegen build step"
fi
else
warn "no api/openapi.yaml — skipped codegen-output check (design the contract with api-designer first)"
fi
echo
echo "== task test / go test ./... =="
if command -v task >/dev/null 2>&1 && [[ -f Taskfile.yml ]]; then
if task test >/tmp/gfb_test.$$ 2>&1; then
ok "task test passed"
else
error "task test failed:"
sed 's/^/ /' /tmp/gfb_test.$$ >&2
fi
rm -f /tmp/gfb_test.$$
elif command -v go >/dev/null 2>&1 && [[ -f go.mod ]]; then
warn "task not on PATH — falling back to go test ./..."
if go test ./... >/tmp/gfb_test.$$ 2>&1; then
ok "go test ./... passed"
else
error "go test ./... failed:"
sed 's/^/ /' /tmp/gfb_test.$$ >&2
fi
rm -f /tmp/gfb_test.$$
else
warn "neither task nor go on PATH — skipped test run"
fi
echo
echo "== house rule: no raw 'go func()' in internal/ =="
if [[ -d internal ]]; then
naked_go=$(grep -rnE '(^|[^a-zA-Z_.])go func\(' --include='*.go' internal 2>/dev/null | grep -v '_test.go' || true)
if [[ -n "$naked_go" ]]; then
error "found a raw 'go func()' in internal/ — route through sourcegraph/conc:"
echo "$naked_go" | sed 's/^/ /'
else
ok "no naked 'go func()' in internal/"
fi
else
warn "no internal/ directory — skipped"
fi
echo
if [[ "$fail" -eq 0 ]]; then
echo "All checks passed."
else
echo "One or more checks failed — see ❌ lines above."
fi
exit "$fail"