-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstart.sh
More file actions
executable file
·242 lines (216 loc) · 8.84 KB
/
Copy pathstart.sh
File metadata and controls
executable file
·242 lines (216 loc) · 8.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
mkdir -p logs
# Zero out logs on each startup for clean debugging
for f in logs/*.log; do
[ -f "$f" ] && : > "$f"
done
# ── Parse flags ──────────────────────────────────────────────────────────
NO_INFRA=false
NO_FRONTEND=false
API_ONLY=false
NO_BROWSER=false
for arg in "$@"; do
case "$arg" in
--no-infra) NO_INFRA=true ;;
--no-frontend) NO_FRONTEND=true ;;
--api-only) API_ONLY=true; NO_INFRA=true; NO_FRONTEND=true ;;
--no-browser) NO_BROWSER=true ;;
--help|-h)
echo "Usage: ./start.sh [OPTIONS]"
echo ""
echo "Options:"
echo " --no-infra Skip Docker infrastructure (use existing or in-memory only)"
echo " --no-frontend Skip Next.js frontend"
echo " --api-only Start only the API server (no infra, no frontend)"
echo " --no-browser Don't auto-open browser on startup"
echo " --help Show this help"
echo ""
echo "Default: starts infrastructure → worker → API → frontend → opens browser"
exit 0
;;
esac
done
TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S")
echo "╔══════════════════════════════════════╗"
echo "║ WorldMaker Startup ║"
echo "╚══════════════════════════════════════╝"
echo ""
echo " Logs: $SCRIPT_DIR/logs/"
echo ""
STEP=1
TOTAL=4
if $NO_INFRA; then TOTAL=$((TOTAL - 1)); fi
if $NO_FRONTEND; then TOTAL=$((TOTAL - 1)); fi
# ── [1] Infrastructure ───────────────────────────────────────────────────
if ! $NO_INFRA; then
echo "[$STEP/$TOTAL] Starting infrastructure..."
docker compose up -d postgres mongodb neo4j redis kafka zookeeper \
>> logs/infra.log 2>&1
echo " Waiting for services to be healthy..."
SERVICES=(postgres mongodb neo4j redis)
MAX_WAIT=60
for svc in "${SERVICES[@]}"; do
ELAPSED=0
while true; do
STATUS=$(docker compose ps "$svc" --format json 2>/dev/null | python3 -c "
import sys, json
try:
data = json.load(sys.stdin)
if isinstance(data, list): data = data[0]
print(data.get('Health', data.get('State', 'unknown')))
except: print('unknown')
" 2>/dev/null || echo "unknown")
if [[ "$STATUS" == *"healthy"* ]]; then
echo " ✓ $svc ready"
break
fi
if (( ELAPSED >= MAX_WAIT )); then
echo " ⚠ $svc not healthy after ${MAX_WAIT}s — continuing anyway" | tee -a logs/infra-error.log
break
fi
sleep 2
ELAPSED=$((ELAPSED + 2))
done
done
# Run migrations if Postgres is up
echo " Running database migrations..."
if uv run alembic upgrade head >> logs/infra.log 2>&1; then
echo " ✓ Migrations applied"
else
echo " ⚠ Migration failed (non-fatal, in-memory store still works)" | tee -a logs/infra-error.log
fi
STEP=$((STEP + 1))
fi
# ── [2] Celery Worker ────────────────────────────────────────────────────
echo "[$STEP/$TOTAL] Starting Celery worker..."
if docker compose up -d worldmaker-worker >> logs/worker.log 2>&1; then
echo " ✓ Worker started (Docker)"
else
if command -v celery &>/dev/null; then
nohup uv run celery -A worldmaker.engine.scheduler worker \
--loglevel=info >> logs/worker.log 2>> logs/worker-error.log &
echo " ✓ Worker started (local, PID: $!)"
else
echo " ⚠ Celery not available — using AsyncScheduler fallback"
fi
fi
STEP=$((STEP + 1))
# ── [3] API Server (background) ─────────────────────────────────────────
echo "[$STEP/$TOTAL] Starting API server..."
nohup uv run worldmaker serve --reload \
>> logs/api.log 2>> logs/api-error.log &
API_PID=$!
echo " PID: $API_PID"
echo " Log: logs/api.log | Errors: logs/api-error.log"
# Wait for API to be ready
echo -n " Waiting for API"
API_READY=false
for i in $(seq 1 30); do
if curl -sf http://localhost:8000/api/v1/health > /dev/null 2>&1; then
API_READY=true
break
fi
echo -n "."
sleep 1
done
echo ""
if $API_READY; then
echo " ✓ API server ready (http://localhost:8000)"
else
echo " ⚠ API not responding yet — check logs/api-error.log" | tee -a logs/api-error.log
echo " (server may still be starting, give it a moment)"
fi
STEP=$((STEP + 1))
# ── [4] Frontend (background) ────────────────────────────────────────────
FRONTEND_READY=false
if ! $NO_FRONTEND; then
echo "[$STEP/$TOTAL] Starting frontend (Next.js)..."
if [ -d "$SCRIPT_DIR/frontend" ] && [ -f "$SCRIPT_DIR/frontend/package.json" ]; then
# Install deps if node_modules missing
if [ ! -d "$SCRIPT_DIR/frontend/node_modules" ]; then
echo " Installing frontend dependencies..."
(cd "$SCRIPT_DIR/frontend" && npm install --silent) >> logs/frontend.log 2>&1 || true
fi
# Start dev server in background.
# IMPORTANT: Must cd into frontend/ so that process.cwd() points to
# frontend/, not worldmaker/. Without this, Next.js infers the wrong
# workspace root and CSS module resolution (tailwindcss) fails.
nohup bash -c "cd '$SCRIPT_DIR/frontend' && npm run dev" \
>> logs/frontend.log 2>> logs/frontend-error.log &
FRONTEND_PID=$!
echo " PID: $FRONTEND_PID"
echo " Log: logs/frontend.log | Errors: logs/frontend-error.log"
# Wait for frontend to be ready
echo -n " Waiting for frontend"
for i in $(seq 1 30); do
if curl -sf http://localhost:3000 > /dev/null 2>&1; then
FRONTEND_READY=true
break
fi
echo -n "."
sleep 1
done
echo ""
if $FRONTEND_READY; then
echo " ✓ Frontend ready (http://localhost:3000)"
else
echo " ⚠ Frontend not responding yet — check logs/frontend-error.log" | tee -a logs/frontend-error.log
fi
else
echo " ⚠ frontend/ directory not found — skipping"
fi
STEP=$((STEP + 1))
fi
# ── Summary ──────────────────────────────────────────────────────────────
echo ""
echo "┌──────────────────────────────────────────────────────┐"
echo "│ WorldMaker is running │"
echo "│ │"
echo "│ API: http://localhost:8000 │"
echo "│ Docs: http://localhost:8000/api/docs │"
if ! $NO_FRONTEND; then
echo "│ Frontend: http://localhost:3000 │"
fi
echo "│ │"
echo "│ Logs: $SCRIPT_DIR/logs/ │"
echo "│ Stop: ./shutdown.sh │"
echo "└──────────────────────────────────────────────────────┘"
echo ""
# ── Open browser & bring to foreground ───────────────────────────────────
if ! $NO_BROWSER; then
LANDING="http://localhost:3000"
if $NO_FRONTEND; then
LANDING="http://localhost:8000/api/docs"
fi
if [[ "$(uname)" == "Darwin" ]]; then
# macOS: open in default browser and bring to front
open "$LANDING"
sleep 0.5
osascript -e '
tell application "System Events"
set frontApp to name of first application process whose frontmost is true
end tell
if frontApp is not "Safari" and frontApp is not "Google Chrome" and frontApp is not "Firefox" and frontApp is not "Arc" then
try
tell application "Google Chrome" to activate
on error
try
tell application "Safari" to activate
end try
end try
end if
' 2>/dev/null || true
echo " ✓ Browser opened → $LANDING"
elif command -v xdg-open &>/dev/null; then
# Linux
xdg-open "$LANDING" 2>/dev/null &
echo " ✓ Browser opened → $LANDING"
else
echo " → Open $LANDING in your browser"
fi
fi
echo ""
echo "All processes running in background. Use ./shutdown.sh to stop."