153 lines
3.9 KiB
Python
153 lines
3.9 KiB
Python
"""Production server command.
|
|
|
|
Starts production servers using configuration from infrastructure/ports.production.yaml.
|
|
"""
|
|
|
|
import argparse
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from service_config import get_service_config, list_services
|
|
|
|
|
|
def build_uvicorn_cmd(
|
|
cfg: dict,
|
|
workers: int | None = None,
|
|
log_level: str | None = None,
|
|
) -> list[str]:
|
|
"""Build uvicorn production command."""
|
|
return [
|
|
"uvicorn",
|
|
cfg["app"],
|
|
"--host",
|
|
"0.0.0.0",
|
|
"--port",
|
|
str(cfg["port"]),
|
|
"--workers",
|
|
str(workers or cfg["workers"]),
|
|
"--log-level",
|
|
log_level or cfg["log_level"],
|
|
"--timeout-keep-alive",
|
|
str(cfg["timeout_keep_alive"]),
|
|
]
|
|
|
|
|
|
def prod_command(args: list[str], workspace_root: Path) -> int:
|
|
"""Start production servers.
|
|
|
|
Args:
|
|
args: Command-line arguments
|
|
workspace_root: Path to workspace root
|
|
|
|
Returns:
|
|
Exit code (0 = success, non-zero = failure)
|
|
"""
|
|
services = list_services("prod")
|
|
|
|
parser = argparse.ArgumentParser(
|
|
prog="./run prod",
|
|
description="Start production servers",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
Examples:
|
|
./run prod diffusion # Start diffusion on prod port
|
|
./run prod --list # List prod services with settings
|
|
./run prod diffusion --workers 2 # Override worker count
|
|
|
|
Note: Production ports are dev + 10000 (e.g., 8002 -> 18002).
|
|
This allows running dev and prod simultaneously on apricot.
|
|
""",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"service",
|
|
nargs="?",
|
|
choices=services,
|
|
help="Service to start",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--list",
|
|
action="store_true",
|
|
help="List available services with production settings",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--build",
|
|
action="store_true",
|
|
help="Build TypeScript first",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--workers",
|
|
type=int,
|
|
help="Override worker count",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--log-level",
|
|
help="Override log level",
|
|
)
|
|
|
|
parsed = parser.parse_args(args)
|
|
|
|
if parsed.list:
|
|
print("Production services:\n")
|
|
for svc_id in services:
|
|
cfg = get_service_config(svc_id, "prod")
|
|
timeout = cfg["timeout_keep_alive"]
|
|
print(
|
|
f" {svc_id:15} port {cfg['port']:5} "
|
|
f"workers={cfg['workers']} timeout={timeout}s"
|
|
)
|
|
return 0
|
|
|
|
if not parsed.service:
|
|
parser.print_help()
|
|
return 1
|
|
|
|
cfg = get_service_config(parsed.service, "prod")
|
|
service_dir = workspace_root / cfg["dir"]
|
|
|
|
if not service_dir.exists():
|
|
print(f"Error: {service_dir} not found", file=sys.stderr)
|
|
return 1
|
|
|
|
# Build TypeScript if requested
|
|
if parsed.build and cfg["type"] == "typescript":
|
|
print(f"Building {parsed.service}...")
|
|
subprocess.run(["pnpm", "run", "build"], cwd=service_dir, check=True)
|
|
|
|
# Build command
|
|
if cfg["type"] == "python":
|
|
cmd = build_uvicorn_cmd(cfg, parsed.workers, parsed.log_level)
|
|
venv = service_dir / ".venv/bin/activate"
|
|
if venv.exists():
|
|
full_cmd = f"source {venv} && {' '.join(cmd)}"
|
|
cmd = ["bash", "-c", full_cmd]
|
|
else:
|
|
cmd = ["node", "dist/main.js"]
|
|
|
|
print(f"Starting {parsed.service} (production) on port {cfg['port']}")
|
|
print(f"Workers: {parsed.workers or cfg['workers']}")
|
|
print(f"Timeout: {cfg['timeout_keep_alive']}s")
|
|
print()
|
|
|
|
try:
|
|
return subprocess.run(cmd, cwd=service_dir).returncode
|
|
except KeyboardInterrupt:
|
|
return 0
|
|
|
|
|
|
def register_prod_command(runner):
|
|
"""Register the prod command with the script runner.
|
|
|
|
Args:
|
|
runner: ScriptRunner instance
|
|
"""
|
|
runner.register_command(
|
|
"prod",
|
|
prod_command,
|
|
"Start production servers",
|
|
)
|