cmvr_edge_ai/scripts/bootstrap.sh

234 lines
6.9 KiB
Bash
Raw Normal View History

2026-07-20 16:59:37 +08:00
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)"
profile="detection-cpu"
cmvr_es_root="${PROJECT_ROOT}/../cmvr-es"
skip_codegen=false
skip_check=false
2026-07-21 12:07:12 +08:00
verify_model_artifact() {
local model_id="$1"
local relative_path="$2"
local expected_size="$3"
local expected_sha256="$4"
local artifact_path="${PROJECT_ROOT}/${relative_path}"
if [[ ! -f "${artifact_path}" ]]; then
echo "error: model artifact is missing for ${model_id}: ${relative_path}" >&2
echo "restore the model file before using the ${profile} profile" >&2
exit 1
fi
if LC_ALL=C grep -q -F "version https://git-lfs.github.com/spec/v1" "${artifact_path}"; then
2026-07-21 16:23:33 +08:00
echo "error: model artifact is an unexpected Git LFS pointer: ${relative_path}" >&2
echo "recommit the complete weight as a regular Git file" >&2
2026-07-21 12:07:12 +08:00
exit 1
fi
local actual_size
actual_size="$(wc -c < "${artifact_path}")"
actual_size="${actual_size//[[:space:]]/}"
if [[ "${actual_size}" != "${expected_size}" ]]; then
echo "error: model artifact size mismatch for ${model_id}: ${relative_path}" >&2
echo "expected ${expected_size} bytes, got ${actual_size} bytes" >&2
exit 1
fi
local actual_sha256
actual_sha256="$(sha256sum "${artifact_path}" | awk '{print $1}')"
if [[ "${actual_sha256}" != "${expected_sha256}" ]]; then
echo "error: model artifact checksum mismatch for ${model_id}: ${relative_path}" >&2
echo "expected SHA256 ${expected_sha256}, got ${actual_sha256}" >&2
exit 1
fi
}
2026-07-20 16:59:37 +08:00
usage() {
cat <<'EOF'
Usage: bash scripts/bootstrap.sh [options]
Create the locked uv environment, optionally generate cmvr-es Python bindings,
and run basic health checks.
Options:
--profile PROFILE core, detection-cpu (default), or dev
--cmvr-es-root PATH cmvr-es checkout (default: sibling ../cmvr-es)
--skip-codegen do not generate cmvr-es protobuf/gRPC bindings
--skip-check install only; skip validation and tests
-h, --help show this help
Profiles:
core framework and minimal fixture only
detection-cpu gRPC + HTTP server/client + PyAV + locked CPU ONNX runtime
dev detection-cpu plus locked ONNX export and test toolchains
2026-07-20 16:59:37 +08:00
EOF
}
while (($#)); do
case "$1" in
--profile)
if (($# < 2)); then
echo "error: --profile requires a value" >&2
exit 2
fi
profile="$2"
shift 2
;;
--cmvr-es-root)
if (($# < 2)); then
echo "error: --cmvr-es-root requires a path" >&2
exit 2
fi
cmvr_es_root="$2"
shift 2
;;
--skip-codegen)
skip_codegen=true
shift
;;
--skip-check)
skip_check=true
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "error: unknown argument: $1" >&2
usage >&2
exit 2
;;
esac
done
case "${profile}" in
core)
sync_args=(--locked --no-default-groups)
needs_cmvr_bindings=false
;;
detection-cpu)
sync_args=(
--locked
--no-default-groups
--extra grpc
--extra http
--extra video
--extra image
--extra server
--extra onnx-cpu
2026-07-20 16:59:37 +08:00
)
needs_cmvr_bindings=true
;;
dev)
sync_args=(
--locked
--no-default-groups
--group dev
--extra grpc
--extra http
--extra video
--extra image
--extra server
--extra onnx-export-cpu
2026-07-20 16:59:37 +08:00
)
needs_cmvr_bindings=true
;;
*)
echo "error: unsupported profile '${profile}'" >&2
echo "expected one of: core, detection-cpu, dev" >&2
exit 2
;;
esac
if ! command -v uv >/dev/null 2>&1; then
echo "error: uv is not installed or not available on PATH" >&2
echo "see https://docs.astral.sh/uv/getting-started/installation/" >&2
exit 1
fi
cd "${PROJECT_ROOT}"
echo "==> project: ${PROJECT_ROOT}"
echo "==> profile: ${profile}"
if [[ "${needs_cmvr_bindings}" == true && "${skip_codegen}" == false ]]; then
if [[ ! -d "${cmvr_es_root}/protos/cmvr" ]]; then
echo "error: cmvr-es protos were not found under: ${cmvr_es_root}" >&2
echo "pass --cmvr-es-root PATH or use --skip-codegen with existing bindings" >&2
exit 1
fi
echo "==> installing the locked protobuf codegen environment"
uv sync --locked --only-group codegen
echo "==> generating cmvr-es Python bindings from ${cmvr_es_root}"
.venv/bin/python scripts/generate_cmvr_stubs.py \
--cmvr-es-root "${cmvr_es_root}" \
--portable
fi
echo "==> installing the locked ${profile} environment"
uv sync "${sync_args[@]}"
if [[ "${skip_check}" == true ]]; then
echo "==> installation complete (checks skipped)"
exit 0
fi
2026-07-21 12:07:12 +08:00
echo "==> validating the minimal framework fixture"
.venv/bin/cmvr-edge-ai validate \
--config tests/fixtures/minimal_pipeline.yaml \
--pipeline minimal
2026-07-20 16:59:37 +08:00
if [[ "${needs_cmvr_bindings}" == true ]]; then
2026-07-21 12:07:12 +08:00
echo "==> verifying detection model artifacts"
verify_model_artifact \
"construction-ppe-yolov8@2" \
"models/detection/construction-ppe-yolov8/v2/model.onnx" \
"44774179" \
"b8e5d116a964d0e7091e14b5382ab9301c196ae33b363516df04fddfb0d0b57a"
2026-07-21 12:07:12 +08:00
verify_model_artifact \
"ppe-6classes-yolov8n@2" \
"models/detection/ppe-6classes-yolov8n/v2/model.onnx" \
"10993196" \
"6cb0e567b4fbf353aeb61c271ab33c45260d6ddddbe26eb3b64c245c2683c03b"
2026-07-21 16:23:33 +08:00
verify_model_artifact \
"people-talking-yolov8x@2" \
"models/detection/people-talking-yolov8x/v2/model.onnx" \
"272787930" \
"b4387307dbdf1083c0229a12e833ccdc4a3ad0063d5bdb7908765072470e840b"
2026-07-21 16:23:33 +08:00
verify_model_artifact \
"yolov8n-mobile-phone@2" \
"models/detection/yolov8n-mobile-phone/v2/model.onnx" \
"12265264" \
"df170d9bc86984894797da6f2101519617669c3f589d75289acb0ba1937f857f"
2026-07-21 12:07:12 +08:00
2026-07-20 16:59:37 +08:00
echo "==> checking detection runtime imports"
.venv/bin/python -c \
"import av, fastapi, grpc, httpx, numpy, onnxruntime, PIL, uvicorn; import cmvr.api.camera_service_pb2_grpc; print(f'onnxruntime={onnxruntime.__version__} numpy={numpy.__version__} pillow={PIL.__version__} fastapi={fastapi.__version__} uvicorn={uvicorn.__version__}')"
2026-07-21 16:23:33 +08:00
echo "==> validating the PPE and phone-use detection pipeline"
2026-07-20 16:59:37 +08:00
.venv/bin/cmvr-edge-ai validate \
--config configs/active_detection.yaml \
2026-07-20 16:59:37 +08:00
--pipeline detection
echo "==> validating the passive Detect Server pipelines"
.venv/bin/cmvr-edge-ai validate \
--config configs/server_detect.yaml
.venv/bin/python server/detect/show_detections.py \
--config configs/detection_viewer.yaml \
--validate-only
2026-07-20 16:59:37 +08:00
.venv/bin/cmvr-edge-ai models
fi
if [[ "${profile}" == dev ]]; then
echo "==> checking ONNX exporter imports"
.venv/bin/python -c \
"import onnx, onnxslim, torch, ultralytics; print(f'onnx={onnx.__version__} onnxslim={onnxslim.__version__} torch={torch.__version__} ultralytics={ultralytics.__version__}')"
2026-07-20 16:59:37 +08:00
echo "==> running tests"
.venv/bin/pytest
fi
echo "==> bootstrap complete"
echo "run commands with: uv run --no-sync cmvr-edge-ai ..."