#!/bin/bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)" SESSION_NAME="${SESSION_NAME:-social-dev}" COMPOSE_FILE="$ROOT_DIR/infra/docker/docker-compose.yml" ENV_FILE="$ROOT_DIR/.env" usage() { echo "Usage: $0 {start|stop}" echo "" echo "Commands:" echo " start Start web + worker processes in tmux" echo " stop Stop and clean up tmux session" exit 1 } start() { echo "=== App Up ===" echo "This script starts web + worker processes in tmux." echo "NOTE: Bootstrap (migrate + init-data) must be run separately." echo "" if ! command -v tmux >/dev/null 2>&1; then echo "Error: tmux is required." >&2 exit 1 fi if [ ! -f "$ENV_FILE" ]; then echo "Error: env file not found at $ENV_FILE" >&2 exit 1 fi if [ ! -f "$COMPOSE_FILE" ]; then echo "Error: compose file not found at $COMPOSE_FILE" >&2 exit 1 fi set -a # shellcheck disable=SC1090 . "$ENV_FILE" set +a if tmux has-session -t "$SESSION_NAME" 2>/dev/null; then echo "Error: tmux session '$SESSION_NAME' already exists." >&2 echo "Hint: tmux kill-session -t $SESSION_NAME" >&2 exit 1 fi echo "Starting web + worker processes in tmux session '$SESSION_NAME'..." WEB_CMD="cd '$ROOT_DIR' && PYTHONPATH=backend/src SOCIAL_RUNTIME__SERVICE_NAME=web uv run gunicorn app:app --bind \ ${SOCIAL_WEB__HOST:-0.0.0.0}:${SOCIAL_WEB__PORT:-8000} --workers \ ${SOCIAL_WEB__GUNICORN__WORKERS:-2} --worker-class \ ${SOCIAL_WEB__GUNICORN__WORKER_CLASS:-uvicorn.workers.UvicornWorker} --timeout \ ${SOCIAL_WEB__GUNICORN__TIMEOUT:-60} \ --log-level ${SOCIAL_RUNTIME__LOG_LEVEL:-info}" WORKER_CRITICAL_CMD="cd '$ROOT_DIR' && PYTHONPATH=backend/src SOCIAL_RUNTIME__SERVICE_NAME=worker-critical uv run celery -A core.celery.app worker --loglevel=info --queues=critical --concurrency=${SOCIAL_WORKER__GROUPS__CRITICAL__CONCURRENCY:-2}" WORKER_DEFAULT_CMD="cd '$ROOT_DIR' && PYTHONPATH=backend/src SOCIAL_RUNTIME__SERVICE_NAME=worker-default uv run celery -A core.celery.app worker --loglevel=info --queues=default --concurrency=${SOCIAL_WORKER__GROUPS__DEFAULT__CONCURRENCY:-2}" WORKER_BULK_CMD="cd '$ROOT_DIR' && PYTHONPATH=backend/src SOCIAL_RUNTIME__SERVICE_NAME=worker-bulk uv run celery -A core.celery.app worker --loglevel=info --queues=bulk --concurrency=${SOCIAL_WORKER__GROUPS__BULK__CONCURRENCY:-1}" tmux new-session -d -s "$SESSION_NAME" -n web "bash -lc \"$WEB_CMD; echo '[web] exited'; exec bash\"" tmux new-window -t "$SESSION_NAME" -n worker-critical "bash -lc \"$WORKER_CRITICAL_CMD; echo '[worker-critical] exited'; exec bash\"" tmux new-window -t "$SESSION_NAME" -n worker-default "bash -lc \"$WORKER_DEFAULT_CMD; echo '[worker-default] exited'; exec bash\"" tmux new-window -t "$SESSION_NAME" -n worker-bulk "bash -lc \"$WORKER_BULK_CMD; echo '[worker-bulk] exited'; exec bash\"" echo "" echo "=== App Started ===" echo "Log files will be created in logs/ directory:" echo " - web.log, web.error.log" echo " - worker-critical.log, worker-critical.error.log" echo " - worker-default.log, worker-default.error.log" echo " - worker-bulk.log, worker-bulk.error.log" echo "" echo "tmux attach -t $SESSION_NAME" echo "tmux list-windows -t $SESSION_NAME" } stop() { echo "=== App Down ===" if tmux has-session -t "$SESSION_NAME" 2>/dev/null; then echo "Stopping tmux session '$SESSION_NAME'..." tmux kill-session -t "$SESSION_NAME" else echo "No tmux session '$SESSION_NAME' found." fi echo "Checking for orphaned processes..." if pgrep -f "gunicorn.*app:app" > /dev/null 2>&1; then echo "Killing orphaned gunicorn processes..." pkill -f "gunicorn.*app:app" fi if pgrep -f "celery.*worker" > /dev/null 2>&1; then echo "Killing orphaned celery processes..." pkill -f "celery.*worker" fi echo "Session stopped and cleaned up." } case "${1:-}" in start) start ;; stop) stop ;; *) usage ;; esac