Three intertwined bodies of work developed and tested together this session. Full test suite green: python (edge) + phpunit (cloud). ## Edge/Pi install path - setup_online.sh: apt-lock retry (apt-daily contention), Type=exec unit + StartLimitIntervalSec in [Unit], journalctl-on-failure, crash-loop detection. - onnxruntime is now best-effort (no sdist / no 32-bit-ARM wheel); [6/8] probes the real backend and hard-fails if none works. - Hailo HEF auto-fetch: edge/tools/fetch_hailo_hef.py (pinned sha256, refuses to guess 8 vs 8L, HAILO_ARCH override for serial-less boards); hailo_pose.py run() timeout fix. - install_offline.sh: mirrors the backend gate; onnxruntime optional. ## Cafe24 cloud package (cloud/deploy/) - build_cafe24_package.sh emits move_detect_app/ (private, one folder) + www/move_detect/ (docroot subdir); no-dev classmap autoloader; preflight.php; hardened .htaccess. - Subdirectory mount support: Request::baseFrom/pathFor strip the mount prefix; index.php walks up to find the app root; views/JS prefix every URL with APP_BASE; session cookie scoped to the mount. ## Delivery audit — silent-failure & correctness (docs/delivery-audit.md) All 4 blockers + all Phase-1 silent-failure + Phase-2 HIGH correctness: - Edge cloud clients send a custom User-Agent (cafe24 WAF blocks python-requests) and allow_redirects=False. - Daemon fails loud on blank CAFE24_URL/DEVICE_SECRET; run_loop logs swallowed exceptions; heartbeat carries real fps + per-camera health; UvcSource reopens a dead camera; offline queue is size-capped and drops poison rows; TARGET_FPS/QUEUE_DB validated. - Zone coordinates normalized 0..1 on both sides (were canvas-px vs camera-px — every zone misplaced); pinch zones emit PINCH/HIGH (were downgraded to WARNING). - Fall detection uses bbox cues when keypoints are low-confidence (occluded fallen person); ghost incident not cancelled by a bystander; displacement normalized per-second (FPS-invariant). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Xmn8KZWqiQUguEMRtn6rW
95 lines
4.3 KiB
Bash
Executable file
95 lines
4.3 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Build a fully self-contained, offline-installable bundle for the move-detect
|
|
# edge daemon (air-gapped / 폐쇄망 target — see rules/common/build-deployment.md).
|
|
#
|
|
# Run this on a BUILD machine that HAS internet. The resulting tarball carries
|
|
# every runtime wheel + the pose model + the edge source + the systemd unit, so
|
|
# the air-gapped Pi installs with ZERO network fetches (see install_offline.sh).
|
|
#
|
|
# RELIABILITY NOTE: onnxruntime and opencv publish per-architecture wheels. The
|
|
# most reliable build is a NATIVE one — run this ON an aarch64 host (a Pi 5, or
|
|
# an arm64 CI runner) so pip downloads wheels for the exact target. Cross-downloading
|
|
# aarch64 wheels from an x86 box (--cross) is best-effort and can miss a wheel whose
|
|
# tags don't match; if it fails, build natively.
|
|
#
|
|
# Usage:
|
|
# deploy/bundle/build_offline_bundle.sh # native (host arch)
|
|
# deploy/bundle/build_offline_bundle.sh --cross # cross to aarch64/cp311
|
|
# TARGET_ARCH=aarch64 PYVER=3.11 deploy/bundle/build_offline_bundle.sh --cross
|
|
set -euo pipefail
|
|
|
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
OUT_DIR="${OUT_DIR:-$REPO_ROOT/dist}"
|
|
STAGE="$OUT_DIR/move-detect-edge-bundle"
|
|
WHEELHOUSE="$STAGE/wheelhouse"
|
|
PYVER="${PYVER:-3.11}" # Pi OS Bookworm ships CPython 3.11
|
|
PYABI="cp${PYVER//./}" # cp311
|
|
TARGET_ARCH="${TARGET_ARCH:-aarch64}"
|
|
RUNTIME_PKGS=(numpy requests onnxruntime opencv-python-headless)
|
|
|
|
MODE="native"
|
|
[ "${1:-}" = "--cross" ] && MODE="cross"
|
|
|
|
echo ">> build mode : $MODE"
|
|
echo ">> python target : $PYVER ($PYABI)"
|
|
echo ">> target arch : $TARGET_ARCH"
|
|
echo ">> output : $OUT_DIR"
|
|
|
|
rm -rf "$STAGE"
|
|
mkdir -p "$WHEELHOUSE" "$STAGE/src" "$STAGE/models" "$STAGE/deploy/systemd" "$STAGE/docs"
|
|
|
|
echo ">> [1/5] downloading runtime wheels -> wheelhouse/"
|
|
if [ "$MODE" = "cross" ]; then
|
|
# Best-effort cross download. Try the common manylinux aarch64 tags.
|
|
python3 -m pip download \
|
|
--only-binary=:all: \
|
|
--python-version "$PYVER" --implementation cp --abi "$PYABI" \
|
|
--platform "manylinux2014_${TARGET_ARCH}" \
|
|
--platform "manylinux_2_28_${TARGET_ARCH}" \
|
|
-d "$WHEELHOUSE" "${RUNTIME_PKGS[@]}"
|
|
else
|
|
# Native: wheels for THIS host's interpreter/arch (run on the Pi / arm64 CI).
|
|
# Guard the ABI: numpy/onnxruntime ship version-specific cpXY wheels, so a build
|
|
# host whose python3 != target PYVER produces a wheelhouse the Pi's cp311 venv
|
|
# cannot install offline. Fail loudly instead of shipping a broken bundle.
|
|
HOST_PYVER="$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')"
|
|
if [ "$HOST_PYVER" != "$PYVER" ]; then
|
|
echo "ERROR: native build host python3 is $HOST_PYVER but target PYVER is $PYVER." >&2
|
|
echo " Run with python$PYVER (e.g. 'PYVER=$HOST_PYVER ...' only if the Pi runs $HOST_PYVER)," >&2
|
|
echo " or use --cross to pin aarch64/$PYABI wheels explicitly." >&2
|
|
exit 1
|
|
fi
|
|
python3 -m pip download -d "$WHEELHOUSE" "${RUNTIME_PKGS[@]}"
|
|
fi
|
|
|
|
echo ">> [2/5] fetching MoveNet ONNX model (build-machine only)"
|
|
( cd "$REPO_ROOT" && python3 -m edge.tools.fetch_movenet )
|
|
cp "$REPO_ROOT/models/movenet_lightning.onnx" "$STAGE/models/"
|
|
|
|
echo ">> [3/5] staging edge source (package + pyproject)"
|
|
cp -r "$REPO_ROOT/edge" "$STAGE/src/edge"
|
|
cp "$REPO_ROOT/pyproject.toml" "$STAGE/src/"
|
|
# do not ship compiled caches
|
|
find "$STAGE/src" -name '__pycache__' -type d -prune -exec rm -rf {} +
|
|
|
|
echo ">> [4/5] staging deploy assets + install script"
|
|
cp "$REPO_ROOT/deploy/systemd/move-detect-edge.service" "$STAGE/deploy/systemd/"
|
|
cp "$REPO_ROOT/deploy/systemd/move-detect-edge.env.example" "$STAGE/deploy/systemd/"
|
|
cp "$REPO_ROOT/deploy/bundle/install_offline.sh" "$STAGE/"
|
|
cp "$REPO_ROOT/docs/pilot-runbook.md" "$STAGE/docs/" 2>/dev/null || true
|
|
chmod +x "$STAGE/install_offline.sh"
|
|
|
|
{
|
|
echo "bundle: move-detect-edge"
|
|
echo "built_mode: $MODE"
|
|
echo "python: $PYVER ($PYABI)"
|
|
echo "target_arch: $TARGET_ARCH"
|
|
echo "wheels:"
|
|
ls -1 "$WHEELHOUSE"
|
|
} > "$STAGE/MANIFEST.txt"
|
|
|
|
echo ">> [5/5] packaging tarball"
|
|
TARBALL="$OUT_DIR/move-detect-edge-bundle-${TARGET_ARCH}-${PYABI}.tar.gz"
|
|
tar -C "$OUT_DIR" -czf "$TARBALL" "move-detect-edge-bundle"
|
|
echo ">> DONE: $TARBALL"
|
|
echo ">> transfer this single file across the air gap, then run install_offline.sh on the Pi."
|