Files
eryao/infra/scripts/app.sh
T

220 lines
6.0 KiB
Bash
Raw Normal View History

2026-03-31 13:32:22 +08:00
#!/bin/bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
2026-03-31 13:32:22 +08:00
SESSION_NAME="${SESSION_NAME:-eryao-dev}"
ENV_FILE="$ROOT_DIR/.env"
ENV_LOADER="$ROOT_DIR/infra/scripts/lib/env.sh"
2026-03-31 13:32:22 +08:00
usage() {
echo "Usage: $0 {start|stop|restart}"
echo ""
echo "Commands:"
2026-04-03 16:56:47 +08:00
echo " start Start local web/worker processes in tmux"
2026-03-31 13:32:22 +08:00
echo " stop Stop tmux session and orphaned local processes"
echo " restart Stop then start all app processes"
exit 1
}
load_env_if_exists() {
# shellcheck disable=SC1090
. "$ENV_LOADER"
load_env_file "$ENV_FILE"
2026-04-28 18:49:38 +08:00
load_env_file "$ROOT_DIR/.env.local"
2026-03-31 13:32:22 +08:00
}
is_port_in_use() {
local port="$1"
if command -v lsof >/dev/null 2>&1; then
lsof -iTCP:"$port" -sTCP:LISTEN -t >/dev/null 2>&1
return $?
fi
if command -v ss >/dev/null 2>&1; then
ss -ltn "sport = :$port" | awk 'NR > 1 {exit 0} END {exit 1}'
return $?
fi
return 1
}
collect_listening_pids() {
local port="$1"
if command -v lsof >/dev/null 2>&1; then
lsof -iTCP:"$port" -sTCP:LISTEN -t | sort -u
return
fi
if command -v ss >/dev/null 2>&1; then
ss -lptn "sport = :$port" | awk -F 'pid=' 'NF > 1 {split($2, tmp, ","); print tmp[1]}' | sort -u
fi
}
kill_pids_gracefully() {
local label="$1"
shift
local pids=("$@")
local alive=()
if [ "${#pids[@]}" -eq 0 ]; then
return
fi
echo "Stopping ${label}: ${pids[*]}"
kill -TERM "${pids[@]}" 2>/dev/null || true
for _ in {1..10}; do
alive=()
for pid in "${pids[@]}"; do
if kill -0 "$pid" 2>/dev/null; then
alive+=("$pid")
fi
done
if [ "${#alive[@]}" -eq 0 ]; then
return
fi
sleep 1
done
echo "Force killing ${label}: ${alive[*]}"
kill -KILL "${alive[@]}" 2>/dev/null || true
}
kill_matching_processes() {
local label="$1"
local pattern="$2"
local pids
pids="$(pgrep -f "$pattern" || true)"
if [ -z "$pids" ]; then
return
fi
# shellcheck disable=SC2086
kill_pids_gracefully "$label" $pids
}
kill_listening_processes() {
local label="$1"
local port="$2"
local pids
pids="$(collect_listening_pids "$port" || true)"
if [ -z "$pids" ]; then
return
fi
# shellcheck disable=SC2086
kill_pids_gracefully "$label" $pids
}
start() {
echo "=== Eryao App Up ==="
2026-04-03 16:56:47 +08:00
echo "This script starts local web + worker processes in tmux."
2026-03-31 13:32:22 +08:00
echo "Redis should be managed separately via docker-compose."
echo "NOTE: Database migration must be run separately."
echo ""
if ! command -v tmux >/dev/null 2>&1; then
echo "Error: tmux is required." >&2
exit 1
fi
if [ ! -f "$ENV_FILE" ]; then
echo "Error: env file not found at $ENV_FILE" >&2
exit 1
fi
load_env_if_exists
UVICORN_LOG_LEVEL="${ERYAO_RUNTIME__LOG_LEVEL:-info}"
UVICORN_LOG_LEVEL="$(echo "$UVICORN_LOG_LEVEL" | tr '[:upper:]' '[:lower:]')"
WEB_PORT="${ERYAO_WEB__PORT:-8000}"
if tmux has-session -t "$SESSION_NAME" 2>/dev/null; then
echo "Error: tmux session '$SESSION_NAME' already exists." >&2
echo "Hint: tmux kill-session -t $SESSION_NAME" >&2
exit 1
fi
if is_port_in_use "$WEB_PORT"; then
echo "Error: web port ${WEB_PORT} is already in use." >&2
echo "Hint: run '$0 stop' or change ERYAO_WEB__PORT in .env" >&2
exit 1
fi
2026-04-03 16:56:47 +08:00
if [ -z "${ERYAO_LLM__PROVIDER_KEYS__DEEPSEEK:-}" ]; then
echo "Warning: ERYAO_LLM__PROVIDER_KEYS__DEEPSEEK is empty; deepseek calls may fail." >&2
2026-03-31 13:32:22 +08:00
fi
WEB_CMD="cd '$ROOT_DIR' && PYTHONPATH=backend/src ERYAO_RUNTIME__SERVICE_NAME=web uv run uvicorn app:app --host ${ERYAO_WEB__HOST:-0.0.0.0} --port ${WEB_PORT} --workers ${ERYAO_WEB__WORKERS:-2} --log-level ${UVICORN_LOG_LEVEL}"
2026-03-31 13:32:22 +08:00
2026-04-03 16:56:47 +08:00
WORKER_AGENT_CMD="cd '$ROOT_DIR' && PYTHONPATH=backend/src ERYAO_RUNTIME__SERVICE_NAME=worker-agent uv run taskiq worker core.taskiq.app:worker_agent_broker core.agentscope.runtime.tasks --workers ${ERYAO_WORKER__GROUPS__AGENT__CONCURRENCY:-2}"
WORKER_GENERAL_CMD="cd '$ROOT_DIR' && PYTHONPATH=backend/src ERYAO_RUNTIME__SERVICE_NAME=worker-general uv run taskiq worker core.taskiq.app:worker_general_broker core.agentscope.runtime.tasks v1.feedback.tasks --workers ${ERYAO_WORKER__GROUPS__GENERAL__CONCURRENCY:-1}"
2026-03-31 13:32:22 +08:00
echo "Starting tmux web process in session '$SESSION_NAME'..."
tmux new-session -d -s "$SESSION_NAME" -n web "bash -lc \"$WEB_CMD; echo '[web] exited'; exec bash\""
tmux new-window -t "$SESSION_NAME" -n worker-agent "bash -lc \"$WORKER_AGENT_CMD; echo '[worker-agent] exited'; exec bash\""
2026-04-03 16:56:47 +08:00
tmux new-window -t "$SESSION_NAME" -n worker-general "bash -lc \"$WORKER_GENERAL_CMD; echo '[worker-general] exited'; exec bash\""
2026-03-31 13:32:22 +08:00
echo ""
echo "=== App Started ==="
echo "Web server running on: http://localhost:${WEB_PORT}"
echo "Health check: http://localhost:${WEB_PORT}/health"
echo ""
2026-03-31 13:32:22 +08:00
echo "Log files will be created in logs/ directory:"
echo " - web.log, web.error.log"
2026-04-03 16:56:47 +08:00
echo " - worker-agent.log, worker-agent.error.log"
echo " - worker-general.log, worker-general.error.log"
2026-03-31 13:32:22 +08:00
echo ""
echo "tmux attach -t $SESSION_NAME"
echo "tmux list-windows -t $SESSION_NAME"
}
stop() {
echo "=== Eryao App Down ==="
echo "Stopping tmux app processes (docker redis is not managed here)."
load_env_if_exists
WEB_PORT="${ERYAO_WEB__PORT:-8000}"
if tmux has-session -t "$SESSION_NAME" 2>/dev/null; then
echo "Stopping tmux session '$SESSION_NAME'..."
tmux kill-session -t "$SESSION_NAME"
else
echo "No tmux session '$SESSION_NAME' found."
fi
echo "Checking for orphaned processes..."
kill_matching_processes "uvicorn" "uvicorn app:app"
kill_matching_processes "taskiq workers" "taskiq worker.*core\.taskiq\.app:"
2026-03-31 13:32:22 +08:00
kill_listening_processes "port ${WEB_PORT} listeners" "$WEB_PORT"
if is_port_in_use "$WEB_PORT"; then
echo "Warning: port ${WEB_PORT} is still in use after cleanup." >&2
echo "Hint: check process with 'lsof -iTCP:${WEB_PORT} -sTCP:LISTEN'" >&2
return 1
fi
echo "Session stopped and cleaned up."
}
restart() {
stop
echo ""
start
}
case "${1:-}" in
start) start ;;
stop) stop ;;
restart) restart ;;
*) usage ;;
esac