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)
This commit is contained in:
parent
eef84d55be
commit
1f5854e90b
2 changed files with 237 additions and 26 deletions
237
manage.sh
Executable file
237
manage.sh
Executable file
|
|
@ -0,0 +1,237 @@
|
|||
#!/usr/bin/env bash
|
||||
# manage.sh — Turnstone diagnostic intelligence layer
|
||||
# Usage: ./manage.sh <command> [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 <command> [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
|
||||
|
|
@ -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
|
||||
Loading…
Reference in a new issue