8ae0eb239a
Public demo of Factory 4 Life project management: - Gate definitions S0 (spec), S1 (build), S2 (integration), S3 (production ready) - Hardware project template with PlatformIO, KiCad CI, and Makefile for fab packages - JSON schema for .kill-life.yaml manifest - CLI scripts: create-project, validate-gates, dashboard with color-coded status output - Example project: led-controller (S0+S1 passed, S2 in progress)
148 lines
4.7 KiB
Bash
Executable File
148 lines
4.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Validate gate criteria for a Factory 4 Life project
|
|
# Usage: ./scripts/validate-gates.sh <org/repo> [--gate s0|s1|s2|s3] [--local <path>]
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage: validate-gates.sh <org/repo> [options]
|
|
|
|
Options:
|
|
--gate <s0|s1|s2|s3> Validate specific gate (default: all)
|
|
--local <path> Use local directory instead of cloning
|
|
--json Output as JSON
|
|
-h, --help Show this help
|
|
|
|
Example:
|
|
./scripts/validate-gates.sh electron-rare/hypnoled --gate s1
|
|
./scripts/validate-gates.sh electron-rare/hypnoled --local ~/hypnoled
|
|
EOF
|
|
}
|
|
|
|
REPO=""
|
|
GATE=""
|
|
LOCAL_PATH=""
|
|
JSON_OUTPUT=false
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--gate) GATE="$2"; shift 2 ;;
|
|
--local) LOCAL_PATH="$2"; shift 2 ;;
|
|
--json) JSON_OUTPUT=true; shift ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*) REPO="$1"; shift ;;
|
|
esac
|
|
done
|
|
|
|
[[ -n "$REPO" ]] || { echo "Error: repo required" >&2; usage >&2; exit 1; }
|
|
|
|
# Get project directory
|
|
if [[ -n "$LOCAL_PATH" ]]; then
|
|
PROJECT_DIR="$LOCAL_PATH"
|
|
else
|
|
TMPDIR=$(mktemp -d)
|
|
trap "rm -rf $TMPDIR" EXIT
|
|
gh repo clone "$REPO" "$TMPDIR/repo" -- --depth 1 2>/dev/null
|
|
PROJECT_DIR="$TMPDIR/repo"
|
|
fi
|
|
|
|
PASS=0
|
|
FAIL=0
|
|
WARN=0
|
|
|
|
check() {
|
|
local gate="$1" desc="$2" result="$3"
|
|
if [[ "$result" == "pass" ]]; then
|
|
echo " [PASS] $desc"
|
|
((PASS++))
|
|
elif [[ "$result" == "warn" ]]; then
|
|
echo " [WARN] $desc"
|
|
((WARN++))
|
|
else
|
|
echo " [FAIL] $desc"
|
|
((FAIL++))
|
|
fi
|
|
}
|
|
|
|
validate_s0() {
|
|
echo "=== Gate S0: Spec Ready ==="
|
|
check s0 ".kill-life.yaml exists" \
|
|
"$([[ -f "$PROJECT_DIR/.kill-life.yaml" ]] && echo pass || echo fail)"
|
|
check s0 "docs/specs/01_spec.md exists" \
|
|
"$([[ -f "$PROJECT_DIR/docs/specs/01_spec.md" ]] && echo pass || echo fail)"
|
|
check s0 "docs/specs/02_arch.md exists" \
|
|
"$([[ -f "$PROJECT_DIR/docs/specs/02_arch.md" ]] && echo pass || echo fail)"
|
|
check s0 "docs/specs/03_plan.md exists" \
|
|
"$([[ -f "$PROJECT_DIR/docs/specs/03_plan.md" ]] && echo pass || echo fail)"
|
|
check s0 "README.md exists" \
|
|
"$([[ -f "$PROJECT_DIR/README.md" ]] && echo pass || echo fail)"
|
|
}
|
|
|
|
validate_s1() {
|
|
echo "=== Gate S1: Build OK ==="
|
|
|
|
# Firmware checks
|
|
if [[ -f "$PROJECT_DIR/firmware/platformio.ini" ]]; then
|
|
check s1 "platformio.ini exists" pass
|
|
check s1 "firmware/src/ has source files" \
|
|
"$(find "$PROJECT_DIR/firmware/src" -name '*.cpp' -o -name '*.c' -o -name '*.h' 2>/dev/null | head -1 | grep -q . && echo pass || echo fail)"
|
|
fi
|
|
|
|
# Hardware checks
|
|
if [[ -d "$PROJECT_DIR/hardware/pcb" ]]; then
|
|
check s1 "KiCad schematic exists" \
|
|
"$(find "$PROJECT_DIR/hardware/pcb" -name '*.kicad_sch' 2>/dev/null | head -1 | grep -q . && echo pass || echo fail)"
|
|
check s1 "KiCad PCB exists" \
|
|
"$(find "$PROJECT_DIR/hardware/pcb" -name '*.kicad_pcb' 2>/dev/null | head -1 | grep -q . && echo pass || echo fail)"
|
|
check s1 "BOM exists" \
|
|
"$([[ -d "$PROJECT_DIR/hardware/bom" ]] && find "$PROJECT_DIR/hardware/bom" -name '*.csv' 2>/dev/null | head -1 | grep -q . && echo pass || echo warn)"
|
|
fi
|
|
|
|
# CI check
|
|
check s1 "GitHub Actions CI configured" \
|
|
"$(find "$PROJECT_DIR/.github/workflows" -name '*.yml' -o -name '*.yaml' 2>/dev/null | head -1 | grep -q . && echo pass || echo fail)"
|
|
}
|
|
|
|
validate_s2() {
|
|
echo "=== Gate S2: Integration ==="
|
|
check s2 "Integration report exists" \
|
|
"$([[ -f "$PROJECT_DIR/docs/integration_report.md" ]] && echo pass || echo fail)"
|
|
check s2 "Prototype documentation exists" \
|
|
"$([[ -d "$PROJECT_DIR/docs/prototype" ]] && echo pass || echo warn)"
|
|
check s2 "Integration tests exist" \
|
|
"$(find "$PROJECT_DIR/tests/integration" -type f 2>/dev/null | head -1 | grep -q . && echo pass || echo warn)"
|
|
}
|
|
|
|
validate_s3() {
|
|
echo "=== Gate S3: Production Ready ==="
|
|
check s3 "User guide exists" \
|
|
"$([[ -f "$PROJECT_DIR/docs/user_guide.md" ]] && echo pass || echo fail)"
|
|
check s3 "Manufacturing guide exists" \
|
|
"$([[ -f "$PROJECT_DIR/docs/manufacturing.md" ]] && echo pass || echo warn)"
|
|
check s3 "Release directory exists" \
|
|
"$([[ -d "$PROJECT_DIR/releases" ]] && echo pass || echo fail)"
|
|
|
|
if [[ -d "$PROJECT_DIR/hardware/pcb" ]]; then
|
|
check s3 "Gerbers generated" \
|
|
"$(find "$PROJECT_DIR/releases" -name '*.gbr' -o -name '*.gtl' 2>/dev/null | head -1 | grep -q . && echo pass || echo fail)"
|
|
fi
|
|
}
|
|
|
|
# Run validations
|
|
echo "Validating: $REPO"
|
|
echo ""
|
|
|
|
case "$GATE" in
|
|
s0) validate_s0 ;;
|
|
s1) validate_s0; validate_s1 ;;
|
|
s2) validate_s0; validate_s1; validate_s2 ;;
|
|
s3) validate_s0; validate_s1; validate_s2; validate_s3 ;;
|
|
"") validate_s0; validate_s1; validate_s2; validate_s3 ;;
|
|
*) echo "Unknown gate: $GATE" >&2; exit 1 ;;
|
|
esac
|
|
|
|
echo ""
|
|
echo "Results: ${PASS} passed, ${FAIL} failed, ${WARN} warnings"
|
|
[[ "$FAIL" -eq 0 ]] && exit 0 || exit 1
|