-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathstart.sh
More file actions
executable file
·295 lines (250 loc) · 10 KB
/
Copy pathstart.sh
File metadata and controls
executable file
·295 lines (250 loc) · 10 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
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
#!/bin/bash
set -u
# Colors for output
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color
# Get absolute path to project root
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$PROJECT_ROOT"
# Python packages live under src/
export PYTHONPATH="$PROJECT_ROOT/src"
API_PORT="${API_PORT:-8000}"
REVIEW_PORT="${REVIEW_PORT:-8100}"
FRONTEND_PORT="${FRONTEND_PORT:-3000}"
NGROK_DOMAIN="${NGROK_DOMAIN:-}"
NGROK_URL="${NGROK_URL:-}"
NGROK_PID=""
NGROK_STARTED=false
# Create logs directory if it doesn't exist
mkdir -p logs
# PID file to track running processes (use absolute path)
PID_FILE="$PROJECT_ROOT/logs/pids.txt"
> "$PID_FILE"
echo -e "${BLUE}Starting BugViper...${NC}\n"
# Avoid accidental use of stale Google ADC paths from the shell.
# If you want Cloud Tasks locally, set GOOGLE_APPLICATION_CREDENTIALS explicitly
# before running this script.
unset GOOGLE_APPLICATION_CREDENTIALS 2>/dev/null || true
if [ ! -d "$PROJECT_ROOT/.venv" ]; then
echo -e "${RED}✗ Python virtualenv not found at .venv${NC}"
echo -e " Create it with: ${YELLOW}uv sync${NC}"
exit 1
fi
if ! command -v npm &> /dev/null; then
echo -e "${YELLOW}Warning: npm not found. Frontend will not start.${NC}"
FRONTEND_AVAILABLE=false
else
FRONTEND_AVAILABLE=true
fi
# Function to cleanup on exit
cleanup() {
echo -e "\n${YELLOW}Stopping all services...${NC}"
if [ -f "$PID_FILE" ]; then
while read -r pid; do
if kill -0 "$pid" 2>/dev/null; then
kill "$pid" 2>/dev/null
wait "$pid" 2>/dev/null
fi
done < "$PID_FILE"
rm "$PID_FILE"
fi
# Kill any remaining processes on our ports
for port in "$API_PORT" "$REVIEW_PORT" "$FRONTEND_PORT"; do
lsof -ti :"$port" 2>/dev/null | xargs kill -9 2>/dev/null || true
done
echo -e "${GREEN}All services stopped.${NC}"
exit 0
}
trap cleanup SIGINT SIGTERM
# Kill existing processes on all ports before starting
kill_port() {
local port=$1
local pids
pids=$(lsof -ti :"$port" 2>/dev/null)
if [ -n "$pids" ]; then
echo -e " Killing existing process on port $port (PID: $(echo $pids | tr '\n' ' '))"
echo "$pids" | xargs kill -9 2>/dev/null || true
sleep 0.5
fi
}
# Check if ngrok is installed
if ! command -v ngrok &> /dev/null; then
echo -e "${YELLOW}Warning: ngrok not found. Install it from https://ngrok.com/download${NC}"
echo -e "${YELLOW}Continuing without ngrok...${NC}\n"
NGROK_AVAILABLE=false
else
NGROK_AVAILABLE=true
fi
# If NGROK_URL isn't exported, load it from .env (without sourcing the whole file).
if [ -z "$NGROK_URL" ] && [ -f "$PROJECT_ROOT/.env" ]; then
while IFS= read -r line; do
case "$line" in
NGROK_URL=*)
NGROK_URL="${line#NGROK_URL=}"
# strip optional surrounding quotes
NGROK_URL="${NGROK_URL#\"}"
NGROK_URL="${NGROK_URL%\"}"
NGROK_URL="${NGROK_URL#\'}"
NGROK_URL="${NGROK_URL%\'}"
break
;;
esac
done < "$PROJECT_ROOT/.env"
fi
# If user provided a fixed NGROK_URL, derive NGROK_DOMAIN so we can start ngrok
# with a reserved domain and avoid random URLs on each run.
if [ -z "$NGROK_DOMAIN" ] && [ -n "$NGROK_URL" ]; then
_tmp="$NGROK_URL"
_tmp="${_tmp#https://}"
_tmp="${_tmp#http://}"
NGROK_DOMAIN="${_tmp%%/*}"
fi
# Start API
echo -e "${BLUE}[1/5] Starting API server...${NC}"
kill_port "$API_PORT"
cd "$PROJECT_ROOT"
source .venv/bin/activate && uvicorn api.app:app --host 0.0.0.0 --port "$API_PORT" --reload --reload-dir "$PROJECT_ROOT/src/api" --reload-dir "$PROJECT_ROOT/src/common" --reload-dir "$PROJECT_ROOT/src/ai_code_review" > "$PROJECT_ROOT/logs/api.log" 2>&1 &
API_PID=$!
echo $API_PID >> "$PID_FILE"
echo -e "${GREEN}✓ API started (PID: $API_PID)${NC}"
echo -e " Log file: logs/api.log"
# Start Review Service
echo -e "\n${BLUE}[2/5] Starting Review Service...${NC}"
kill_port "$REVIEW_PORT"
cd "$PROJECT_ROOT"
source .venv/bin/activate && uvicorn ai_code_review.app:app --host 0.0.0.0 --port "$REVIEW_PORT" --reload --reload-dir "$PROJECT_ROOT/src/ai_code_review" --reload-dir "$PROJECT_ROOT/src/common" > "$PROJECT_ROOT/logs/review.log" 2>&1 &
REVIEW_PID=$!
echo $REVIEW_PID >> "$PID_FILE"
echo -e "${GREEN}✓ Review Service started (PID: $REVIEW_PID)${NC}"
echo -e " Log file: logs/review.log"
# Start Frontend
echo -e "\n${BLUE}[3/5] Starting Frontend...${NC}"
kill_port "$FRONTEND_PORT"
if [ "$FRONTEND_AVAILABLE" = true ]; then
cd "$PROJECT_ROOT/apps/frontend"
FRONTEND_PID=""
if [ ! -d "node_modules" ]; then
echo -e " Installing frontend dependencies (npm ci)..."
npm ci > "$PROJECT_ROOT/logs/frontend-install.log" 2>&1
if [ $? -ne 0 ]; then
echo -e " ${YELLOW}⚠ Frontend npm ci failed — check logs/frontend-install.log${NC}"
echo -e " ${YELLOW} Frontend skipped${NC}"
else
echo -e " ${GREEN}✓ Frontend deps installed${NC}"
fi
fi
if [ -d "node_modules" ]; then
# Force webpack dev server to avoid Turbopack panics on some machines.
NEXT_TELEMETRY_DISABLED=1 PORT="$FRONTEND_PORT" npm run dev -- --webpack > "$PROJECT_ROOT/logs/frontend.log" 2>&1 &
FRONTEND_PID=$!
fi
if [ "$FRONTEND_PID" != "" ]; then
echo $FRONTEND_PID >> "$PID_FILE"
echo -e "${GREEN}✓ Frontend started (PID: $FRONTEND_PID)${NC}"
echo -e " Log file: logs/frontend.log"
fi
else
echo -e " ${YELLOW}⚠ Frontend skipped (npm not found)${NC}"
fi
# Wait for API to be ready
echo -e "\n${BLUE}Waiting for API to be ready...${NC}"
cd "$PROJECT_ROOT"
for i in {1..30}; do
if curl -s "http://localhost:${API_PORT}/docs" > /dev/null 2>&1; then
echo -e "${GREEN}✓ API is ready!${NC}"
break
fi
if [ $i -eq 30 ]; then
echo -e "${RED}✗ API failed to start within 30 seconds${NC}"
echo -e "${YELLOW}Check logs/api.log for errors${NC}"
cleanup
fi
sleep 1
done
# Start ngrok if available
if [ "$NGROK_AVAILABLE" = true ]; then
echo -e "\n${BLUE}[4/5] Starting ngrok tunnel...${NC}"
if [ -n "$NGROK_DOMAIN" ]; then
ngrok http "$API_PORT" --domain="$NGROK_DOMAIN" > "$PROJECT_ROOT/logs/ngrok.log" 2>&1 &
else
ngrok http "$API_PORT" > "$PROJECT_ROOT/logs/ngrok.log" 2>&1 &
fi
NGROK_PID=$!
NGROK_STARTED=true
echo $NGROK_PID >> "$PID_FILE"
echo -e "${GREEN}✓ Ngrok started (PID: $NGROK_PID)${NC}"
echo -e " Log file: logs/ngrok.log"
# Wait for ngrok to initialize and get URL
echo -n " Waiting for URL..."
for i in {1..10}; do
NGROK_URL=$(curl -s http://localhost:4040/api/tunnels 2>/dev/null | grep -o '"public_url":"https://[^"]*' | grep -o 'https://[^"]*' | head -1)
if [ -n "$NGROK_URL" ]; then
echo -e "\r URL: $NGROK_URL "
break
fi
sleep 1
done
if [ -z "$NGROK_URL" ]; then
echo -e "\r ${YELLOW}Could not retrieve ngrok URL${NC}"
fi
else
echo -e "\n${YELLOW}[4/5] Skipping ngrok (not installed)${NC}"
fi
# Display summary
echo -e "\n${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${GREEN} BugViper is now running!${NC}"
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n"
echo -e "${BLUE}URLs:${NC}"
echo -e " Frontend: ${YELLOW}http://localhost:${FRONTEND_PORT}${NC}"
echo -e " API: ${YELLOW}http://localhost:${API_PORT}${NC}"
echo -e " API Docs: ${YELLOW}http://localhost:${API_PORT}/docs${NC}"
echo -e " Review: ${YELLOW}http://localhost:${REVIEW_PORT}${NC}"
echo -e " Review Docs: ${YELLOW}http://localhost:${REVIEW_PORT}/docs${NC}"
if [ "$NGROK_AVAILABLE" = true ] && [ -n "$NGROK_URL" ]; then
echo -e " Ngrok: ${YELLOW}$NGROK_URL${NC}"
if [ "$NGROK_STARTED" = true ]; then
echo -e " Ngrok Admin: ${YELLOW}http://localhost:4040${NC}"
fi
echo -e " Webhook: ${YELLOW}${NGROK_URL%/}/api/v1/webhook/onComment${NC}"
fi
echo -e "\n${BLUE}View Logs:${NC}"
echo -e " API: ${YELLOW}tail -f logs/api.log${NC}"
echo -e " Frontend: ${YELLOW}tail -f logs/frontend.log${NC}"
if [ "$NGROK_STARTED" = true ]; then
echo -e " Ngrok: ${YELLOW}tail -f logs/ngrok.log${NC}"
fi
echo -e " All: ${YELLOW}tail -f logs/*.log${NC}"
echo -e "\n${BLUE}Process IDs:${NC}"
echo -e " API: ${YELLOW}$API_PID${NC}"
echo -e " Review: ${YELLOW}$REVIEW_PID${NC}"
if [ "${FRONTEND_PID:-}" != "" ]; then
echo -e " Frontend: ${YELLOW}$FRONTEND_PID${NC}"
fi
if [ "$NGROK_STARTED" = true ] && [ "$NGROK_PID" != "" ]; then
echo -e " Ngrok: ${YELLOW}$NGROK_PID${NC}"
fi
echo -e "\n${RED}Press Ctrl+C to stop all services${NC}\n"
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n"
# Keep this script running so background processes don't get terminated
# when the non-interactive shell exits. Also detects crashes.
while true; do
sleep 2
for pid in "$API_PID" "$REVIEW_PID"; do
if ! kill -0 "$pid" 2>/dev/null; then
echo -e "\n${RED}✗ A service process exited (PID: $pid). Stopping all services...${NC}"
cleanup
fi
done
if [ "${FRONTEND_PID:-}" != "" ] && ! kill -0 "$FRONTEND_PID" 2>/dev/null; then
echo -e "\n${RED}✗ Frontend process exited (PID: $FRONTEND_PID). Stopping all services...${NC}"
cleanup
fi
if [ "$NGROK_STARTED" = true ] && [ "$NGROK_PID" != "" ] && ! kill -0 "$NGROK_PID" 2>/dev/null; then
echo -e "\n${YELLOW}⚠ Ngrok tunnel exited (PID: $NGROK_PID). Webhook URL will not be available.${NC}"
NGROK_STARTED=false
NGROK_PID=""
fi
done