Repository navigation
Expand file tree
/
Copy pathdev.sh
More file actions
144 lines (126 loc) · 4.12 KB
/
Copy pathdev.sh
File metadata and controls
144 lines (126 loc) · 4.12 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
#!/usr/bin/env bash
# Starts the whole stack for local development: Postgres (docker), migrations,
# the FastAPI app, the Telegram bot, and the web UI. Works under bash on
# Linux/macOS and Git Bash on Windows.
#
# Usage:
# ./scripts/dev.sh # start everything
# ./scripts/dev.sh --no-bot # skip the Telegram bot
# ./scripts/dev.sh --no-frontend # skip the web UI
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
BACKEND_DIR="$ROOT_DIR/backend"
FRONTEND_DIR="$ROOT_DIR/frontend"
RUN_BOT=1
RUN_FRONTEND=1
for arg in "$@"; do
case "$arg" in
--no-bot) RUN_BOT=0 ;;
--no-frontend) RUN_FRONTEND=0 ;;
*)
echo "Unknown option: $arg" >&2
exit 1
;;
esac
done
if [ "$RUN_FRONTEND" -eq 1 ] && [ ! -d "$FRONTEND_DIR" ]; then
RUN_FRONTEND=0
fi
cd "$BACKEND_DIR"
if [ ! -f .env ]; then
echo "Missing backend/.env. Copy backend/.env.example to backend/.env and fill in the values first." >&2
exit 1
fi
# Load .env into this shell so we can sanity-check required vars up front.
set -a
# shellcheck disable=SC1091
source .env
set +a
missing=()
[ -z "${BOAT_API_KEY:-}" ] && missing+=("BOAT_API_KEY")
[ -z "${TOKEN_ENCRYPTION_KEYS:-}" ] && missing+=("TOKEN_ENCRYPTION_KEYS")
[ -z "${HOOK_TOKEN:-}" ] && missing+=("HOOK_TOKEN")
[ -z "${PERMISSION_HOOK_BASE_URL:-}" ] && missing+=("PERMISSION_HOOK_BASE_URL")
if [ "$RUN_BOT" -eq 1 ] && [ -z "${TELEGRAM_BOT_TOKEN:-}" ]; then
missing+=("TELEGRAM_BOT_TOKEN (or rerun with --no-bot)")
fi
if [ "${#missing[@]}" -gt 0 ]; then
echo "Missing required backend/.env values:" >&2
printf ' - %s\n' "${missing[@]}" >&2
exit 1
fi
echo "==> Starting Postgres (docker compose)"
docker compose up -d
echo "==> Waiting for Postgres to be healthy"
POSTGRES_CONTAINER="$(docker compose ps -q postgres)"
for _ in $(seq 1 30); do
status="$(docker inspect --format='{{.State.Health.Status}}' "$POSTGRES_CONTAINER" 2>/dev/null || echo "starting")"
[ "$status" = "healthy" ] && break
sleep 1
done
if [ "$status" != "healthy" ]; then
echo "Postgres did not become healthy in time." >&2
exit 1
fi
echo "==> Installing backend dependencies (uv sync)"
uv sync
echo "==> Running migrations (alembic upgrade head)"
uv run alembic upgrade head
# Give each service its own process group, including reloaders/npm children.
set -m
pids=()
cleanup() {
echo
echo "==> Shutting down"
for pid in "${pids[@]}"; do
kill -- -"$pid" 2>/dev/null || kill "$pid" 2>/dev/null || true
done
}
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
echo "==> Starting FastAPI app on http://127.0.0.1:8000"
uv run fastapi dev app/main.py --host 127.0.0.1 --port 8000 &
pids+=("$!")
if [ "$RUN_BOT" -eq 1 ]; then
echo "==> Starting Telegram bot"
uv run python -m app.telegram_bot &
pids+=("$!")
else
echo "==> Skipping Telegram bot (--no-bot)"
fi
if [ "$RUN_FRONTEND" -eq 1 ]; then
if [ ! -f "$FRONTEND_DIR/.env.local" ] && [ -f "$FRONTEND_DIR/.env.example" ]; then
cp "$FRONTEND_DIR/.env.example" "$FRONTEND_DIR/.env.local"
fi
if [ ! -d "$FRONTEND_DIR/node_modules" ]; then
echo "==> Installing frontend dependencies (npm install)"
(cd "$FRONTEND_DIR" && npm install)
fi
echo "==> Starting web UI on http://127.0.0.1:3000"
# Replace the subshell so its PID remains the frontend process-group leader.
# Otherwise cleanup can kill the shell while its npm child is still starting.
(cd "$FRONTEND_DIR" && exec npm run dev) &
pids+=("$!")
else
echo "==> Skipping web UI (--no-frontend)"
fi
echo "==> Everything is up. Press Ctrl+C to stop."
# Plain `wait` ignores an individual service failure while siblings keep
# running, and ultimately discards their exit statuses. Monitor each child
# with Bash 3-compatible primitives so a failed stack stops promptly.
while true; do
for pid in "${pids[@]}"; do
if ! kill -0 "$pid" 2>/dev/null; then
if wait "$pid"; then
status=1 # Even a normal exit is unexpected for a development server.
else
status=$?
fi
echo "Development service (PID $pid) exited; stopping the stack." >&2
exit "$status"
fi
done
sleep 1
done