From db9e2069716266ec806e4cabebd9dc826197a609 Mon Sep 17 00:00:00 2001 From: pyr0ball Date: Fri, 8 May 2026 16:58:12 -0700 Subject: [PATCH] chore: standardize manage.sh, remove start_dev.sh - manage.sh: start/stop/restart/status/logs/open/dev/ingest/build-fts/test following avocet pattern (PID files, colored output, native processes) - start mode: builds Vue SPA, uvicorn on :8534, python http.server on :8535 - dev mode: uvicorn --reload + Vite HMR, trap cleanup on exit - scripts/start_dev.sh: removed (superseded by manage.sh dev) --- manage.sh | 237 +++++++++++++++++++++++++++++++++++++++++++ scripts/start_dev.sh | 26 ----- 2 files changed, 237 insertions(+), 26 deletions(-) create mode 100755 manage.sh delete mode 100755 scripts/start_dev.sh diff --git a/manage.sh b/manage.sh new file mode 100755 index 0000000..578dadc --- /dev/null +++ b/manage.sh @@ -0,0 +1,237 @@ +#!/usr/bin/env bash +# manage.sh — Turnstone diagnostic intelligence layer +# Usage: ./manage.sh [args] +set -euo pipefail + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m' +info() { echo -e "${BLUE}[turnstone]${NC} $*"; } +success() { echo -e "${GREEN}[turnstone]${NC} $*"; } +warn() { echo -e "${YELLOW}[turnstone]${NC} $*"; } +error() { echo -e "${RED}[turnstone]${NC} $*" >&2; exit 1; } + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +API_PORT=8534 +UI_PORT=8535 +VITE_PORT=5174 # local HMR port in dev mode (proxies /api → 8534) + +LOG_DIR="log" +API_PID_FILE=".turnstone-api.pid" +UI_PID_FILE=".turnstone-ui.pid" + +DB="${TURNSTONE_DB:-${SCRIPT_DIR}/data/turnstone.db}" + +CONDA_BASE="${CONDA_BASE:-/devl/miniconda3}" +PYTHON="${CONDA_BASE}/envs/cf/bin/python" + +# ── Helpers ─────────────────────────────────────────────────────────────────── + +_is_alive() { + local pid_file="$1" + [[ -f "$pid_file" ]] && kill -0 "$(<"$pid_file")" 2>/dev/null +} + +_kill_pid_file() { + local pid_file="$1" label="$2" + if [[ -f "$pid_file" ]]; then + local pid + pid=$(<"$pid_file") + if kill -0 "$pid" 2>/dev/null; then + kill "$pid" && rm -f "$pid_file" + success "$label stopped (PID $pid)." + else + warn "Stale PID file for $label (PID $pid not running). Cleaning up." + rm -f "$pid_file" + fi + else + warn "$label not running." + fi +} + +_wait_for_port() { + local port="$1" label="$2" pid_file="$3" + for _i in $(seq 1 20); do + sleep 0.5 + (echo "" >/dev/tcp/127.0.0.1/"$port") 2>/dev/null && return 0 + if ! _is_alive "$pid_file"; then + rm -f "$pid_file" + error "$label died during startup. Check ${LOG_DIR}/api.log" + fi + done + error "$label did not bind to port $port within 10 s." +} + +# ── Usage ───────────────────────────────────────────────────────────────────── + +usage() { + echo "" + echo -e " ${BLUE}Turnstone — Diagnostic Log Intelligence${NC}" + echo "" + echo " Usage: ./manage.sh [args]" + echo "" + echo " Production-like (built SPA + uvicorn):" + echo -e " ${GREEN}start${NC} Build Vue SPA, start API (:${API_PORT}) + static UI (:${UI_PORT})" + echo -e " ${GREEN}stop${NC} Stop API and UI servers" + echo -e " ${GREEN}restart${NC} Stop then start" + echo -e " ${GREEN}status${NC} Show running processes" + echo -e " ${GREEN}logs [api|ui]${NC} Tail log files (default: api)" + echo -e " ${GREEN}open${NC} Open UI in browser" + echo "" + echo " Development (hot-reload):" + echo -e " ${GREEN}dev${NC} uvicorn --reload (:${API_PORT}) + Vite HMR (:${VITE_PORT})" + echo "" + echo " Data:" + echo -e " ${GREEN}ingest CORPUS_DIR [DB]${NC} Ingest a corpus directory into the database" + echo -e " ${GREEN}build-fts${NC} Rebuild the FTS search index" + echo "" + echo " Tests:" + echo -e " ${GREEN}test [args]${NC} Run pytest suite" + echo "" + echo " DB: ${DB}" + echo " Conda env: cf" + echo "" + echo " Examples:" + echo " ./manage.sh start" + echo " ./manage.sh dev" + echo " ./manage.sh ingest corpus/raw/" + echo " ./manage.sh ingest corpus/raw/ data/custom.db" + echo "" +} + +# ── Commands ────────────────────────────────────────────────────────────────── + +CMD="${1:-help}" +shift || true + +case "$CMD" in + + start) + if _is_alive "$API_PID_FILE"; then + warn "API already running (PID $(<"$API_PID_FILE")) — use 'restart' to rebuild." + exit 0 + fi + mkdir -p "$LOG_DIR" data + + info "Building Vue SPA…" + (cd web && npm run build) 2>&1 | tee "${LOG_DIR}/build.log" | grep -E "built in|error" || true + success "SPA built → web/dist/" + + info "Starting FastAPI on port ${API_PORT}…" + TURNSTONE_DB="$DB" nohup "$PYTHON" -m uvicorn app.rest:app \ + --host 0.0.0.0 --port "$API_PORT" \ + >> "${LOG_DIR}/api.log" 2>&1 & + echo $! > "$API_PID_FILE" + _wait_for_port "$API_PORT" "FastAPI" "$API_PID_FILE" + success "API → http://localhost:${API_PORT} (PID $(<"$API_PID_FILE"))" + + info "Starting static UI server on port ${UI_PORT}…" + nohup "$PYTHON" -m http.server "$UI_PORT" --directory web/dist \ + >> "${LOG_DIR}/ui.log" 2>&1 & + echo $! > "$UI_PID_FILE" + _wait_for_port "$UI_PORT" "UI server" "$UI_PID_FILE" + success "UI → http://localhost:${UI_PORT} (PID $(<"$UI_PID_FILE"))" + ;; + + stop) + _kill_pid_file "$API_PID_FILE" "FastAPI" + _kill_pid_file "$UI_PID_FILE" "UI server" + ;; + + restart) + bash "$0" stop + exec bash "$0" start + ;; + + status) + echo "" + if _is_alive "$API_PID_FILE"; then + success "FastAPI RUNNING PID $(<"$API_PID_FILE") → http://localhost:${API_PORT}" + else + echo -e " FastAPI ${RED}STOPPED${NC}" + fi + if _is_alive "$UI_PID_FILE"; then + success "UI server RUNNING PID $(<"$UI_PID_FILE") → http://localhost:${UI_PORT}" + else + echo -e " UI server ${RED}STOPPED${NC}" + fi + echo "" + ;; + + logs) + target="${1:-api}" + case "$target" in + api) tail -f "${LOG_DIR}/api.log" ;; + ui) tail -f "${LOG_DIR}/ui.log" ;; + *) error "Unknown log target: $target. Use 'api' or 'ui'." ;; + esac + ;; + + open) + URL="http://localhost:${UI_PORT}" + info "Opening ${URL}" + if command -v xdg-open &>/dev/null; then xdg-open "$URL" + elif command -v open &>/dev/null; then open "$URL" + else echo "$URL" + fi + ;; + + dev) + DEV_API_PID=".turnstone-dev-api.pid" + mkdir -p "$LOG_DIR" data + + if _is_alive "$DEV_API_PID"; then + warn "Dev API already running (PID $(<"$DEV_API_PID"))" + else + info "Starting uvicorn --reload on port ${API_PORT}…" + TURNSTONE_DB="$DB" nohup "$PYTHON" -m uvicorn app.rest:app \ + --host 0.0.0.0 --port "$API_PORT" --reload \ + >> "${LOG_DIR}/api.log" 2>&1 & + echo $! > "$DEV_API_PID" + _wait_for_port "$API_PORT" "FastAPI (dev)" "$DEV_API_PID" + success "API (hot-reload) → http://localhost:${API_PORT}" + fi + + _cleanup_dev() { + local pid + pid=$(<"$DEV_API_PID" 2>/dev/null) || true + [[ -n "${pid:-}" ]] && kill "$pid" 2>/dev/null && rm -f "$DEV_API_PID" + info "Dev servers stopped." + } + trap _cleanup_dev EXIT INT TERM + + info "Starting Vite HMR on port ${VITE_PORT}…" + success "Frontend (HMR) → http://localhost:${VITE_PORT}" + (cd web && npm run dev -- --port "$VITE_PORT") + ;; + + ingest) + if [[ $# -lt 1 ]]; then + error "Usage: ./manage.sh ingest CORPUS_DIR [DB_PATH]" + fi + info "Ingesting $1 → ${2:-$DB}…" + "$PYTHON" scripts/ingest_corpus.py "$1" "${2:-$DB}" + ;; + + build-fts) + info "Rebuilding FTS index for ${DB}…" + TURNSTONE_DB="$DB" "$PYTHON" scripts/build_fts_index.py "$DB" + success "FTS index rebuilt." + ;; + + test) + info "Running test suite…" + PYTEST="${CONDA_BASE}/envs/cf/bin/pytest" + [[ -x "$PYTEST" ]] || error "pytest not found in cf env at ${PYTEST}" + TURNSTONE_DB=":memory:" "$PYTEST" tests/ -v "$@" + ;; + + help|--help|-h) + usage + ;; + + *) + error "Unknown command: ${CMD}. Run './manage.sh help' for usage." + ;; + +esac diff --git a/scripts/start_dev.sh b/scripts/start_dev.sh deleted file mode 100755 index 89e12cc..0000000 --- a/scripts/start_dev.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash -# Start Turnstone dev instance: FastAPI on :8534, Vite on :8535 -set -euo pipefail - -REPO="$(cd "$(dirname "$0")/.." && pwd)" -DB="${TURNSTONE_DB:-$REPO/data/turnstone.db}" - -echo "==> Turnstone dev instance" -echo " DB: $DB" -echo " API: http://localhost:8534" -echo " UI: http://localhost:8535" -echo "" - -# FastAPI in background -TURNSTONE_DB="$DB" conda run --no-capture-output -n cf \ - uvicorn app.rest:app --host 0.0.0.0 --port 8534 --reload \ - --log-level info & -API_PID=$! -echo "==> FastAPI PID $API_PID" - -# Vite dev server in foreground (Ctrl-C stops both) -cleanup() { kill "$API_PID" 2>/dev/null || true; } -trap cleanup EXIT INT TERM - -cd "$REPO/web" -npm run dev