Skip to content

Commit ffbacbd

Browse files
authored
Merge pull request #75 from ClipABit/cli-improvements
cli-74: seperate dev instances by prefixing with name
2 parents b1b25cc + 2051f02 commit ffbacbd

File tree

5 files changed

+83
-14
lines changed

5 files changed

+83
-14
lines changed

backend/apps/dev_combined.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"""
1818

1919
import logging
20+
import os
2021
import modal
2122

2223
from shared.config import get_environment, get_secrets
@@ -37,10 +38,25 @@
3738
f"Current environment: {env}. Use separate apps for staging/prod."
3839
)
3940

40-
logger.info("Starting Combined Dev App - all services in one app for local iteration")
41+
# Get dev name prefix (required in dev mode to avoid naming conflicts)
42+
# Only validate locally - Modal containers re-import but don't need DEV_NAME
43+
# since the app name is already determined by the local import
44+
dev_name = os.environ.get("DEV_NAME")
45+
if not dev_name:
46+
if modal.is_local():
47+
raise ValueError(
48+
"DEV_NAME environment variable is required for dev mode. "
49+
"Run with: uv run dev <name>"
50+
)
51+
else:
52+
# Inside Modal container - use placeholder (app name already set)
53+
dev_name = "container"
54+
55+
app_name = f"{dev_name}-{env}-server"
56+
logger.info(f"Starting Combined Dev App '{app_name}' - all services in one app for local iteration")
4157

4258
app = modal.App(
43-
name=f"{env}-server",
59+
name=app_name,
4460
image=get_dev_image(),
4561
secrets=[get_secrets()]
4662
)

backend/cli.py

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""CLI for serving Modal apps locally."""
22

3+
import os
34
import signal
45
import subprocess
56
import sys
@@ -31,16 +32,33 @@ def _prefix_output(process, name, color):
3132
def serve_all():
3233
"""
3334
Serve the combined dev app (all services in one).
34-
35+
3536
For local development, we use dev_combined.py which includes
3637
Server, Search, and Processing in a single Modal app.
3738
This allows hot-reload on all services without cross-app lookup issues.
39+
40+
Usage: uv run dev <name>
41+
42+
The name parameter is required and will prefix the Modal app name
43+
to avoid conflicts when multiple developers run dev instances.
3844
"""
39-
print("Starting combined dev app (all services in one)...\n")
40-
print(f" \033[32m●{RESET} dev-combined (server + search + processing)\n")
45+
if len(sys.argv) < 2:
46+
print("Error: Name parameter is required for dev mode.")
47+
print("Usage: uv run dev <name>")
48+
print("\nExample: uv run dev john")
49+
print("This creates a Modal app named 'john-dev-server'")
50+
sys.exit(1)
51+
52+
dev_name = sys.argv[1]
53+
54+
# Set environment variable for the Modal app to read
55+
os.environ["DEV_NAME"] = dev_name
56+
57+
print(f"Starting combined dev app for '{dev_name}'...\n")
58+
print(f" \033[32m●{RESET} {dev_name}-dev-server (server + search + processing)\n")
4159
print("Note: For staging/prod, deploy individual apps separately.\n")
4260
print("-" * 60 + "\n")
43-
61+
4462
# Run with color-coded output prefixing
4563
color = "\033[32m" # Green for combined dev app
4664
process = subprocess.Popen(
@@ -49,18 +67,19 @@ def serve_all():
4967
stderr=subprocess.STDOUT,
5068
text=True,
5169
bufsize=1,
70+
env={**os.environ, "DEV_NAME": dev_name},
5271
)
53-
72+
5473
# Handle graceful shutdown
5574
def signal_handler(sig, frame):
5675
process.terminate()
5776
sys.exit(0)
58-
77+
5978
signal.signal(signal.SIGINT, signal_handler)
6079
signal.signal(signal.SIGTERM, signal_handler)
61-
80+
6281
# Stream output with color prefix
63-
_prefix_output(process, "dev", color)
82+
_prefix_output(process, dev_name, color)
6483
process.wait()
6584

6685

backend/services/http_server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def create_fastapi_app(self, processing_service_cls=None):
9292

9393
self.fastapi_app = FastAPI(title="Clipabit Server")
9494

95-
# Add CORS middleware for testing
95+
# Add CORS middleware
9696
self.fastapi_app.add_middleware(
9797
CORSMiddleware,
9898
allow_origins=["*"],

frontend/streamlit/cli.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,31 @@
1+
import os
12
import subprocess
3+
import sys
24
from pathlib import Path
35

46

57
def serve() -> None:
8+
"""
9+
Serve the Streamlit frontend.
10+
11+
Usage: uv run dev <name>
12+
13+
The name parameter is required in dev mode and must match the backend
14+
dev name to connect to the correct Modal app URLs.
15+
"""
16+
env = os.environ.get("ENVIRONMENT", "dev")
17+
18+
if env == "dev":
19+
if len(sys.argv) < 2:
20+
print("Error: Name parameter is required for dev mode.")
21+
print("Usage: uv run dev <name>")
22+
print("\nExample: uv run dev john")
23+
print("This connects to the Modal app named 'john-dev-server'")
24+
sys.exit(1)
25+
26+
dev_name = sys.argv[1]
27+
os.environ["DEV_NAME"] = dev_name
28+
print(f"Starting frontend for dev instance '{dev_name}'...")
29+
630
app_path = Path(__file__).resolve().parent / "app.py"
731
subprocess.run(["streamlit", "run", str(app_path)], check=True)

frontend/streamlit/config.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,15 @@ class Config:
2323
if ENVIRONMENT not in ["dev", "prod", "staging"]:
2424
raise ValueError(f"Invalid ENVIRONMENT value: {ENVIRONMENT}. Must be one of: dev, prod, staging")
2525

26-
print(f"Running in {ENVIRONMENT} environment")
26+
# Dev name prefix (required in dev mode to avoid naming conflicts)
27+
DEV_NAME = os.environ.get("DEV_NAME", "")
28+
if ENVIRONMENT == "dev" and not DEV_NAME:
29+
raise ValueError(
30+
"DEV_NAME environment variable is required for dev mode. "
31+
"Run with: uv run dev <name>"
32+
)
33+
34+
print(f"Running in {ENVIRONMENT} environment" + (f" (dev instance: {DEV_NAME})" if DEV_NAME else ""))
2735

2836
# Modal app name (matches backend app name)
2937
APP_NAME = f"clipabit-{ENVIRONMENT}"
@@ -32,12 +40,14 @@ class Config:
3240
url_portion = "dev" if ENVIRONMENT == "dev" else ""
3341
url_portion2 = "-dev" if ENVIRONMENT == "dev" else ""
3442

43+
# App name prefix for dev mode (e.g., "john-dev" instead of "dev")
44+
app_prefix = f"{DEV_NAME}-{ENVIRONMENT}" if ENVIRONMENT == "dev" else ENVIRONMENT
3545

3646
# Server API URL (handles upload, status, videos, delete, cache)
37-
SERVER_BASE_URL = f"https://clipabit01--{ENVIRONMENT}-server-{url_portion}server-asgi-app{url_portion2}.modal.run"
47+
SERVER_BASE_URL = f"https://clipabit01--{app_prefix}-server-{url_portion}server-asgi-app{url_portion2}.modal.run"
3848

3949
# Search API URL (in dev its server-searchservice-asgi-app, else its search-searchservice-asgi-app)
40-
SEARCH_BASE_URL = f"https://clipabit01--{ENVIRONMENT}-{"server" if ENVIRONMENT == "dev" else "search"}-searchservice-asgi-app{url_portion2}.modal.run"
50+
SEARCH_BASE_URL = f"https://clipabit01--{app_prefix}-{"server" if ENVIRONMENT == "dev" else "search"}-searchservice-asgi-app{url_portion2}.modal.run"
4151

4252
# API Endpoints
4353
SERVER_UPLOAD_URL = f"{SERVER_BASE_URL}/upload"

0 commit comments

Comments
 (0)