#!/usr/bin/env bash # Build, test, and relaunch camera-qrng locally. # # 1. Kills any running camera-qrng process # 2. Runs cargo check and cargo test # 3. Builds release for native + all installed cross targets # 4. Verifies each built binary # 5. Relaunches the native binary in the background # # Usage: # ./scripts/build-test.sh # full build-test cycle # ./scripts/build-test.sh --no-launch # build + test only, don't relaunch # ./scripts/build-test.sh --quick # native only, skip cross targets set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" LOG_FILE="$PROJECT_DIR/camera-qrng.log" PID_FILE="$PROJECT_DIR/.camera-qrng.pid" BINARY_NAME="camera-qrng" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' CYAN='\033[0;36m' NC='\033[0m' info() { echo -e "${GREEN}[INFO]${NC} $1"; } warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } err() { echo -e "${RED}[ERR]${NC} $1"; } step() { echo -e "${CYAN}[STEP]${NC} $1"; } # --- Parse flags --- NO_LAUNCH=false QUICK=false for arg in "$@"; do case "$arg" in --no-launch) NO_LAUNCH=true ;; --quick) QUICK=true ;; -h|--help) echo "Usage: $0 [--no-launch] [--quick]" echo " --no-launch Build and test only, don't relaunch the service" echo " --quick Native build only, skip cross-compilation targets" exit 0 ;; *) err "Unknown flag: $arg"; exit 1 ;; esac done cd "$PROJECT_DIR" # ================================================== # 1. Kill previous instance # ================================================== step "Killing any running $BINARY_NAME..." killed=false if [[ -f "$PID_FILE" ]]; then old_pid=$(cat "$PID_FILE") if kill -0 "$old_pid" 2>/dev/null; then kill "$old_pid" 2>/dev/null && killed=true # Wait up to 5s for graceful shutdown for i in $(seq 1 10); do kill -0 "$old_pid" 2>/dev/null || break sleep 0.5 done # Force kill if still alive if kill -0 "$old_pid" 2>/dev/null; then kill -9 "$old_pid" 2>/dev/null sleep 0.5 fi fi rm -f "$PID_FILE" fi # Also kill any stray processes not tracked by PID file pgrep -f "target/release/$BINARY_NAME" 2>/dev/null | while read -r pid; do if [[ "$pid" != "$$" ]]; then kill "$pid" 2>/dev/null && killed=true fi done sleep 0.5 if [[ "$killed" == true ]]; then info "Previous instance stopped" else info "No previous instance running" fi # ================================================== # 2. Check and test # ================================================== step "Running cargo check..." cargo check 2>&1 | tail -3 info "Check passed" step "Running cargo test..." cargo test 2>&1 | tail -5 info "Tests passed" # ================================================== # 3. Build targets # ================================================== # Detect native target triple ARCH=$(uname -m) OS=$(uname -s) case "${ARCH}-${OS}" in arm64-Darwin) NATIVE_TARGET="aarch64-apple-darwin" ;; x86_64-Darwin) NATIVE_TARGET="x86_64-apple-darwin" ;; aarch64-Linux) NATIVE_TARGET="aarch64-unknown-linux-gnu" ;; x86_64-Linux) NATIVE_TARGET="x86_64-unknown-linux-gnu" ;; *) NATIVE_TARGET="native" ;; esac # Build targets: always native, optionally cross CROSS_TARGETS=() if [[ "$QUICK" == false ]]; then case "$OS" in Darwin) # Build both macOS architectures if [[ "$ARCH" == "arm64" ]]; then CROSS_TARGETS+=("x86_64-apple-darwin") else CROSS_TARGETS+=("aarch64-apple-darwin") fi # Linux targets if cross is installed if command -v cross &>/dev/null; then CROSS_TARGETS+=("aarch64-unknown-linux-gnu" "x86_64-unknown-linux-gnu") else warn "Skipping Linux targets (install 'cross': cargo install cross)" fi ;; Linux) if [[ "$ARCH" == "aarch64" ]]; then CROSS_TARGETS+=("x86_64-unknown-linux-gnu") else CROSS_TARGETS+=("aarch64-unknown-linux-gnu") fi ;; esac fi success=0 failed=0 # --- Native build --- step "Building native release ($NATIVE_TARGET)..." cargo build --release 2>&1 | tail -3 NATIVE_BIN="target/release/$BINARY_NAME" if [[ -f "$NATIVE_BIN" ]]; then info "Native: $(file "$NATIVE_BIN" | sed "s|$PROJECT_DIR/||")" success=$((success + 1)) else err "Native binary not found at $NATIVE_BIN" failed=$((failed + 1)) fi # --- Cross targets --- for target in "${CROSS_TARGETS[@]}"; do step "Building $target..." use_cross=false if [[ "$OS" == "Darwin" ]] && [[ "$target" == *"linux"* ]]; then use_cross=true fi if [[ "$use_cross" == true ]]; then if cross build --release --target "$target" 2>&1 | tail -3; then bin="target/$target/release/$BINARY_NAME" if [[ -f "$bin" ]]; then info "$target: $(file "$bin" | sed "s|$PROJECT_DIR/||")" success=$((success + 1)) else err "$target binary not found" failed=$((failed + 1)) fi else err "$target build failed" failed=$((failed + 1)) fi else if cargo build --release --target "$target" 2>&1 | tail -3; then bin="target/$target/release/$BINARY_NAME" if [[ -f "$bin" ]]; then info "$target: $(file "$bin" | sed "s|$PROJECT_DIR/||")" success=$((success + 1)) else err "$target binary not found" failed=$((failed + 1)) fi else err "$target build failed" failed=$((failed + 1)) fi fi done echo "" info "Build results: $success succeeded, $failed failed" if [[ $failed -gt 0 ]]; then err "Some builds failed - check output above" if [[ "$NO_LAUNCH" == true ]]; then exit 1 fi warn "Continuing to launch native binary despite cross-build failures..." fi # ================================================== # 4. Verify native binary runs # ================================================== step "Verifying native binary..." if [[ -x "$NATIVE_BIN" ]]; then info "Binary OK: $(ls -lh "$NATIVE_BIN" | awk '{print $5}') $(file "$NATIVE_BIN" | grep -o 'Mach-O\|ELF\|PE32' || echo 'binary')" else err "Native binary not executable" exit 1 fi # ================================================== # 5. Relaunch # ================================================== if [[ "$NO_LAUNCH" == true ]]; then info "Skipping launch (--no-launch)" exit 0 fi step "Launching $BINARY_NAME in background..." nohup "$NATIVE_BIN" > "$LOG_FILE" 2>&1 & NEW_PID=$! echo "$NEW_PID" > "$PID_FILE" # Give it a moment to start sleep 2 if kill -0 "$NEW_PID" 2>/dev/null; then info "$BINARY_NAME running (PID $NEW_PID)" info "Log: $LOG_FILE" info "PID file: $PID_FILE" echo "" head -10 "$LOG_FILE" 2>/dev/null | while IFS= read -r line; do echo " $line" done echo "" info "Health check: http://localhost:${PORT:-8787}/health" else err "$BINARY_NAME failed to start. Log output:" cat "$LOG_FILE" 2>/dev/null rm -f "$PID_FILE" exit 1 fi