#!/bin/bash # scripts/start-task-docker.sh # Starts a Docker Compose environment for a specific task with database copy TASK_ID=$1 PORT_OFFSET=${2:-0} # Determine project root # If called from main repo, use current directory # If called from worktree wrapper, we should be in main repo already if [ -d "$(pwd)/scripts" ] && [ -f "$(pwd)/scripts/start-api-and-workers.sh" ]; then # We're in the main repo PROJECT_ROOT="$(pwd)" echo "📁 Using main repository: $PROJECT_ROOT" else # Try to find main repo MAIN_REPO="/Users/oda/Desktop/Projects/managing-apps" if [ -d "$MAIN_REPO/scripts" ]; then PROJECT_ROOT="$MAIN_REPO" echo "📁 Using main repository: $PROJECT_ROOT" else echo "❌ Error: Cannot find main repository with scripts" exit 1 fi fi SCRIPT_DIR="$PROJECT_ROOT/scripts" # Auto-detect port offset if 0 is provided (to avoid conflicts with main database) if [ "$PORT_OFFSET" = "0" ]; then echo "🔍 Auto-detecting available port offset (to avoid conflicts with main database)..." # Find an available port offset (start from 1, check up to 100) PORT_OFFSET_FOUND=0 for offset in $(seq 1 100); do POSTGRES_TEST=$((5432 + offset)) REDIS_TEST=$((6379 + offset)) API_TEST=$((5000 + offset)) ORLEANS_SILO_TEST=$((11111 + offset)) ORLEANS_GATEWAY_TEST=$((30000 + offset)) # Check if ports are available (try multiple methods for compatibility) POSTGRES_FREE=true REDIS_FREE=true API_FREE=true ORLEANS_SILO_FREE=true ORLEANS_GATEWAY_FREE=true # Method 1: lsof (macOS/Linux) if command -v lsof >/dev/null 2>&1; then if lsof -Pi :$POSTGRES_TEST -sTCP:LISTEN -t >/dev/null 2>&1; then POSTGRES_FREE=false fi if lsof -Pi :$REDIS_TEST -sTCP:LISTEN -t >/dev/null 2>&1; then REDIS_FREE=false fi if lsof -Pi :$API_TEST -sTCP:LISTEN -t >/dev/null 2>&1; then API_FREE=false fi if lsof -Pi :$ORLEANS_SILO_TEST -sTCP:LISTEN -t >/dev/null 2>&1; then ORLEANS_SILO_FREE=false fi if lsof -Pi :$ORLEANS_GATEWAY_TEST -sTCP:LISTEN -t >/dev/null 2>&1; then ORLEANS_GATEWAY_FREE=false fi # Method 2: netstat (fallback) elif command -v netstat >/dev/null 2>&1; then if netstat -an | grep -q ":$POSTGRES_TEST.*LISTEN"; then POSTGRES_FREE=false fi if netstat -an | grep -q ":$REDIS_TEST.*LISTEN"; then REDIS_FREE=false fi if netstat -an | grep -q ":$API_TEST.*LISTEN"; then API_FREE=false fi fi # If all ports are free, use this offset if [ "$POSTGRES_FREE" = "true" ] && [ "$REDIS_FREE" = "true" ] && [ "$API_FREE" = "true" ] && [ "$ORLEANS_SILO_FREE" = "true" ] && [ "$ORLEANS_GATEWAY_FREE" = "true" ]; then PORT_OFFSET=$offset PORT_OFFSET_FOUND=1 echo "✅ Found available port offset: $PORT_OFFSET" echo " PostgreSQL: $POSTGRES_TEST" echo " Redis: $REDIS_TEST" echo " API: $API_TEST" break fi done if [ "$PORT_OFFSET_FOUND" = "0" ]; then echo "❌ Could not find available port offset (checked offsets 1-100)" echo "💡 Try manually specifying a port offset: bash $0 $TASK_ID 10" exit 1 fi fi POSTGRES_PORT=$((5432 + PORT_OFFSET)) API_PORT=$((5000 + PORT_OFFSET)) REDIS_PORT=$((6379 + PORT_OFFSET)) # Convert to lowercase (compatible with bash 3.2+) DB_NAME="managing_$(echo "$TASK_ID" | tr '[:upper:]' '[:lower:]')" ORLEANS_DB_NAME="orleans_$(echo "$TASK_ID" | tr '[:upper:]' '[:lower:]')" echo "🚀 Starting Docker environment for task: $TASK_ID" echo "📊 Port offset: $PORT_OFFSET" echo "📊 PostgreSQL: localhost:$POSTGRES_PORT" echo "🔌 API: http://localhost:$API_PORT" echo "💾 Redis: localhost:$REDIS_PORT" echo "💾 Database: $DB_NAME" # Verify main database is accessible echo "🔍 Verifying main database connection..." if ! PGPASSWORD=postgres psql -h localhost -p 5432 -U postgres -d managing -c '\q' 2>/dev/null; then echo "❌ Cannot connect to main database at localhost:5432" echo "💡 Starting main database..." cd "$PROJECT_ROOT/src/Managing.Docker" # Use docker compose (newer) or docker-compose (older) if command -v docker &> /dev/null && docker compose version &> /dev/null; then docker compose -f docker-compose.yml -f docker-compose.local.yml up -d postgres else docker-compose -f docker-compose.yml -f docker-compose.local.yml up -d postgres fi echo "⏳ Waiting for database to start..." sleep 15 fi # Create compose file echo "📝 Creating Docker Compose file..." bash "$SCRIPT_DIR/create-task-compose.sh" "$TASK_ID" "$PORT_OFFSET" COMPOSE_FILE="$PROJECT_ROOT/src/Managing.Docker/docker-compose.task-${TASK_ID}.yml" # Start services (except API/Workers - we'll start them after DB copy) echo "🐳 Starting PostgreSQL, Redis..." cd "$PROJECT_ROOT/src/Managing.Docker" # Use docker compose (newer) or docker-compose (older) if command -v docker &> /dev/null && docker compose version &> /dev/null; then docker compose -f "$COMPOSE_FILE" up -d postgres-${TASK_ID} redis-${TASK_ID} else docker-compose -f "$COMPOSE_FILE" up -d postgres-${TASK_ID} redis-${TASK_ID} fi # Wait for PostgreSQL echo "⏳ Waiting for PostgreSQL..." for i in {1..60}; do if PGPASSWORD=postgres psql -h localhost -p $POSTGRES_PORT -U postgres -d postgres -c '\q' 2>/dev/null; then echo "✅ PostgreSQL is ready" break fi if [ $i -eq 60 ]; then echo "❌ PostgreSQL not ready after 60 attempts" if command -v docker &> /dev/null && docker compose version &> /dev/null; then docker compose -f "$COMPOSE_FILE" down else docker-compose -f "$COMPOSE_FILE" down fi exit 1 fi sleep 2 done # Copy database echo "📦 Copying database from main repo..." bash "$SCRIPT_DIR/copy-database-for-task.sh" "$TASK_ID" "localhost" "5432" "localhost" "$POSTGRES_PORT" if [ $? -eq 0 ]; then # Start API and Workers using dotnet run echo "🚀 Starting API and Workers with dotnet run..." bash "$SCRIPT_DIR/start-api-and-workers.sh" "$TASK_ID" "$PORT_OFFSET" echo "" echo "✅ Environment ready!" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "📊 API: http://localhost:$API_PORT" echo "💾 Database: $DB_NAME on port $POSTGRES_PORT" echo "💾 Redis: localhost:$REDIS_PORT" echo "🔧 To view API logs: tail -f .task-pids/api-${TASK_ID}.log" echo "🔧 To view Workers logs: tail -f .task-pids/workers-${TASK_ID}.log" echo "🔧 To stop: bash scripts/stop-task-docker.sh $TASK_ID" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" else echo "❌ Database copy failed" if command -v docker &> /dev/null && docker compose version &> /dev/null; then docker compose -f "$COMPOSE_FILE" down else docker-compose -f "$COMPOSE_FILE" down fi exit 1 fi