Files
electron 8ae0eb239a feat: public example of life-project gates system
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)
2026-04-07 15:39:30 +02:00

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