chore: add local dev workflow
This commit is contained in:
1 parent
ecf1e51915
commit
f917b71960
3 files changed
+296
No files matched your search
+250
@@ -0,0 +1,250 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
STATE_DIR="${VIBE_DEV_STATE_DIR:-$ROOT/.vibe-dev}"
|
||||
LOG_DIR="$STATE_DIR/logs"
|
||||
PID_DIR="$STATE_DIR/pids"
|
||||
|
||||
BACKEND_HOST="${VIBE_BACKEND_HOST:-127.0.0.1}"
|
||||
BACKEND_PORT="${VIBE_BACKEND_PORT:-8899}"
|
||||
FRONTEND_HOST="${VIBE_FRONTEND_HOST:-127.0.0.1}"
|
||||
FRONTEND_PORT="${VIBE_FRONTEND_PORT:-5899}"
|
||||
if [[ -n "${PYTHON:-}" ]]; then
|
||||
PYTHON_BIN="$PYTHON"
|
||||
elif [[ -x "$ROOT/.venv/bin/python" ]]; then
|
||||
PYTHON_BIN="$ROOT/.venv/bin/python"
|
||||
elif [[ -x "$ROOT/agent/.venv/bin/python" ]]; then
|
||||
PYTHON_BIN="$ROOT/agent/.venv/bin/python"
|
||||
else
|
||||
PYTHON_BIN="python3"
|
||||
fi
|
||||
|
||||
mkdir -p "$LOG_DIR" "$PID_DIR"
|
||||
|
||||
usage() {
|
||||
cat <<USAGE
|
||||
Usage: scripts/dev <command>
|
||||
|
||||
Commands:
|
||||
up Start backend and frontend dev servers
|
||||
stop Stop dev servers started by this script
|
||||
restart [service] Restart backend, frontend, or all services
|
||||
status Show process status and URLs
|
||||
logs [service] Tail logs for backend, frontend, or all
|
||||
open Open the frontend in the default browser
|
||||
urls Print local dev URLs
|
||||
|
||||
Environment:
|
||||
VIBE_BACKEND_PORT Backend port (default: 8899)
|
||||
VIBE_FRONTEND_PORT Frontend port (default: 5899)
|
||||
PYTHON Python binary (default: local .venv, then python3)
|
||||
USAGE
|
||||
}
|
||||
|
||||
pid_file() {
|
||||
printf "%s/%s.pid" "$PID_DIR" "$1"
|
||||
}
|
||||
|
||||
log_file() {
|
||||
printf "%s/%s.log" "$LOG_DIR" "$1"
|
||||
}
|
||||
|
||||
is_running() {
|
||||
local service="$1"
|
||||
local file
|
||||
file="$(pid_file "$service")"
|
||||
[[ -f "$file" ]] && kill -0 "$(cat "$file")" 2>/dev/null
|
||||
}
|
||||
|
||||
start_service() {
|
||||
local service="$1"
|
||||
shift
|
||||
if is_running "$service"; then
|
||||
printf "%s already running (pid %s)\n" "$service" "$(cat "$(pid_file "$service")")"
|
||||
return
|
||||
fi
|
||||
|
||||
printf "starting %s...\n" "$service"
|
||||
nohup "$@" >"$(log_file "$service")" 2>&1 </dev/null &
|
||||
echo "$!" >"$(pid_file "$service")"
|
||||
printf "%s pid %s, log %s\n" "$service" "$(cat "$(pid_file "$service")")" "$(log_file "$service")"
|
||||
}
|
||||
|
||||
stop_service() {
|
||||
local service="$1"
|
||||
local file
|
||||
file="$(pid_file "$service")"
|
||||
if ! [[ -f "$file" ]]; then
|
||||
printf "%s not started by scripts/dev\n" "$service"
|
||||
return
|
||||
fi
|
||||
|
||||
local pid
|
||||
pid="$(cat "$file")"
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
printf "stopping %s (pid %s)...\n" "$service" "$pid"
|
||||
pkill -TERM -P "$pid" 2>/dev/null || true
|
||||
kill "$pid" 2>/dev/null || true
|
||||
sleep 0.5
|
||||
pkill -KILL -P "$pid" 2>/dev/null || true
|
||||
kill -KILL "$pid" 2>/dev/null || true
|
||||
fi
|
||||
rm -f "$file"
|
||||
}
|
||||
|
||||
url_ok() {
|
||||
local url="$1"
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsS --max-time 2 "$url" >/dev/null 2>&1
|
||||
else
|
||||
"$PYTHON_BIN" - "$url" >/dev/null 2>&1 <<'PY'
|
||||
import sys
|
||||
import urllib.request
|
||||
|
||||
urllib.request.urlopen(sys.argv[1], timeout=2).read(1)
|
||||
PY
|
||||
fi
|
||||
}
|
||||
|
||||
wait_for_url() {
|
||||
local service="$1"
|
||||
local url="$2"
|
||||
local attempts="${3:-30}"
|
||||
|
||||
printf "waiting for %s at %s" "$service" "$url"
|
||||
for ((i = 1; i <= attempts; i++)); do
|
||||
if url_ok "$url"; then
|
||||
printf " ready\n"
|
||||
return 0
|
||||
fi
|
||||
if ! is_running "$service"; then
|
||||
printf "\n%s exited early; see %s\n" "$service" "$(log_file "$service")" >&2
|
||||
return 1
|
||||
fi
|
||||
printf "."
|
||||
sleep 1
|
||||
done
|
||||
printf "\n%s not ready after %ss; see %s\n" "$service" "$attempts" "$(log_file "$service")" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
cmd_up() {
|
||||
start_service backend env PYTHONPATH="$ROOT/agent" "$PYTHON_BIN" "$ROOT/agent/cli.py" serve \
|
||||
--host "$BACKEND_HOST" \
|
||||
--port "$BACKEND_PORT"
|
||||
start_service frontend bash -lc '
|
||||
set -euo pipefail
|
||||
cd "$1"
|
||||
if ! [[ -d node_modules ]]; then
|
||||
npm install
|
||||
fi
|
||||
exec npm run dev -- --host "$2" --port "$3"
|
||||
' bash "$ROOT/frontend" "$FRONTEND_HOST" "$FRONTEND_PORT"
|
||||
if ! wait_for_url backend "http://127.0.0.1:$BACKEND_PORT/health" 30 || \
|
||||
! wait_for_url frontend "http://127.0.0.1:$FRONTEND_PORT" 30; then
|
||||
cmd_stop
|
||||
exit 1
|
||||
fi
|
||||
cmd_urls
|
||||
}
|
||||
|
||||
cmd_stop() {
|
||||
stop_service frontend
|
||||
stop_service backend
|
||||
}
|
||||
|
||||
cmd_restart() {
|
||||
local service="${1:-all}"
|
||||
case "$service" in
|
||||
backend)
|
||||
stop_service backend
|
||||
start_service backend env PYTHONPATH="$ROOT/agent" "$PYTHON_BIN" "$ROOT/agent/cli.py" serve \
|
||||
--host "$BACKEND_HOST" \
|
||||
--port "$BACKEND_PORT"
|
||||
wait_for_url backend "http://127.0.0.1:$BACKEND_PORT/health" 30
|
||||
;;
|
||||
frontend)
|
||||
stop_service frontend
|
||||
start_service frontend bash -lc '
|
||||
set -euo pipefail
|
||||
cd "$1"
|
||||
if ! [[ -d node_modules ]]; then
|
||||
npm install
|
||||
fi
|
||||
exec npm run dev -- --host "$2" --port "$3"
|
||||
' bash "$ROOT/frontend" "$FRONTEND_HOST" "$FRONTEND_PORT"
|
||||
wait_for_url frontend "http://127.0.0.1:$FRONTEND_PORT" 30
|
||||
;;
|
||||
all)
|
||||
cmd_stop
|
||||
cmd_up
|
||||
;;
|
||||
*)
|
||||
printf "unknown service: %s\n" "$service" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
cmd_status() {
|
||||
for service in backend frontend; do
|
||||
if is_running "$service"; then
|
||||
printf "%-8s running pid=%s log=%s\n" "$service" "$(cat "$(pid_file "$service")")" "$(log_file "$service")"
|
||||
else
|
||||
printf "%-8s stopped\n" "$service"
|
||||
fi
|
||||
done
|
||||
cmd_urls
|
||||
}
|
||||
|
||||
cmd_logs() {
|
||||
local service="${1:-all}"
|
||||
case "$service" in
|
||||
backend|frontend)
|
||||
touch "$(log_file "$service")"
|
||||
tail -f "$(log_file "$service")"
|
||||
;;
|
||||
all)
|
||||
touch "$(log_file backend)" "$(log_file frontend)"
|
||||
tail -f "$(log_file backend)" "$(log_file frontend)"
|
||||
;;
|
||||
*)
|
||||
printf "unknown service: %s\n" "$service" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
cmd_open() {
|
||||
local url="http://127.0.0.1:$FRONTEND_PORT"
|
||||
if command -v open >/dev/null 2>&1; then
|
||||
open "$url"
|
||||
else
|
||||
printf "%s\n" "$url"
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_urls() {
|
||||
cat <<URLS
|
||||
Frontend: http://127.0.0.1:$FRONTEND_PORT
|
||||
Backend: http://127.0.0.1:$BACKEND_PORT
|
||||
API docs: http://127.0.0.1:$BACKEND_PORT/docs
|
||||
URLS
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
up) cmd_up ;;
|
||||
stop) cmd_stop ;;
|
||||
restart) shift; cmd_restart "${1:-all}" ;;
|
||||
status) cmd_status ;;
|
||||
logs) shift; cmd_logs "${1:-all}" ;;
|
||||
open) cmd_open ;;
|
||||
urls) cmd_urls ;;
|
||||
-h|--help|help|"") usage ;;
|
||||
*)
|
||||
printf "unknown command: %s\n\n" "$1" >&2
|
||||
usage >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
Reference in new issue
Block a user