#!/bin/bash # scripts/vibe-kanban/vibe-setup.sh # Setup script for Vibe Kanban - sets up database and Docker services # This script runs in the "setup" section of Vibe Kanban # Usage: bash scripts/vibe-kanban/vibe-setup.sh [TASK_ID] [PORT_OFFSET] # TASK_ID can also come from environment variables or worktree path PORT_OFFSET=${2:-0} # Detect worktree root WORKTREE_ROOT="$(pwd)" # Check if we're in a nested structure (Vibe Kanban worktree) if [ -d "$WORKTREE_ROOT/managing-apps" ] && [ -d "$WORKTREE_ROOT/managing-apps/src/Managing.Api" ]; then WORKTREE_PROJECT_ROOT="$WORKTREE_ROOT/managing-apps" elif [ -d "$WORKTREE_ROOT/src/Managing.Api" ]; then WORKTREE_PROJECT_ROOT="$WORKTREE_ROOT" else echo "❌ Cannot find project structure in worktree" echo " Current directory: $WORKTREE_ROOT" exit 1 fi echo "📁 Worktree project root: $WORKTREE_PROJECT_ROOT" # TASK_ID file to ensure consistency across runs TASK_ID_FILE="$WORKTREE_PROJECT_ROOT/.vibe-task-id" # Try to get TASK_ID from various sources TASK_ID=$1 # First, check if we have a stored TASK_ID for this worktree (ensures consistency) if [ -z "$TASK_ID" ] && [ -f "$TASK_ID_FILE" ]; then TASK_ID=$(cat "$TASK_ID_FILE" 2>/dev/null | tr -d '[:space:]') if [ -n "$TASK_ID" ]; then echo "📋 Using stored TASK_ID from previous setup: $TASK_ID" fi fi if [ -z "$TASK_ID" ]; then # Try environment variables (Vibe Kanban might set these) if [ -n "$VIBE_TASK_ID" ]; then TASK_ID="$VIBE_TASK_ID" echo "📋 Found TASK_ID from VIBE_TASK_ID: $TASK_ID" elif [ -n "$VIBE_TASK_NAME" ]; then TASK_ID="$VIBE_TASK_NAME" echo "📋 Found TASK_ID from VIBE_TASK_NAME: $TASK_ID" elif [ -n "$TASK_ID_ENV" ]; then TASK_ID="$TASK_ID_ENV" echo "📋 Found TASK_ID from TASK_ID_ENV: $TASK_ID" elif [ -n "$TASK" ]; then TASK_ID="$TASK" echo "📋 Found TASK_ID from TASK: $TASK_ID" fi fi # Try to extract from worktree path (Vibe Kanban worktrees often contain task ID/name) if [ -z "$TASK_ID" ]; then # Extract task ID from worktree path (e.g., /path/to/worktrees/TASK-123/... or /path/to/worktrees/ticket-name/...) # Try UUID format first (Vibe Kanban might use UUIDs) DETECTED_TASK=$(echo "$WORKTREE_ROOT" | grep -oE '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}' | head -1) # If no UUID, try task ID pattern (e.g., DEV-123, TASK-456) if [ -z "$DETECTED_TASK" ]; then DETECTED_TASK=$(echo "$WORKTREE_ROOT" | grep -oE '[A-Z]+-[0-9]+' | head -1) fi # If still no match, try to get the last directory name (might be task name) if [ -z "$DETECTED_TASK" ]; then LAST_DIR=$(basename "$WORKTREE_ROOT") # Skip common directory names if [ "$LAST_DIR" != "managing-apps" ] && [ "$LAST_DIR" != "worktrees" ] && [ "$LAST_DIR" != "Projects" ]; then # Generate a numeric ID from the directory name (hash-based for consistency) # This ensures the same ticket name always gets the same numeric ID HASH=$(echo -n "$LAST_DIR" | shasum -a 256 | cut -d' ' -f1 | head -c 8) # Convert hex to decimal and take modulo to get a number between 1-9999 NUMERIC_ID=$((0x$HASH % 9999 + 1)) DETECTED_TASK="TASK-$NUMERIC_ID" echo "📋 Generated numeric TASK_ID from ticket name '$LAST_DIR': $DETECTED_TASK" fi fi if [ -n "$DETECTED_TASK" ]; then TASK_ID="$DETECTED_TASK" echo "📋 Detected TASK_ID from worktree path: $TASK_ID" fi fi # Fallback to numeric ID based on worktree path hash (ensures consistency) if [ -z "$TASK_ID" ]; then # Generate a consistent numeric ID from worktree path HASH=$(echo -n "$WORKTREE_ROOT" | shasum -a 256 | cut -d' ' -f1 | head -c 8) NUMERIC_ID=$((0x$HASH % 9999 + 1)) TASK_ID="TASK-$NUMERIC_ID" echo "📋 Generated consistent numeric TASK_ID from worktree path: $TASK_ID" fi # Store TASK_ID for future use (ensures same worktree always uses same TASK_ID) echo "$TASK_ID" > "$TASK_ID_FILE" echo "💾 Stored TASK_ID for future use: $TASK_ID" # Find main repository (try common locations) MAIN_REPO_PATHS=( "/Users/oda/Desktop/Projects/managing-apps" "$(git -C "$WORKTREE_PROJECT_ROOT" rev-parse --show-toplevel 2>/dev/null || echo '')" "$(dirname "$WORKTREE_ROOT" 2>/dev/null)/managing-apps" ) MAIN_REPO="" for path in "${MAIN_REPO_PATHS[@]}"; do if [ -n "$path" ] && [ -d "$path" ] && [ -d "$path/scripts" ] && [ -f "$path/scripts/start-task-docker.sh" ]; then MAIN_REPO="$path" break fi done if [ -z "$MAIN_REPO" ]; then echo "❌ Cannot find main repository with scripts" echo "💡 Tried:" for path in "${MAIN_REPO_PATHS[@]}"; do echo " - $path" done exit 1 fi echo "📁 Main repository: $MAIN_REPO" echo "🔧 Setting up environment for task: $TASK_ID" SCRIPT_DIR="$MAIN_REPO/scripts" # Auto-detect port offset if 0 is provided if [ "$PORT_OFFSET" = "0" ]; then echo "🔍 Auto-detecting available port offset..." 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)) POSTGRES_FREE=true REDIS_FREE=true API_FREE=true ORLEANS_SILO_FREE=true ORLEANS_GATEWAY_FREE=true 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 fi 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" break fi done if [ "$PORT_OFFSET_FOUND" = "0" ]; then echo "❌ Could not find available port offset (checked offsets 1-100)" exit 1 fi fi POSTGRES_PORT=$((5432 + PORT_OFFSET)) API_PORT=$((5000 + PORT_OFFSET)) REDIS_PORT=$((6379 + PORT_OFFSET)) DB_NAME="managing_$(echo "$TASK_ID" | tr '[:upper:]' '[:lower:]')" ORLEANS_DB_NAME="orleans_$(echo "$TASK_ID" | tr '[:upper:]' '[:lower:]')" # Extract TASK_SLOT from TASK_ID numeric part (e.g., TASK-5439 -> 5439) # This ensures unique Orleans ports for each task TASK_SLOT=$(echo "$TASK_ID" | grep -oE '[0-9]+' | head -1) if [ -z "$TASK_SLOT" ] || [ "$TASK_SLOT" = "0" ]; then # Fallback: use a hash-based numeric ID if TASK_ID doesn't contain numbers HASH=$(echo -n "$TASK_ID" | shasum -a 256 | cut -d' ' -f1 | head -c 8) TASK_SLOT=$((0x$HASH % 9999 + 1)) echo "⚠️ TASK_ID doesn't contain a number, generated TASK_SLOT: $TASK_SLOT" else echo "📊 TASK_SLOT extracted from TASK_ID: $TASK_SLOT" fi # Calculate Orleans ports based on TASK_SLOT ORLEANS_SILO_PORT=$((11111 + (TASK_SLOT - 1) * 10)) ORLEANS_GATEWAY_PORT=$((30000 + (TASK_SLOT - 1) * 10)) ORLEANS_DASHBOARD_PORT=$((9999 + (TASK_SLOT - 1))) echo "📊 Port offset: $PORT_OFFSET" echo "📊 PostgreSQL: localhost:$POSTGRES_PORT" echo "📊 Redis: localhost:$REDIS_PORT" echo "📊 API: http://localhost:$API_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 "$MAIN_REPO/src/Managing.Docker" 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="$MAIN_REPO/src/Managing.Docker/docker-compose.task-${TASK_ID}.yml" # Start services (PostgreSQL and Redis only) echo "🐳 Starting PostgreSQL and Redis..." cd "$MAIN_REPO/src/Managing.Docker" 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 [ $? -ne 0 ]; then 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 # Store configuration for later use (in worktree) SETUP_CONFIG_FILE="$WORKTREE_PROJECT_ROOT/.vibe-setup.env" echo "💾 Saving setup configuration..." cat > "$SETUP_CONFIG_FILE" <