Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
b1b1ea139This file is pinned to the selected release. Download the original to open it in your design tool.
#!/usr/bin/env bash
# SPDX-License-Identifier: MIT
# Local CI — the same gates the hosted workflow would run (GitHub Actions is
# unavailable on this account per the zero-budget policy; run this before push).
# ./scripts/check.sh # tests, mypy, CAD asserts, envelope, docs, (KiCad when present)
# ./scripts/check.sh --full # + re-render the gallery and fail if it drifted from the CAD
# ./scripts/check.sh --sitl # + ArduPilot SITL end-to-end (needs a SITL build, see SITL.md)
set -euo pipefail
cd "$(dirname "$0")/.."
FULL=0; SITL=0
for a in "$@"; do case "$a" in --full) FULL=1;; --sitl) SITL=1;; *) echo "unknown flag $a"; exit 2;; esac; done
# .venv from scripts/setup-dev.sh when present, else the system python3 (stdlib is enough)
PY=python3; [ -x .venv/bin/python ] && PY=.venv/bin/python
echo "== software test suite ($($PY --version))"
$PY software/tests/test_backend.py
if $PY -m mypy --version >/dev/null 2>&1; then
echo "== mypy (typed policy modules, mypy.ini)"
$PY -m mypy
fi
echo "== every model file parses + evaluates"
cd cad
for f in mower.scad assembly.scad enclosure.scad actuator_brackets.scad \
gps_mast.scad lidar_mount.scad camera_mount.scad controls_bracket.scad badge.scad \
attachments_brackets.scad sensor_mounts.scad; do
echo " $f"
# openscad exits 0 on a failed assert() — fail on any ERROR line instead
if openscad -o /tmp/check.csg "$f" 2>&1 | grep -E "^(ERROR|WARNING: Assertion)"; then
echo "FAIL: $f"; exit 1
fi
done
echo "== envelope self-check vs the published spec"
openscad -o /tmp/check.csg assembly.scad 2>&1 | grep "ENVELOPE" | tee /tmp/env.txt
grep -q "L=1968" /tmp/env.txt && grep -q "W=1610" /tmp/env.txt && grep -q "H=1039" /tmp/env.txt
if [ "$FULL" = 1 ]; then
echo "== renders fresh vs CAD (--full)"
./render_gallery.sh >/dev/null 2>&1
cd ..
if ! $PY scripts/renders_match.py cad/renders; then # perceptual, not byte-exact (see script)
echo "STALE RENDERS: cad/renders changed after re-render — commit the fresh ones"; exit 1
fi
cd cad
fi
# MowerCarrier PCB gates (#39): ERC clean, DRC with zero errors + schematic parity
if ls ../hardware/pcb/kicad/*.kicad_sch >/dev/null 2>&1 && command -v kicad-cli >/dev/null; then
echo "== KiCad ERC + DRC (MowerCarrier: errors + schematic parity)"
for f in ../hardware/pcb/kicad/*.kicad_sch; do
kicad-cli sch erc --severity-all --exit-code-violations -o /tmp/erc.rpt "$f" 2>&1 | grep -E "violation" || true
kicad-cli sch erc --severity-all --exit-code-violations -o /tmp/erc.rpt "$f" >/dev/null 2>&1 || { cat /tmp/erc.rpt; exit 1; }
done
for f in ../hardware/pcb/kicad/*.kicad_pcb; do
kicad-cli pcb drc --severity-error --schematic-parity --exit-code-violations -o /tmp/drc.rpt "$f" >/dev/null 2>&1 \
|| { cat /tmp/drc.rpt; exit 1; }
grep -E "^\*\* Found" /tmp/drc.rpt | sed 's/^/ /'
done
fi
# ESP32 lap-bar firmware compiles (current Arduino-ESP32 core) — when the toolchain is installed
if command -v arduino-cli >/dev/null && arduino-cli core list 2>/dev/null | grep -q "^esp32:esp32"; then
echo "== firmware: lapbar_controller compiles (esp32:esp32)"
arduino-cli compile --fqbn esp32:esp32:esp32 --warnings default ../firmware/lapbar_controller 2>&1 \
| grep -E "error|warning|Sketch uses" | sed 's/^/ /'
arduino-cli compile --fqbn esp32:esp32:esp32 ../firmware/lapbar_controller >/dev/null 2>&1 || exit 1
fi
if [ -x ../.venv/bin/mkdocs ]; then
echo "== docs site builds clean (mkdocs --strict)"
(cd .. && NO_MKDOCS_2_WARNING=1 .venv/bin/mkdocs build --strict -q -d "$(mktemp -d)")
fi
if [ "$FULL" = 1 ] && [ -x ../hardware/pcb/kicad/gen_kicad.sh ] && command -v freerouting >/dev/null; then
echo "== MowerCarrier regenerates byte-identically from design.py (--full)"
../hardware/pcb/kicad/gen_kicad.sh all >/dev/null 2>&1
if ! git -C .. diff --quiet -- hardware/pcb/kicad/*.kicad_sch hardware/pcb/kicad/*.kicad_pcb; then
echo "STALE PCB: the committed KiCad files differ from what design.py/gen_pcb.py generate — commit them"; exit 1
fi
# design files match, so the derived exports (zip timestamps, SVG dates, ray-trace noise) are too
git -C .. checkout -- hardware/pcb/kicad/fab hardware/pcb/schematic.svg hardware/pcb/layout.svg \
hardware/pcb/render-top.png hardware/pcb/render-iso.png
fi
if [ "$SITL" = 1 ]; then
echo "== ArduPilot SITL end-to-end (scripts/sitl.sh + sitl_smoke.py)"
cd ..
PORT=8099 ./scripts/sitl.sh > /tmp/check_sitl.log 2>&1 &
SITLPID=$!
until grep -qE "safety switch|PROBLEM|Traceback|no SITL" /tmp/check_sitl.log; do
kill -0 $SITLPID 2>/dev/null || { cat /tmp/check_sitl.log; exit 1; }; sleep 1; done
if grep -qE "PROBLEM|Traceback|no SITL" /tmp/check_sitl.log; then cat /tmp/check_sitl.log; kill $SITLPID; exit 1; fi
$PY scripts/sitl_smoke.py http://localhost:8099 || { kill $SITLPID; exit 1; }
kill $SITLPID; wait $SITLPID 2>/dev/null || true
cd cad
fi
echo "ALL CHECKS PASSED"