#!/usr/bin/env bash # update.sh — One-click update for HOT-Step-CPP source builders (Linux/macOS). # # Pulls latest code, verifies integration hooks, rebuilds everything # incrementally. Reuses existing build infrastructure patterns. # # Usage: # ./update.sh Incremental update (safe, default) # ./update.sh --force Reset local changes before pulling (with confirmation) # ./update.sh --clean Force clean engine rebuild # ./update.sh --skip-engine Skip engine rebuild (UI/server changes only) # ./update.sh --help Show this help # # For portable release users: you don't need this script. # Download new releases from GitHub instead. set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" cd "$SCRIPT_DIR" # ── Colors ──────────────────────────────────────────────────────────── RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' CYAN='\033[0;36m' NC='\033[0m' # No Color # ── Parse arguments ────────────────────────────────────────────────── FORCE=0 CLEAN=0 SKIP_ENGINE=0 show_help() { cat </dev/null; then # cmake is only required if we're building the engine if [ "$cmd" = "cmake" ] && [ "$SKIP_ENGINE" = "1" ]; then continue fi echo -e " ${RED}[FAIL] $cmd not found in PATH${NC}" PREREQ_OK=0 fi done if [ "$PREREQ_OK" = "0" ]; then echo "" echo " Fix the above issues and try again." exit 1 fi echo " All prerequisites found." # ── Phase 1: Pre-flight safety ────────────────────────────────────── echo "" echo -e "${CYAN}[2/5] Pre-flight checks...${NC}" # Save current HEAD for changelog later OLD_HEAD=$(git rev-parse HEAD 2>/dev/null || echo "unknown") # Check for uncommitted changes if ! git diff --quiet 2>/dev/null || ! git diff --cached --quiet 2>/dev/null; then if [ "$FORCE" = "1" ]; then echo "" echo -e " ${YELLOW}WARNING: You have uncommitted changes. --force will DISCARD them.${NC}" echo "" echo " Modified files:" git status --short echo "" read -p " Discard all local changes and continue? [y/N] " confirm if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then echo " Aborted by user." exit 1 fi echo " Resetting working tree..." git reset --hard git clean -fd else echo "" echo -e " ${RED}ERROR: You have uncommitted changes:${NC}" echo "" git status --short echo "" echo " Options:" echo " 1. Commit or stash your changes first" echo " 2. Run: ./update.sh --force (discards ALL local changes)" echo "" exit 1 fi else echo " Working tree is clean." fi # Shut down running server (if any) echo " Checking for running server..." STATUS_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3001/api/status 2>/dev/null || echo "000") if [ "$STATUS_CODE" = "200" ]; then echo " Server is running — requesting graceful shutdown..." curl -s -X POST http://localhost:3001/api/shutdown >/dev/null 2>&1 || true # Wait for ace-server to exit retries=0 while pgrep -x "ace-server" >/dev/null 2>&1; do sleep 1 retries=$((retries + 1)) if [ "$retries" -ge 10 ]; then echo " Force-killing ace-server after 10s timeout..." pkill -9 -x "ace-server" 2>/dev/null || true sleep 2 break fi done echo " Server stopped." else echo " No running server detected." fi # ── Phase 2: Code sync ────────────────────────────────────────────── echo "" echo -e "${CYAN}[3/5] Pulling latest code...${NC}" if ! git pull --ff-only origin master; then echo "" echo -e " ${RED}ERROR: git pull --ff-only failed.${NC}" echo " This usually means your local branch has diverged from origin/master." echo " Options:" echo " 1. Run: git rebase origin/master" echo " 2. Run: ./update.sh --force (discards local changes)" echo "" exit 1 fi git submodule update --init --recursive || echo " WARNING: Submodule update had issues." # Show what changed NEW_HEAD=$(git rev-parse HEAD 2>/dev/null || echo "unknown") if [ "$OLD_HEAD" != "$NEW_HEAD" ]; then echo "" echo " Changes pulled:" git log --oneline "$OLD_HEAD".."$NEW_HEAD" echo "" else echo " Already up to date." fi # ── Phase 3: Hook verification ────────────────────────────────────── echo "" echo -e "${CYAN}[4/5] Verifying integration hooks...${NC}" if [ -f "engine/verify-hooks.ps1" ]; then # On Linux we can't run PowerShell directly — do a simpler grep-based check HOOK_ERRORS=0 if [ -f "engine/src/pipeline-synth-ops.cpp" ]; then if grep -q '#include.*"hot-step-sampler\.h"' engine/src/pipeline-synth-ops.cpp; then echo -e " ${GREEN}[OK]${NC} pipeline-synth-ops.cpp -> hot-step-sampler.h" else echo -e " ${RED}[FAIL]${NC} pipeline-synth-ops.cpp missing hot-step-sampler.h" HOOK_ERRORS=$((HOOK_ERRORS + 1)) fi fi if [ -f "engine/src/model-store.h" ]; then if grep -q '#include.*"hot-step-params\.h"' engine/src/model-store.h; then echo -e " ${GREEN}[OK]${NC} model-store.h -> hot-step-params.h" else echo -e " ${RED}[FAIL]${NC} model-store.h missing hot-step-params.h" HOOK_ERRORS=$((HOOK_ERRORS + 1)) fi fi if [ -f "engine/src/dit.h" ]; then if grep -q '#include.*"adapter-merge\.h"' engine/src/dit.h; then echo -e " ${GREEN}[OK]${NC} dit.h -> adapter-merge.h" else echo -e " ${RED}[FAIL]${NC} dit.h missing adapter-merge.h" HOOK_ERRORS=$((HOOK_ERRORS + 1)) fi if grep -q '#include.*"adapter-runtime\.h"' engine/src/dit.h; then echo -e " ${GREEN}[OK]${NC} dit.h -> adapter-runtime.h" else echo -e " ${RED}[FAIL]${NC} dit.h missing adapter-runtime.h" HOOK_ERRORS=$((HOOK_ERRORS + 1)) fi fi if [ "$HOOK_ERRORS" -gt 0 ]; then echo "" echo -e " ${RED}FATAL: $HOOK_ERRORS integration hook(s) broken after pull!${NC}" echo " Run the upstream-sync workflow to repair them." exit 1 fi else echo " verify-hooks.ps1 not found — skipping hook check." fi # ── Phase 4: Build ─────────────────────────────────────────────────── echo "" echo -e "${CYAN}[5/5] Building...${NC}" # --- Server dependencies --- echo " Installing server dependencies..." (cd server && npm install --no-audit --no-fund 2>/dev/null) || echo " WARNING: Server npm install had issues." (cd server && npm rebuild better-sqlite3 2>/dev/null) || true # --- UI dependencies --- echo " Installing UI dependencies..." (cd ui && npm install --no-audit --no-fund 2>/dev/null) || echo " WARNING: UI npm install had issues." # --- Engine build --- if [ "$SKIP_ENGINE" = "1" ]; then echo " Skipping engine build (--skip-engine flag)." else # Clean build requested? if [ "$CLEAN" = "1" ]; then echo "" echo -e " ${YELLOW}WARNING: --clean flag set. Full engine rebuild.${NC}" read -p " Continue with clean rebuild? [y/N] " confirm if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then echo " Clean rebuild skipped." else echo " Cleaning engine build directory..." rm -rf engine/build fi fi echo " Building engine..." # Auto-detect GPU backend CMAKE_EXTRA="" DETECTED="" if command -v nvcc &>/dev/null; then CMAKE_EXTRA="$CMAKE_EXTRA -DGGML_CUDA=ON" DETECTED="CUDA" echo " CUDA toolchain: $(nvcc --version 2>/dev/null | grep release || echo 'detected')" fi if [ -n "$VULKAN_SDK" ]; then CMAKE_EXTRA="$CMAKE_EXTRA -DGGML_VULKAN=ON" if [ -n "$DETECTED" ]; then DETECTED="$DETECTED + Vulkan" else DETECTED="Vulkan" fi fi if [ -z "$DETECTED" ]; then DETECTED="CPU-only" echo " No GPU SDK detected — building CPU-only backend." else echo " Detected backends: $DETECTED" fi # Build engine mkdir -p engine/build cd engine/build if [ ! -f "CMakeCache.txt" ] || [ "$CLEAN" = "1" ]; then cmake .. $CMAKE_EXTRA -DGGML_CPU_ALL_VARIANTS=ON -DGGML_BACKEND_DL=ON fi cmake --build . --config Release -j "$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)" cd "$SCRIPT_DIR" if [ $? -ne 0 ]; then echo "" echo -e " ${RED}ERROR: Engine build failed!${NC}" echo " Common fixes:" echo " - Install build-essential (gcc/g++)" echo " - Ensure CUDA Toolkit is in PATH (if using CUDA)" echo " - Try: ./update.sh --clean" exit 1 fi echo " Engine build complete." fi # --- UI build --- echo " Building UI..." (cd ui && npx vite build) || echo -e " ${YELLOW}WARNING: UI build had issues.${NC}" echo " UI build complete." # ── Phase 5: Validation & Report ───────────────────────────────────── echo "" echo "=======================================================" # Verify engine binary exists ENGINE_OK=0 if [ -f "engine/build/Release/ace-server" ] || [ -f "engine/build/ace-server" ]; then ENGINE_OK=1 fi UI_OK=0 if [ -f "ui/dist/index.html" ]; then UI_OK=1 fi if [ "$ENGINE_OK" = "1" ]; then echo -e " ${GREEN}[OK]${NC} Engine binary found" elif [ "$SKIP_ENGINE" = "1" ]; then echo " [--] Engine build skipped" else echo -e " ${RED}[!!]${NC} Engine binary NOT found — build may have failed" fi if [ "$UI_OK" = "1" ]; then echo -e " ${GREEN}[OK]${NC} UI dist/ built" else echo -e " ${RED}[!!]${NC} UI dist/ not found — build may have failed" fi echo "" if [ "$OLD_HEAD" != "$NEW_HEAD" ]; then echo " Updated: ${OLD_HEAD:0:8} -> ${NEW_HEAD:0:8}" else echo " No new commits (rebuild only)." fi echo "" echo " Run ./launch.sh to start the application." echo "=======================================================" echo ""