From f6508e06774799342717b237e08e76f583090f28 Mon Sep 17 00:00:00 2001 From: JeffJiang Date: Tue, 10 Mar 2026 07:38:19 +0800 Subject: [PATCH] feat(dev): refactor service startup to use dedicated start script (#1042) --- Makefile | 93 +------------------------- scripts/start.sh | 136 +++++++++++++++++++++++++++++++++++++++ scripts/wait-for-port.sh | 34 ++++++++++ 3 files changed, 171 insertions(+), 92 deletions(-) create mode 100755 scripts/start.sh create mode 100755 scripts/wait-for-port.sh diff --git a/Makefile b/Makefile index 43516ef..3f2079a 100644 --- a/Makefile +++ b/Makefile @@ -151,98 +151,7 @@ setup-sandbox: # Start all services dev: - @echo "Stopping existing services if any..." - @-pkill -f "langgraph dev" 2>/dev/null || true - @-pkill -f "uvicorn src.gateway.app:app" 2>/dev/null || true - @-pkill -f "next dev" 2>/dev/null || true - @-nginx -c $(PWD)/docker/nginx/nginx.local.conf -p $(PWD) -s quit 2>/dev/null || true - @sleep 1 - @-pkill -9 nginx 2>/dev/null || true - @-./scripts/cleanup-containers.sh deer-flow-sandbox 2>/dev/null || true - @sleep 1 - @echo "" - @echo "==========================================" - @echo " Starting DeerFlow Development Server" - @echo "==========================================" - @echo "" - @echo "Services starting up..." - @echo " → Backend: LangGraph + Gateway" - @echo " → Frontend: Next.js" - @echo " → Nginx: Reverse Proxy" - @echo "" - @if ! { \ - [ -n "$$DEER_FLOW_CONFIG_PATH" ] && [ -f "$$DEER_FLOW_CONFIG_PATH" ] || \ - [ -f backend/config.yaml ] || \ - [ -f config.yaml ]; \ - }; then \ - echo "✗ No DeerFlow config file found."; \ - echo " Checked these locations:"; \ - echo " - $$DEER_FLOW_CONFIG_PATH (when DEER_FLOW_CONFIG_PATH is set)"; \ - echo " - backend/config.yaml"; \ - echo " - ./config.yaml"; \ - echo ""; \ - echo " Run 'make config' from the repo root to generate ./config.yaml, then set required model API keys in .env or your config file."; \ - exit 1; \ - fi - @cleanup() { \ - trap - INT TERM; \ - echo ""; \ - echo "Shutting down services..."; \ - pkill -f "langgraph dev" 2>/dev/null || true; \ - pkill -f "uvicorn src.gateway.app:app" 2>/dev/null || true; \ - pkill -f "next dev" 2>/dev/null || true; \ - nginx -c $(PWD)/docker/nginx/nginx.local.conf -p $(PWD) -s quit 2>/dev/null || true; \ - sleep 1; \ - pkill -9 nginx 2>/dev/null || true; \ - echo "Cleaning up sandbox containers..."; \ - ./scripts/cleanup-containers.sh deer-flow-sandbox 2>/dev/null || true; \ - echo "✓ All services stopped"; \ - exit 0; \ - }; \ - trap cleanup INT TERM; \ - mkdir -p logs; \ - echo "Starting LangGraph server..."; \ - cd backend && NO_COLOR=1 uv run langgraph dev --no-browser --allow-blocking --no-reload > ../logs/langgraph.log 2>&1 & \ - sleep 3; \ - echo "✓ LangGraph server started on localhost:2024"; \ - echo "Starting Gateway API..."; \ - cd backend && uv run uvicorn src.gateway.app:app --host 0.0.0.0 --port 8001 > ../logs/gateway.log 2>&1 & \ - sleep 3; \ - if ! lsof -i :8001 -sTCP:LISTEN -t >/dev/null 2>&1; then \ - echo "✗ Gateway API failed to start. Last log output:"; \ - tail -60 logs/gateway.log; \ - echo ""; \ - echo "Likely configuration errors:"; \ - grep -E "Failed to load configuration|Environment variable .* not found|config\.yaml.*not found" logs/gateway.log | tail -5 || true; \ - cleanup; \ - fi; \ - echo "✓ Gateway API started on localhost:8001"; \ - echo "Starting Frontend..."; \ - cd frontend && pnpm run dev > ../logs/frontend.log 2>&1 & \ - sleep 3; \ - echo "✓ Frontend started on localhost:3000"; \ - echo "Starting Nginx reverse proxy..."; \ - mkdir -p logs && nginx -g 'daemon off;' -c $(PWD)/docker/nginx/nginx.local.conf -p $(PWD) > logs/nginx.log 2>&1 & \ - sleep 2; \ - echo "✓ Nginx started on localhost:2026"; \ - echo ""; \ - echo "=========================================="; \ - echo " DeerFlow is ready!"; \ - echo "=========================================="; \ - echo ""; \ - echo " 🌐 Application: http://localhost:2026"; \ - echo " 📡 API Gateway: http://localhost:2026/api/*"; \ - echo " 🤖 LangGraph: http://localhost:2026/api/langgraph/*"; \ - echo ""; \ - echo " 📋 Logs:"; \ - echo " - LangGraph: logs/langgraph.log"; \ - echo " - Gateway: logs/gateway.log"; \ - echo " - Frontend: logs/frontend.log"; \ - echo " - Nginx: logs/nginx.log"; \ - echo ""; \ - echo "Press Ctrl+C to stop all services"; \ - echo ""; \ - wait + @./scripts/start.sh # Stop all services stop: diff --git a/scripts/start.sh b/scripts/start.sh new file mode 100755 index 0000000..2dbaf9a --- /dev/null +++ b/scripts/start.sh @@ -0,0 +1,136 @@ +#!/usr/bin/env bash +# +# start.sh - Start all DeerFlow development services +# +# Must be run from the repo root directory. + +set -e + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$REPO_ROOT" + +# ── Stop existing services ──────────────────────────────────────────────────── + +echo "Stopping existing services if any..." +pkill -f "langgraph dev" 2>/dev/null || true +pkill -f "uvicorn src.gateway.app:app" 2>/dev/null || true +pkill -f "next dev" 2>/dev/null || true +nginx -c "$REPO_ROOT/docker/nginx/nginx.local.conf" -p "$REPO_ROOT" -s quit 2>/dev/null || true +sleep 1 +pkill -9 nginx 2>/dev/null || true +./scripts/cleanup-containers.sh deer-flow-sandbox 2>/dev/null || true +sleep 1 + +# ── Banner ──────────────────────────────────────────────────────────────────── + +echo "" +echo "==========================================" +echo " Starting DeerFlow Development Server" +echo "==========================================" +echo "" +echo "Services starting up..." +echo " → Backend: LangGraph + Gateway" +echo " → Frontend: Next.js" +echo " → Nginx: Reverse Proxy" +echo "" + +# ── Config check ───────────────────────────────────────────────────────────── + +if ! { \ + [ -n "$DEER_FLOW_CONFIG_PATH" ] && [ -f "$DEER_FLOW_CONFIG_PATH" ] || \ + [ -f backend/config.yaml ] || \ + [ -f config.yaml ]; \ + }; then + echo "✗ No DeerFlow config file found." + echo " Checked these locations:" + echo " - $DEER_FLOW_CONFIG_PATH (when DEER_FLOW_CONFIG_PATH is set)" + echo " - backend/config.yaml" + echo " - ./config.yaml" + echo "" + echo " Run 'make config' from the repo root to generate ./config.yaml, then set required model API keys in .env or your config file." + exit 1 +fi + +# ── Cleanup trap ───────────────────────────────────────────────────────────── + +cleanup() { + trap - INT TERM + echo "" + echo "Shutting down services..." + pkill -f "langgraph dev" 2>/dev/null || true + pkill -f "uvicorn src.gateway.app:app" 2>/dev/null || true + pkill -f "next dev" 2>/dev/null || true + nginx -c "$REPO_ROOT/docker/nginx/nginx.local.conf" -p "$REPO_ROOT" -s quit 2>/dev/null || true + sleep 1 + pkill -9 nginx 2>/dev/null || true + echo "Cleaning up sandbox containers..." + ./scripts/cleanup-containers.sh deer-flow-sandbox 2>/dev/null || true + echo "✓ All services stopped" + exit 0 +} +trap cleanup INT TERM + +# ── Start services ──────────────────────────────────────────────────────────── + +mkdir -p logs + +echo "Starting LangGraph server..." +(cd backend && NO_COLOR=1 uv run langgraph dev --no-browser --allow-blocking --no-reload > ../logs/langgraph.log 2>&1) & +./scripts/wait-for-port.sh 2024 60 "LangGraph" || { + echo " See logs/langgraph.log for details" + tail -20 logs/langgraph.log + cleanup +} +echo "✓ LangGraph server started on localhost:2024" + +echo "Starting Gateway API..." +(cd backend && uv run uvicorn src.gateway.app:app --host 0.0.0.0 --port 8001 > ../logs/gateway.log 2>&1) & +./scripts/wait-for-port.sh 8001 30 "Gateway API" || { + echo "✗ Gateway API failed to start. Last log output:" + tail -60 logs/gateway.log + echo "" + echo "Likely configuration errors:" + grep -E "Failed to load configuration|Environment variable .* not found|config\.yaml.*not found" logs/gateway.log | tail -5 || true + cleanup +} +echo "✓ Gateway API started on localhost:8001" + +echo "Starting Frontend..." +(cd frontend && pnpm run dev > ../logs/frontend.log 2>&1) & +./scripts/wait-for-port.sh 3000 120 "Frontend" || { + echo " See logs/frontend.log for details" + tail -20 logs/frontend.log + cleanup +} +echo "✓ Frontend started on localhost:3000" + +echo "Starting Nginx reverse proxy..." +nginx -g 'daemon off;' -c "$REPO_ROOT/docker/nginx/nginx.local.conf" -p "$REPO_ROOT" > logs/nginx.log 2>&1 & +./scripts/wait-for-port.sh 2026 10 "Nginx" || { + echo " See logs/nginx.log for details" + tail -10 logs/nginx.log + cleanup +} +echo "✓ Nginx started on localhost:2026" + +# ── Ready ───────────────────────────────────────────────────────────────────── + +echo "" +echo "==========================================" +echo " DeerFlow is ready!" +echo "==========================================" +echo "" +echo " 🌐 Application: http://localhost:2026" +echo " 📡 API Gateway: http://localhost:2026/api/*" +echo " 🤖 LangGraph: http://localhost:2026/api/langgraph/*" +echo "" +echo " 📋 Logs:" +echo " - LangGraph: logs/langgraph.log" +echo " - Gateway: logs/gateway.log" +echo " - Frontend: logs/frontend.log" +echo " - Nginx: logs/nginx.log" +echo "" +echo "Press Ctrl+C to stop all services" +echo "" + +wait diff --git a/scripts/wait-for-port.sh b/scripts/wait-for-port.sh new file mode 100755 index 0000000..4afe99d --- /dev/null +++ b/scripts/wait-for-port.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# +# wait-for-port.sh - Wait for a TCP port to become available +# +# Usage: ./scripts/wait-for-port.sh [timeout_seconds] [service_name] +# +# Arguments: +# port - TCP port to wait for (required) +# timeout_seconds - Max seconds to wait (default: 60) +# service_name - Display name for messages (default: "Service") +# +# Exit codes: +# 0 - Port is listening +# 1 - Timed out waiting + +PORT="${1:?Usage: wait-for-port.sh [timeout] [service_name]}" +TIMEOUT="${2:-60}" +SERVICE="${3:-Service}" + +elapsed=0 +interval=1 + +while ! lsof -i :"$PORT" -sTCP:LISTEN -t >/dev/null 2>&1; do + if [ "$elapsed" -ge "$TIMEOUT" ]; then + echo "" + echo "✗ $SERVICE failed to start on port $PORT after ${TIMEOUT}s" + exit 1 + fi + printf "\r Waiting for %s on port %s... %ds" "$SERVICE" "$PORT" "$elapsed" + sleep "$interval" + elapsed=$((elapsed + interval)) +done + +printf "\r %-60s\r" "" # clear the waiting line