-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdev.sh
More file actions
executable file
·293 lines (259 loc) · 12.8 KB
/
dev.sh
File metadata and controls
executable file
·293 lines (259 loc) · 12.8 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
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "$0")" && pwd)"
BACKEND_DIR="$ROOT/backend"
FRONTEND_DIR="$ROOT/frontend"
SECRETS_FILE="$HOME/.config/flick/secrets.env"
# Colors
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
# ---------------------------------------------------------------------------
# Dynamic ports: derive a deterministic offset from the workspace path so
# multiple Conductor workspaces of the same repo don't collide.
# Range: backend 8001-8999, frontend 3001-3999
# ---------------------------------------------------------------------------
PORT_HASH=$(echo -n "$ROOT" | shasum | awk '{print $1}')
PORT_OFFSET=$(( 16#${PORT_HASH:0:4} % 999 + 1 )) # 1–999
# Allow manual override: BACKEND_PORT=8080 FRONTEND_PORT=3001 ./dev.sh
BACKEND_PORT="${BACKEND_PORT:-$(( 8000 + PORT_OFFSET ))}"
FRONTEND_PORT="${FRONTEND_PORT:-$(( 3000 + PORT_OFFSET ))}"
echo -e "${CYAN}Workspace:${NC} $ROOT"
echo -e "${CYAN}Ports:${NC} frontend → :${FRONTEND_PORT} backend → :${BACKEND_PORT}"
echo ""
# ---------------------------------------------------------------------------
# 1. Secrets — load from central file or run interactive setup
# ---------------------------------------------------------------------------
# Defaults for optional vars (so they expand to empty if not in secrets.env)
DATABASE_URL=""
SUPABASE_URL=""
SUPABASE_PUBLISHABLE_KEY=""
LLM_PROVIDER="gemini"
GEMINI_API_KEY=""
ANTHROPIC_API_KEY=""
BRAINTRUST_API_KEY=""
ADMIN_EMAILS=""
LINEAR_OAUTH_CLIENT_ID=""
LINEAR_OAUTH_CLIENT_SECRET=""
SLACK_CLIENT_ID=""
SLACK_CLIENT_SECRET=""
POSTHOG_OAUTH_CLIENT_ID=""
POSTHOG_OAUTH_CLIENT_SECRET=""
SENTRY_DSN=""
SENTRY_ORG=""
SENTRY_PROJECT=""
SENTRY_AUTH_TOKEN=""
NEXT_PUBLIC_POSTHOG_KEY=""
GCP_PROJECT_ID=""
VIDEO_GCS_BUCKET=""
interactive_setup() {
echo -e "${BOLD}${CYAN}═══════════════════════════════════════════════════${NC}"
echo -e "${BOLD} First-time setup — configuring ~/.config/flick/secrets.env${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
echo ""
echo -e " This file stores your secrets once and is shared across all workspaces."
echo -e " Press ${BOLD}Enter${NC} to skip optional values."
echo ""
# ── Required: Supabase ──
echo -e "${BOLD}${GREEN}Supabase (required)${NC}"
read -rp " DATABASE_URL (postgresql+asyncpg://...): " DATABASE_URL
read -rp " SUPABASE_URL (https://xxx.supabase.co): " SUPABASE_URL
read -rp " SUPABASE_PUBLISHABLE_KEY (sb_publishable_...): " SUPABASE_PUBLISHABLE_KEY
echo ""
# ── Required: LLM ──
echo -e "${BOLD}${GREEN}LLM Provider (required — at least one API key)${NC}"
read -rp " LLM_PROVIDER [gemini]: " LLM_PROVIDER
LLM_PROVIDER="${LLM_PROVIDER:-gemini}"
read -rp " GEMINI_API_KEY: " GEMINI_API_KEY
read -rp " ANTHROPIC_API_KEY: " ANTHROPIC_API_KEY
echo ""
# ── Optional: Admin ──
echo -e "${BOLD}${YELLOW}Admin (optional)${NC}"
read -rp " ADMIN_EMAILS (comma-separated): " ADMIN_EMAILS
echo ""
# ── Optional: Linear OAuth ──
echo -e "${BOLD}${YELLOW}Linear OAuth (optional)${NC}"
read -rp " LINEAR_OAUTH_CLIENT_ID: " LINEAR_OAUTH_CLIENT_ID
read -rp " LINEAR_OAUTH_CLIENT_SECRET: " LINEAR_OAUTH_CLIENT_SECRET
echo ""
# ── Optional: Slack OAuth ──
echo -e "${BOLD}${YELLOW}Slack OAuth (optional)${NC}"
read -rp " SLACK_CLIENT_ID: " SLACK_CLIENT_ID
read -rp " SLACK_CLIENT_SECRET: " SLACK_CLIENT_SECRET
echo ""
# ── Optional: PostHog OAuth ──
echo -e "${BOLD}${YELLOW}PostHog OAuth (optional)${NC}"
read -rp " POSTHOG_OAUTH_CLIENT_ID: " POSTHOG_OAUTH_CLIENT_ID
read -rp " POSTHOG_OAUTH_CLIENT_SECRET: " POSTHOG_OAUTH_CLIENT_SECRET
echo ""
# ── Optional: Sentry ──
echo -e "${BOLD}${YELLOW}Sentry (optional — leave empty for local dev)${NC}"
read -rp " SENTRY_DSN: " SENTRY_DSN
read -rp " SENTRY_ORG: " SENTRY_ORG
read -rp " SENTRY_PROJECT: " SENTRY_PROJECT
read -rp " SENTRY_AUTH_TOKEN: " SENTRY_AUTH_TOKEN
echo ""
# ── Optional: GCP / Video Storage ──
echo -e "${BOLD}${YELLOW}GCP / Video Storage (optional — needed for View Analyzed Clip)${NC}"
read -rp " GCP_PROJECT_ID: " GCP_PROJECT_ID
read -rp " VIDEO_GCS_BUCKET: " VIDEO_GCS_BUCKET
echo ""
# ── Optional: PostHog Analytics ──
echo -e "${BOLD}${YELLOW}PostHog Analytics for frontend (optional)${NC}"
read -rp " NEXT_PUBLIC_POSTHOG_KEY (phc_...): " NEXT_PUBLIC_POSTHOG_KEY
echo ""
# ── Optional: Braintrust ──
echo -e "${BOLD}${YELLOW}Braintrust (optional)${NC}"
read -rp " BRAINTRUST_API_KEY: " BRAINTRUST_API_KEY
echo ""
# Write the file — use write_kv to preserve $ characters in values
mkdir -p "$(dirname "$SECRETS_FILE")"
{
printf '%s\n' "# ── Flick secrets — shared across all workspaces ──"
printf '%s\n' "# Generated by dev.sh on $(date +%Y-%m-%d)"
printf '%s\n' "# Edit this file directly to update secrets."
printf '\n%s\n' "# ── Supabase (REQUIRED) ──"
printf '%s\n' "DATABASE_URL=${DATABASE_URL}"
printf '%s\n' "SUPABASE_URL=${SUPABASE_URL}"
printf '%s\n' "SUPABASE_PUBLISHABLE_KEY=${SUPABASE_PUBLISHABLE_KEY}"
printf '\n%s\n' "# ── LLM (at least one key required) ──"
printf '%s\n' "LLM_PROVIDER=${LLM_PROVIDER}"
printf '%s\n' "GEMINI_API_KEY=${GEMINI_API_KEY}"
printf '%s\n' "ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}"
printf '%s\n' "BRAINTRUST_API_KEY=${BRAINTRUST_API_KEY}"
printf '\n%s\n' "# ── Admin ──"
printf '%s\n' "ADMIN_EMAILS=${ADMIN_EMAILS}"
printf '\n%s\n' "# ── OAuth — Linear (optional) ──"
printf '%s\n' "LINEAR_OAUTH_CLIENT_ID=${LINEAR_OAUTH_CLIENT_ID}"
printf '%s\n' "LINEAR_OAUTH_CLIENT_SECRET=${LINEAR_OAUTH_CLIENT_SECRET}"
printf '\n%s\n' "# ── OAuth — Slack (optional) ──"
printf '%s\n' "SLACK_CLIENT_ID=${SLACK_CLIENT_ID}"
printf '%s\n' "SLACK_CLIENT_SECRET=${SLACK_CLIENT_SECRET}"
printf '\n%s\n' "# ── OAuth — PostHog (optional) ──"
printf '%s\n' "POSTHOG_OAUTH_CLIENT_ID=${POSTHOG_OAUTH_CLIENT_ID}"
printf '%s\n' "POSTHOG_OAUTH_CLIENT_SECRET=${POSTHOG_OAUTH_CLIENT_SECRET}"
printf '\n%s\n' "# ── Sentry (optional, leave empty for local dev) ──"
printf '%s\n' "SENTRY_DSN=${SENTRY_DSN}"
printf '%s\n' "SENTRY_ORG=${SENTRY_ORG}"
printf '%s\n' "SENTRY_PROJECT=${SENTRY_PROJECT}"
printf '%s\n' "SENTRY_AUTH_TOKEN=${SENTRY_AUTH_TOKEN}"
printf '\n%s\n' "# ── PostHog Analytics in frontend (optional) ──"
printf '%s\n' "NEXT_PUBLIC_POSTHOG_KEY=${NEXT_PUBLIC_POSTHOG_KEY}"
printf '\n%s\n' "# ── GCP / Video Storage (optional) ──"
printf '%s\n' "GCP_PROJECT_ID=${GCP_PROJECT_ID}"
printf '%s\n' "VIDEO_GCS_BUCKET=${VIDEO_GCS_BUCKET}"
} > "$SECRETS_FILE"
echo -e "${GREEN}Saved secrets to ${SECRETS_FILE}${NC}"
echo ""
}
if [ ! -f "$SECRETS_FILE" ]; then
interactive_setup
else
# shellcheck disable=SC1090
source "$SECRETS_FILE"
echo -e "${CYAN}Secrets:${NC} loaded from ${SECRETS_FILE}"
fi
# ---------------------------------------------------------------------------
# 2. Generate .env files (regenerated every run for correct ports)
# ---------------------------------------------------------------------------
{
printf '%s\n' "# ── Auto-generated by dev.sh — edit ~/.config/flick/secrets.env instead ──"
printf '\n%s\n' "# Supabase"
printf '%s\n' "DATABASE_URL=${DATABASE_URL}"
printf '%s\n' "SUPABASE_URL=${SUPABASE_URL}"
printf '%s\n' "SUPABASE_PUBLISHABLE_KEY=${SUPABASE_PUBLISHABLE_KEY}"
printf '\n%s\n' "# LLM"
printf '%s\n' "LLM_PROVIDER=${LLM_PROVIDER}"
printf '%s\n' "GEMINI_API_KEY=${GEMINI_API_KEY}"
printf '%s\n' "ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}"
printf '%s\n' "BRAINTRUST_API_KEY=${BRAINTRUST_API_KEY}"
printf '\n%s\n' "# Admin"
printf '%s\n' "ADMIN_EMAILS=${ADMIN_EMAILS}"
printf '\n%s\n' "# OAuth — redirect URIs point to THIS workspace's frontend port"
printf '%s\n' "LINEAR_OAUTH_CLIENT_ID=${LINEAR_OAUTH_CLIENT_ID}"
printf '%s\n' "LINEAR_OAUTH_CLIENT_SECRET=${LINEAR_OAUTH_CLIENT_SECRET}"
printf '%s\n' "LINEAR_OAUTH_REDIRECT_URI=http://localhost:${FRONTEND_PORT}/auth/linear/callback"
printf '%s\n' "SLACK_CLIENT_ID=${SLACK_CLIENT_ID}"
printf '%s\n' "SLACK_CLIENT_SECRET=${SLACK_CLIENT_SECRET}"
printf '%s\n' "SLACK_OAUTH_REDIRECT_URI=http://localhost:${FRONTEND_PORT}/auth/slack/callback"
printf '%s\n' "POSTHOG_OAUTH_CLIENT_ID=${POSTHOG_OAUTH_CLIENT_ID}"
printf '%s\n' "POSTHOG_OAUTH_CLIENT_SECRET=${POSTHOG_OAUTH_CLIENT_SECRET}"
printf '%s\n' "POSTHOG_OAUTH_REDIRECT_URI=http://localhost:${FRONTEND_PORT}/auth/posthog/callback"
printf '%s\n' "POSTHOG_OAUTH_HOST=https://us.posthog.com"
printf '\n%s\n' "# Dynamic (workspace-specific)"
printf '%s\n' "FRONTEND_URL=http://localhost:${FRONTEND_PORT}"
printf '\n%s\n' "# Sentry"
printf '%s\n' "SENTRY_DSN=${SENTRY_DSN}"
printf '%s\n' "SENTRY_ENVIRONMENT=development"
printf '\n%s\n' "# Dev auth bypass (local testing only)"
printf '%s\n' "DEV_AUTH_TOKEN=dev-local-token"
printf '%s\n' "DEV_AUTH_USER_ID=bcbd7c2f-4e22-4d01-aa46-524709ff70c0"
printf '%s\n' "DEV_AUTH_EMAIL=ignacio@bugster.dev"
printf '\n%s\n' "# Video reconstruction (local)"
printf '%s\n' "VIDEO_OUTPUT_DIR=${ROOT}/session_videos"
printf '%s\n' "VIDEO_DEBUG_ARCHIVE_ENABLED=true"
printf '%s\n' "VIDEO_DEBUG_ARCHIVE_DIR=${ROOT}/session_videos/debug_archive"
printf '\n%s\n' "# Defaults"
printf '%s\n' "SAVE_DEBUG_FRAMES=false"
printf '%s\n' "ENABLE_HEURISTIC_USER_JOURNEY=true"
printf '\n%s\n' "# Event pipeline"
printf '%s\n' "EVENT_PIPELINE_VIDEO_VALIDATION_ENABLED=true"
printf '%s\n' "USE_EMBEDDING_CLUSTERING=true"
printf '\n%s\n' "# GCP / Cloud Tasks"
printf '%s\n' "GCP_PROJECT_ID=${GCP_PROJECT_ID}"
printf '%s\n' "VIDEO_GCS_BUCKET=${VIDEO_GCS_BUCKET}"
} > "$BACKEND_DIR/.env"
{
printf '%s\n' "# ── Auto-generated by dev.sh — edit ~/.config/flick/secrets.env instead ──"
printf '\n%s\n' "NEXT_PUBLIC_SUPABASE_URL=${SUPABASE_URL}"
printf '%s\n' "NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=${SUPABASE_PUBLISHABLE_KEY}"
printf '%s\n' "NEXT_PUBLIC_API_URL=http://localhost:${BACKEND_PORT}"
printf '\n%s\n' "# PostHog analytics"
printf '%s\n' "NEXT_PUBLIC_POSTHOG_KEY=${NEXT_PUBLIC_POSTHOG_KEY}"
printf '%s\n' "NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com"
printf '\n%s\n' "# Sentry"
printf '%s\n' "NEXT_PUBLIC_SENTRY_DSN=${SENTRY_DSN}"
printf '%s\n' "SENTRY_DSN=${SENTRY_DSN}"
printf '%s\n' "SENTRY_ORG=${SENTRY_ORG}"
printf '%s\n' "SENTRY_PROJECT=${SENTRY_PROJECT}"
printf '%s\n' "SENTRY_AUTH_TOKEN=${SENTRY_AUTH_TOKEN}"
printf '\n%s\n' "# OAuth (mirrored from backend for SSR routes)"
printf '%s\n' "LINEAR_OAUTH_CLIENT_ID=${LINEAR_OAUTH_CLIENT_ID}"
printf '%s\n' "LINEAR_OAUTH_CLIENT_SECRET=${LINEAR_OAUTH_CLIENT_SECRET}"
printf '%s\n' "LINEAR_OAUTH_REDIRECT_URI=http://localhost:${FRONTEND_PORT}/auth/linear/callback"
printf '%s\n' "SLACK_CLIENT_ID=${SLACK_CLIENT_ID}"
printf '%s\n' "SLACK_CLIENT_SECRET=${SLACK_CLIENT_SECRET}"
printf '%s\n' "SLACK_OAUTH_REDIRECT_URI=http://localhost:${FRONTEND_PORT}/auth/slack/callback"
} > "$FRONTEND_DIR/.env"
echo -e "${CYAN}Env files:${NC} generated backend/.env and frontend/.env"
# ---------------------------------------------------------------------------
# 3. Dependencies
# ---------------------------------------------------------------------------
command -v bun >/dev/null 2>&1 || { echo -e "${RED}bun not found. Install: https://bun.sh${NC}"; exit 1; }
command -v python3 >/dev/null 2>&1 || { echo -e "${RED}python3 not found${NC}"; exit 1; }
if [ ! -d "$FRONTEND_DIR/node_modules" ]; then
echo -e "${CYAN}[frontend]${NC} Installing dependencies..."
(cd "$FRONTEND_DIR" && bun install)
fi
if [ ! -d "$BACKEND_DIR/.venv" ]; then
echo -e "${CYAN}[backend]${NC} Creating venv and installing dependencies..."
python3 -m venv "$BACKEND_DIR/.venv"
"$BACKEND_DIR/.venv/bin/pip" install -q -r "$BACKEND_DIR/requirements.txt"
fi
# ---------------------------------------------------------------------------
# 4. Start services
# ---------------------------------------------------------------------------
BACKEND_PID=""
FRONTEND_PID=""
cleanup() {
echo -e "\n${YELLOW}Shutting down...${NC}"
[ -n "$BACKEND_PID" ] && kill "$BACKEND_PID" 2>/dev/null || true
[ -n "$FRONTEND_PID" ] && kill "$FRONTEND_PID" 2>/dev/null || true
wait 2>/dev/null || true
echo -e "${GREEN}Done.${NC}"
}
trap cleanup EXIT INT TERM
echo -e "${GREEN}Starting backend :${BACKEND_PORT} and frontend :${FRONTEND_PORT}${NC}\n"
(cd "$BACKEND_DIR" && source .venv/bin/activate && uvicorn app.main:app --host 0.0.0.0 --port "$BACKEND_PORT" --reload) &
BACKEND_PID=$!
(cd "$FRONTEND_DIR" && PORT="$FRONTEND_PORT" bun run dev) &
FRONTEND_PID=$!
wait