From ab08e0241be9f577c3d7341edc22300ee1fa8614 Mon Sep 17 00:00:00 2001 From: cryptooda Date: Wed, 31 Dec 2025 04:36:20 +0700 Subject: [PATCH] Update Vibe Kanban setup and scripts - Added new entries to .gitignore for environment files and dynamically generated Docker Compose files. - Updated documentation to reflect the new path for the Vibe Kanban dev server script. - Enhanced task composition scripts to extract TASK_SLOT from TASK_ID, ensuring unique Orleans ports and preventing conflicts. - Removed the old vibe-dev-server script, consolidating functionality into the new structure. --- .cursor/commands/vibe-kanban.md | 40 ++ .gitignore | 6 + docs/API_AND_WORKERS_PROCESSES.md | 122 +++++++ docs/VIBE_KANBAN_DEV_SERVER_SETUP.md | 14 +- docs/VIBE_KANBAN_PROJECT_SETTINGS.md | 4 +- scripts/cleanup-api-workers.sh | 291 +++++++++++++++ scripts/create-task-compose.sh | 19 +- scripts/list-api-workers-processes.sh | 147 ++++++++ scripts/start-api-and-workers.sh | 25 +- scripts/vibe-kanban/README.md | 126 +++++++ scripts/vibe-kanban/cleanup-api-workers.sh | 361 +++++++++++++++++++ scripts/{ => vibe-kanban}/vibe-dev-server.sh | 103 +++++- scripts/vibe-kanban/vibe-setup.sh | 312 ++++++++++++++++ 13 files changed, 1535 insertions(+), 35 deletions(-) create mode 100644 .cursor/commands/vibe-kanban.md create mode 100644 docs/API_AND_WORKERS_PROCESSES.md create mode 100644 scripts/cleanup-api-workers.sh create mode 100755 scripts/list-api-workers-processes.sh create mode 100644 scripts/vibe-kanban/README.md create mode 100755 scripts/vibe-kanban/cleanup-api-workers.sh rename scripts/{ => vibe-kanban}/vibe-dev-server.sh (54%) create mode 100755 scripts/vibe-kanban/vibe-setup.sh diff --git a/.cursor/commands/vibe-kanban.md b/.cursor/commands/vibe-kanban.md new file mode 100644 index 00000000..82c1ca26 --- /dev/null +++ b/.cursor/commands/vibe-kanban.md @@ -0,0 +1,40 @@ +# vibe-kanban + +Quick reference for Vibe Kanban MCP interactions. + +## Available Projects + +- `gmx-interface`: 574c1123-facf-4a8d-a6dd-1789db369fbf +- `kaigen-web`: cd0907b5-0933-4f6c-9516-aac4d5d360bc +- `managing-apps`: 1a4fdbff-8b23-49d5-9953-2476846cbcc2 + +## Common Operations + +### List Tasks +List all tasks in a project: +- Use `list_tasks` with `project_id: "1a4fdbff-8b23-49d5-9953-2476846cbcc2"` for managing-apps + +### Create Task +Create new task: +- Use `create_task` with `project_id` and `title` (description optional) + +### Update Task +Update task status/title/description: +- Use `update_task` with `task_id` and optional `status`, `title`, `description` +- Statuses: `todo`, `inprogress`, `inreview`, `done`, `cancelled` + +### Get Task Details +Get full task info: +- Use `get_task` with `task_id` + +### Delete Task +Remove task: +- Use `delete_task` with `task_id` + +## Notes + +- Always pass `project_id` or `task_id` where required +- Use `list_projects` to get project IDs +- Use `list_tasks` to get task IDs +- See `docs/VIBE_KANBAN_QUICK_START.md` for full documentation + diff --git a/.gitignore b/.gitignore index 4f287225..eb0ee4e6 100644 --- a/.gitignore +++ b/.gitignore @@ -395,3 +395,9 @@ scripts/privy/privy-users.csv .vibe-kanban/*.sqlite # Task process PID files and logs .task-pids/ + +.vibe-setup.env +.vibe-task-id + +# Task-specific Docker Compose files (generated dynamically) +src/Managing.Docker/docker-compose.task-*.yml diff --git a/docs/API_AND_WORKERS_PROCESSES.md b/docs/API_AND_WORKERS_PROCESSES.md new file mode 100644 index 00000000..8e969e36 --- /dev/null +++ b/docs/API_AND_WORKERS_PROCESSES.md @@ -0,0 +1,122 @@ +# API and Workers Processes + +This document lists all processes that run when the API and Workers are started. + +## Process Hierarchy + +### 1. API Process (`dotnet run` for Managing.Api) + +**Parent Process:** +- `dotnet run` - The .NET CLI process that starts the API + - PID stored in: `.task-pids/api-${TASK_ID}.pid` + +**Child Process:** +- `Managing.Api` executable - The actual API application + - Location: `src/Managing.Api/bin/Debug/net8.0/Managing.Api` + - This is the main ASP.NET Core application + +**Background Services (within the API process):** +All of these run as `IHostedService` within the same API process: + +1. **GrainInitializer** - Initializes Orleans grains +2. **DiscordService** - Discord bot service +3. **PricesFifteenMinutesWorker** - Updates prices every 15 minutes (if enabled) +4. **PricesOneHourWorker** - Updates prices every hour (if enabled) +5. **PricesFourHoursWorker** - Updates prices every 4 hours (if enabled) +6. **PricesOneDayWorker** - Updates prices every day (if enabled) +7. **PricesFiveMinutesWorker** - Updates prices every 5 minutes (if enabled) +8. **SpotlightWorker** - Spotlight feature worker (if enabled) +9. **TraderWatcher** - Watches traders (if enabled) +10. **LeaderboardWorker** - Updates leaderboard (if enabled) +11. **FundingRatesWatcher** - Watches funding rates (if enabled) +12. **GeneticAlgorithmWorker** - Genetic algorithm worker (if enabled) +13. **NotifyBundleBacktestWorker** - Notifies about bundle backtests (if enabled) + +**Orleans Components (within the API process):** +- Orleans Silo - Runs on port `11111 + (TASK_SLOT - 1) * 10` +- Orleans Gateway - Runs on port `30000 + (TASK_SLOT - 1) * 10` +- Orleans Dashboard - Runs on port `9999 + (TASK_SLOT - 1)` (development only) + +### 2. Workers Process (`dotnet run` for Managing.Workers) + +**Parent Process:** +- `dotnet run` - The .NET CLI process that starts the Workers + - PID stored in: `.task-pids/workers-${TASK_ID}.pid` + +**Child Process:** +- `Managing.Workers` executable - The actual Workers application + - Location: `src/Managing.Workers/bin/Debug/net8.0/Managing.Workers` + - This is a .NET Host application + +**Background Services (within the Workers process):** +All of these run as `BackgroundService` within the same Workers process: + +1. **BacktestComputeWorker** - Processes backtest jobs (if enabled) +2. **GeneticComputeWorker** - Processes genetic algorithm jobs (if enabled) +3. **BundleBacktestHealthCheckWorker** - Health check for bundle backtests (if enabled, only on TASK_SLOT=1) + +## Process Management + +### Starting Processes +Processes are started by `scripts/start-api-and-workers.sh`: +- API: `cd src/Managing.Api && dotnet run &` +- Workers: `cd src/Managing.Workers && dotnet run &` + +### Stopping Processes +Processes are stopped by `scripts/stop-task-docker.sh` or the cleanup script: +1. Read PID from `.task-pids/api-${TASK_ID}.pid` +2. Kill the parent `dotnet run` process +3. Kill any orphaned child processes +4. Read PID from `.task-pids/workers-${TASK_ID}.pid` +5. Kill the parent `dotnet run` process +6. Kill any orphaned child processes + +### Finding All Related Processes + +To find all processes related to a specific task: + +```bash +# Find by PID file +TASK_ID="YOUR_TASK_ID" +API_PID=$(cat .task-pids/api-${TASK_ID}.pid 2>/dev/null) +WORKERS_PID=$(cat .task-pids/workers-${TASK_ID}.pid 2>/dev/null) + +# Find child processes +ps -ef | grep $API_PID +ps -ef | grep $WORKERS_PID + +# Find by executable name +ps aux | grep "Managing.Api" +ps aux | grep "Managing.Workers" +ps aux | grep "dotnet run" +``` + +### Finding Processes by Port + +```bash +# Find API process by port +lsof -i :5000 # Default API port +lsof -i :$((5000 + PORT_OFFSET)) # With port offset + +# Find Orleans processes by port +lsof -i :11111 # Orleans Silo (default) +lsof -i :30000 # Orleans Gateway (default) +``` + +## Important Notes + +1. **Single Process Architecture**: All background services run within the same process as the API or Workers. They are not separate processes. + +2. **PID Files**: The PID files store the parent `dotnet run` process PID, not the child executable PID. + +3. **Orphaned Processes**: If the parent `dotnet run` process is killed, the child `Managing.Api` or `Managing.Workers` process may become orphaned. The cleanup script should handle this. + +4. **Port Conflicts**: Each task uses unique ports based on `PORT_OFFSET`: + - API: `5000 + PORT_OFFSET` + - PostgreSQL: `5432 + PORT_OFFSET` + - Redis: `6379 + PORT_OFFSET` + - Orleans Silo: `11111 + (TASK_SLOT - 1) * 10` + - Orleans Gateway: `30000 + (TASK_SLOT - 1) * 10` + +5. **Worker Consolidation**: Most workers have been consolidated into the API process. The separate `Managing.Workers` project now only runs compute-intensive workers (BacktestComputeWorker, GeneticComputeWorker). + diff --git a/docs/VIBE_KANBAN_DEV_SERVER_SETUP.md b/docs/VIBE_KANBAN_DEV_SERVER_SETUP.md index 60388cc4..7f8edc5d 100644 --- a/docs/VIBE_KANBAN_DEV_SERVER_SETUP.md +++ b/docs/VIBE_KANBAN_DEV_SERVER_SETUP.md @@ -9,7 +9,7 @@ Vibe Kanban runs the dev server script from a different working directory than e In the Vibe Kanban dev server script field, use the **absolute path** to the wrapper script: ```bash -bash /Users/oda/Desktop/Projects/managing-apps/scripts/vibe-dev-server.sh +bash /Users/oda/Desktop/Projects/managing-apps/scripts/vibe-kanban/vibe-dev-server.sh ``` ## Alternative: If Relative Path is Required @@ -17,7 +17,7 @@ bash /Users/oda/Desktop/Projects/managing-apps/scripts/vibe-dev-server.sh If Vibe Kanban requires a relative path and you know it runs from `/Users/oda/Desktop`, use: ```bash -bash Projects/managing-apps/scripts/vibe-dev-server.sh +bash Projects/managing-apps/scripts/vibe-kanban/vibe-dev-server.sh ``` ## What the Wrapper Script Does @@ -36,7 +36,7 @@ You can test the script manually: ```bash # From any directory -bash /Users/oda/Desktop/Projects/managing-apps/scripts/vibe-dev-server.sh TEST-001 0 +bash /Users/oda/Desktop/Projects/managing-apps/scripts/vibe-kanban/vibe-dev-server.sh TEST-001 0 ``` ## Debug Output @@ -54,17 +54,17 @@ This helps identify if Vibe Kanban is running from an unexpected directory. 1. Verify the script exists: ```bash - ls -la /Users/oda/Desktop/Projects/managing-apps/scripts/vibe-dev-server.sh + ls -la /Users/oda/Desktop/Projects/managing-apps/scripts/vibe-kanban/vibe-dev-server.sh ``` 2. Check permissions: ```bash - chmod +x /Users/oda/Desktop/Projects/managing-apps/scripts/vibe-dev-server.sh + chmod +x /Users/oda/Desktop/Projects/managing-apps/scripts/vibe-kanban/vibe-dev-server.sh ``` 3. Try running it directly: ```bash - bash /Users/oda/Desktop/Projects/managing-apps/scripts/vibe-dev-server.sh TEST-001 + bash /Users/oda/Desktop/Projects/managing-apps/scripts/vibe-kanban/vibe-dev-server.sh TEST-001 ``` ### Error: "Cannot change to project root" @@ -76,7 +76,7 @@ This helps identify if Vibe Kanban is running from an unexpected directory. **Dev Server Script Field:** ``` -bash /Users/oda/Desktop/Projects/managing-apps/scripts/vibe-dev-server.sh +bash /Users/oda/Desktop/Projects/managing-apps/scripts/vibe-kanban/vibe-dev-server.sh ``` **Health Check URL:** diff --git a/docs/VIBE_KANBAN_PROJECT_SETTINGS.md b/docs/VIBE_KANBAN_PROJECT_SETTINGS.md index eb0c830a..230a333b 100644 --- a/docs/VIBE_KANBAN_PROJECT_SETTINGS.md +++ b/docs/VIBE_KANBAN_PROJECT_SETTINGS.md @@ -29,12 +29,12 @@ In the **QA** or **Testing** section, configure: **Dev Environment Script:** ``` -managing-apps/scripts/vibe-dev-server.sh +managing-apps/scripts/vibe-kanban/vibe-dev-server.sh ``` **Or use absolute path:** ``` -/Users/oda/Desktop/Projects/managing-apps/scripts/vibe-dev-server.sh +/Users/oda/Desktop/Projects/managing-apps/scripts/vibe-kanban/vibe-dev-server.sh ``` **Note:** This path is relative to `/Users/oda/Desktop/Projects` (the Projects folder root) diff --git a/scripts/cleanup-api-workers.sh b/scripts/cleanup-api-workers.sh new file mode 100644 index 00000000..60d644ed --- /dev/null +++ b/scripts/cleanup-api-workers.sh @@ -0,0 +1,291 @@ +#!/bin/bash +# scripts/cleanup-api-workers.sh +# Cleanup script for Vibe Kanban - stops API and Workers processes only +# Usage: bash scripts/cleanup-api-workers.sh + +TASK_ID=$1 + +# Try to get TASK_ID from various sources +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 "$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 + +# Determine project root +if [ -n "$VIBE_WORKTREE_ROOT" ] && [ -d "$VIBE_WORKTREE_ROOT/src/Managing.Api" ]; then + PROJECT_ROOT="$VIBE_WORKTREE_ROOT" + echo "๐Ÿ“ Using Vibe Kanban worktree: $PROJECT_ROOT" +elif [ -d "$(pwd)/scripts" ] && [ -f "$(pwd)/scripts/start-api-and-workers.sh" ]; then + PROJECT_ROOT="$(pwd)" + echo "๐Ÿ“ Using current directory: $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 project root" + exit 1 + fi +fi + +# If TASK_ID still not found, try to detect from worktree path or PID files +if [ -z "$TASK_ID" ]; then + # Try to extract from worktree path (Vibe Kanban worktrees often contain task ID) + if [ -n "$VIBE_WORKTREE_ROOT" ]; then + WORKTREE_PATH="$VIBE_WORKTREE_ROOT" + # Try to extract task ID from path (e.g., /path/to/worktrees/TASK-123/...) + DETECTED_TASK=$(echo "$WORKTREE_PATH" | grep -oE '[A-Z]+-[0-9]+' | head -1) + if [ -n "$DETECTED_TASK" ]; then + TASK_ID="$DETECTED_TASK" + echo "๐Ÿ“‹ Detected TASK_ID from worktree path: $TASK_ID" + fi + fi + + # Try to find from PID files in worktree + if [ -z "$TASK_ID" ] && [ -n "$VIBE_WORKTREE_ROOT" ]; then + PID_DIR_CHECK="$VIBE_WORKTREE_ROOT/.task-pids" + if [ -d "$PID_DIR_CHECK" ]; then + # Find the most recent PID file with a running process + for pid_file in $(ls -t "$PID_DIR_CHECK"/*.pid 2>/dev/null); do + pid=$(cat "$pid_file" 2>/dev/null | tr -d '[:space:]') + if [ -n "$pid" ] && ps -p "$pid" > /dev/null 2>&1; then + # Extract task ID from filename (e.g., api-DEV-123.pid -> DEV-123) + DETECTED_TASK=$(basename "$pid_file" .pid | sed 's/^api-//; s/^workers-//') + if [ -n "$DETECTED_TASK" ]; then + TASK_ID="$DETECTED_TASK" + echo "๐Ÿ“‹ Detected TASK_ID from running process PID file: $TASK_ID" + break + fi + fi + done + fi + fi + + # Try to find from PID files in main repo if still not found + if [ -z "$TASK_ID" ]; then + PID_DIR_CHECK="$PROJECT_ROOT/.task-pids" + if [ -d "$PID_DIR_CHECK" ]; then + # Find the most recent PID file with a running process + for pid_file in $(ls -t "$PID_DIR_CHECK"/*.pid 2>/dev/null); do + pid=$(cat "$pid_file" 2>/dev/null | tr -d '[:space:]') + if [ -n "$pid" ] && ps -p "$pid" > /dev/null 2>&1; then + # Extract task ID from filename (e.g., api-DEV-123.pid -> DEV-123) + DETECTED_TASK=$(basename "$pid_file" .pid | sed 's/^api-//; s/^workers-//') + if [ -n "$DETECTED_TASK" ]; then + TASK_ID="$DETECTED_TASK" + echo "๐Ÿ“‹ Detected TASK_ID from running process PID file: $TASK_ID" + break + fi + fi + done + fi + fi + + # Try to find from current directory if it's a worktree + if [ -z "$TASK_ID" ]; then + CURRENT_DIR="$(pwd)" + DETECTED_TASK=$(echo "$CURRENT_DIR" | grep -oE '[A-Z]+-[0-9]+' | head -1) + if [ -n "$DETECTED_TASK" ]; then + TASK_ID="$DETECTED_TASK" + echo "๐Ÿ“‹ Detected TASK_ID from current directory: $TASK_ID" + fi + fi +fi + +PID_DIR="$PROJECT_ROOT/.task-pids" +API_PID_FILE="$PID_DIR/api-${TASK_ID}.pid" +WORKERS_PID_FILE="$PID_DIR/workers-${TASK_ID}.pid" + +if [ -z "$TASK_ID" ]; then + echo "" + echo "โŒ Error: TASK_ID is required but could not be determined" + echo "" + echo "๐Ÿ’ก Usage: $0 " + echo "๐Ÿ’ก Or set one of these environment variables:" + echo " - VIBE_TASK_ID" + echo " - TASK_ID_ENV" + echo " - TASK" + echo "" + echo "๐Ÿ’ก Or ensure you're running from a Vibe Kanban worktree with task ID in the path" + echo "" + echo "๐Ÿ” Debug information:" + echo " Current directory: $(pwd)" + echo " VIBE_WORKTREE_ROOT: ${VIBE_WORKTREE_ROOT:-not set}" + echo " PROJECT_ROOT: $PROJECT_ROOT" + if [ -d "$PID_DIR" ]; then + echo " Available PID files in $PID_DIR:" + ls -1 "$PID_DIR"/*.pid 2>/dev/null | head -5 | while read file; do + pid=$(cat "$file" 2>/dev/null | tr -d '[:space:]') + task=$(basename "$file" .pid | sed 's/^api-//; s/^workers-//') + if [ -n "$pid" ] && ps -p "$pid" > /dev/null 2>&1; then + echo " โœ… $file (PID: $pid, Task: $task) - RUNNING" + else + echo " โš ๏ธ $file (PID: $pid, Task: $task) - not running" + fi + done || echo " (none found)" + fi + echo "" + echo "๐Ÿ’ก To clean up a specific task, run:" + echo " $0 " + echo "" + echo "๐Ÿ’ก Or set VIBE_TASK_ID environment variable before running the script" + exit 1 +fi + +echo "๐Ÿงน Cleaning up API and Workers for task: $TASK_ID" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + +# Function to kill process and its children +kill_process_tree() { + local pid=$1 + local name=$2 + + if [ -z "$pid" ] || [ "$pid" = "0" ]; then + return 0 + fi + + if ! ps -p "$pid" > /dev/null 2>&1; then + return 0 + fi + + echo " ๐Ÿ›‘ Stopping $name (PID: $pid)..." + + # First, try graceful shutdown + kill "$pid" 2>/dev/null || true + sleep 2 + + # Check if still running + if ps -p "$pid" > /dev/null 2>&1; then + echo " โš ๏ธ Process still running, force killing..." + kill -9 "$pid" 2>/dev/null || true + sleep 1 + fi + + # Kill any child processes + local child_pids=$(pgrep -P "$pid" 2>/dev/null) + if [ -n "$child_pids" ]; then + for child_pid in $child_pids; do + echo " ๐Ÿ›‘ Stopping child process (PID: $child_pid)..." + kill "$child_pid" 2>/dev/null || true + sleep 1 + if ps -p "$child_pid" > /dev/null 2>&1; then + kill -9 "$child_pid" 2>/dev/null || true + fi + done + fi + + # Verify process is stopped + if ps -p "$pid" > /dev/null 2>&1; then + echo " โš ๏ธ Warning: Process $pid may still be running" + return 1 + else + echo " โœ… $name stopped" + return 0 + fi +} + +# Function to find and kill orphaned processes by name +kill_orphaned_processes() { + local task_id=$1 + local process_name=$2 + local found_any=false + + # Find processes that match the executable name and worktree path + local processes=$(ps aux | grep "$process_name" | grep -v grep | grep -E "worktree|$task_id" || true) + + if [ -n "$processes" ]; then + echo " ๐Ÿ” Found orphaned $process_name processes:" + echo "$processes" | while read line; do + local pid=$(echo "$line" | awk '{print $2}') + if [ -n "$pid" ] && ps -p "$pid" > /dev/null 2>&1; then + echo " ๐Ÿ›‘ Killing orphaned process (PID: $pid)..." + kill "$pid" 2>/dev/null || true + sleep 1 + if ps -p "$pid" > /dev/null 2>&1; then + kill -9 "$pid" 2>/dev/null || true + fi + found_any=true + fi + done + fi +} + +# Stop API process +echo "๐Ÿ“Š Stopping API process..." +if [ -f "$API_PID_FILE" ]; then + API_PID=$(cat "$API_PID_FILE" 2>/dev/null | tr -d '[:space:]') + if [ -n "$API_PID" ] && [ "$API_PID" != "0" ]; then + kill_process_tree "$API_PID" "API" + else + echo " โš ๏ธ Invalid PID in file: $API_PID_FILE" + fi + rm -f "$API_PID_FILE" +else + echo " โš ๏ธ API PID file not found: $API_PID_FILE" +fi + +# Kill orphaned Managing.Api processes +kill_orphaned_processes "$TASK_ID" "Managing.Api" + +# Stop Workers process +echo "" +echo "๐Ÿ“Š Stopping Workers process..." +if [ -f "$WORKERS_PID_FILE" ]; then + WORKERS_PID=$(cat "$WORKERS_PID_FILE" 2>/dev/null | tr -d '[:space:]') + if [ -n "$WORKERS_PID" ] && [ "$WORKERS_PID" != "0" ]; then + kill_process_tree "$WORKERS_PID" "Workers" + else + echo " โš ๏ธ Invalid PID in file: $WORKERS_PID_FILE" + fi + rm -f "$WORKERS_PID_FILE" +else + echo " โš ๏ธ Workers PID file not found: $WORKERS_PID_FILE" +fi + +# Kill orphaned Managing.Workers processes +kill_orphaned_processes "$TASK_ID" "Managing.Workers" + +# Kill orphaned dotnet run processes that might be related +echo "" +echo "๐Ÿ“Š Checking for orphaned dotnet run processes..." +DOTNET_RUN_PIDS=$(ps aux | grep "dotnet run" | grep -v grep | awk '{print $2}' || true) +if [ -n "$DOTNET_RUN_PIDS" ]; then + for pid in $DOTNET_RUN_PIDS; do + # Check if this dotnet run is a parent of Managing.Api or Managing.Workers + local has_api_child=$(pgrep -P "$pid" | xargs ps -p 2>/dev/null | grep -c "Managing.Api" || echo "0") + local has_workers_child=$(pgrep -P "$pid" | xargs ps -p 2>/dev/null | grep -c "Managing.Workers" || echo "0") + + if [ "$has_api_child" != "0" ] || [ "$has_workers_child" != "0" ]; then + echo " ๐Ÿ›‘ Killing orphaned dotnet run process (PID: $pid)..." + kill "$pid" 2>/dev/null || true + sleep 1 + if ps -p "$pid" > /dev/null 2>&1; then + kill -9 "$pid" 2>/dev/null || true + fi + fi + done +fi + +# Clean up log files (optional - comment out if you want to keep logs) +# echo "" +# echo "๐Ÿ“Š Cleaning up log files..." +# rm -f "$PID_DIR/api-${TASK_ID}.log" "$PID_DIR/workers-${TASK_ID}.log" 2>/dev/null || true + +echo "" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" +echo "โœ… Cleanup complete for task: $TASK_ID" +echo "" +echo "๐Ÿ’ก Note: Log files are preserved in: $PID_DIR" +echo "๐Ÿ’ก To remove log files, uncomment the cleanup section in the script" + diff --git a/scripts/create-task-compose.sh b/scripts/create-task-compose.sh index 69e16a36..8b073fe6 100755 --- a/scripts/create-task-compose.sh +++ b/scripts/create-task-compose.sh @@ -17,8 +17,17 @@ DB_NAME="managing_$(echo "$TASK_ID" | tr '[:upper:]' '[:lower:]')" ORLEANS_DB_NAME="orleans_$(echo "$TASK_ID" | tr '[:upper:]' '[:lower:]')" TASK_ID_LOWER="$(echo "$TASK_ID" | tr '[:upper:]' '[:lower:]')" -# Calculate unique task slot based on port offset (for Orleans clustering) -TASK_SLOT=$((PORT_OFFSET / 10 + 1)) +# Extract TASK_SLOT from TASK_ID numeric part (e.g., TASK-5439 -> 5439) +# This ensures unique Orleans ports for each task and prevents port conflicts +TASK_SLOT=$(echo "$TASK_ID" | grep -oE '[0-9]+' | head -1) +if [ -z "$TASK_SLOT" ] || [ "$TASK_SLOT" = "0" ]; then + # Fallback: use port offset calculation if TASK_ID doesn't contain numbers + TASK_SLOT=$((PORT_OFFSET / 10 + 1)) +fi + +# Calculate Orleans ports based on TASK_SLOT (for display purposes) +ORLEANS_SILO_PORT_CALC=$((11111 + (TASK_SLOT - 1) * 10)) +ORLEANS_GATEWAY_PORT_CALC=$((30000 + (TASK_SLOT - 1) * 10)) SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" @@ -75,8 +84,8 @@ echo "โœ… Created $COMPOSE_FILE" echo " PostgreSQL: localhost:$POSTGRES_PORT" echo " Redis: localhost:$REDIS_PORT" echo " API will run via dotnet run on port: $API_PORT" -echo " Orleans Silo: localhost:$ORLEANS_SILO_PORT" -echo " Orleans Gateway: localhost:$ORLEANS_GATEWAY_PORT" +echo " Orleans Silo: localhost:$ORLEANS_SILO_PORT_CALC (based on TASK_SLOT=$TASK_SLOT)" +echo " Orleans Gateway: localhost:$ORLEANS_GATEWAY_PORT_CALC (based on TASK_SLOT=$TASK_SLOT)" echo " InfluxDB: Using main instance at localhost:8086" -echo " Task Slot: $TASK_SLOT" +echo " Task Slot: $TASK_SLOT (extracted from TASK_ID: $TASK_ID)" diff --git a/scripts/list-api-workers-processes.sh b/scripts/list-api-workers-processes.sh new file mode 100755 index 00000000..f52e9883 --- /dev/null +++ b/scripts/list-api-workers-processes.sh @@ -0,0 +1,147 @@ +#!/bin/bash +# scripts/list-api-workers-processes.sh +# Lists all processes related to API and Workers for a given task + +TASK_ID=$1 + +# Try to get TASK_ID from environment if not provided +if [ -z "$TASK_ID" ] && [ -n "$VIBE_TASK_ID" ]; then + TASK_ID="$VIBE_TASK_ID" +fi + +# Determine project root +if [ -n "$VIBE_WORKTREE_ROOT" ] && [ -d "$VIBE_WORKTREE_ROOT/src/Managing.Api" ]; then + PROJECT_ROOT="$VIBE_WORKTREE_ROOT" +elif [ -d "$(pwd)/scripts" ] && [ -f "$(pwd)/scripts/start-api-and-workers.sh" ]; then + PROJECT_ROOT="$(pwd)" +else + MAIN_REPO="/Users/oda/Desktop/Projects/managing-apps" + if [ -d "$MAIN_REPO/scripts" ]; then + PROJECT_ROOT="$MAIN_REPO" + else + echo "โŒ Error: Cannot find project root" + exit 1 + fi +fi + +PID_DIR="$PROJECT_ROOT/.task-pids" +API_PID_FILE="$PID_DIR/api-${TASK_ID}.pid" +WORKERS_PID_FILE="$PID_DIR/workers-${TASK_ID}.pid" + +if [ -z "$TASK_ID" ]; then + echo "๐Ÿ“‹ Listing ALL API and Workers processes..." + echo "" + echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + echo "๐Ÿ” All dotnet run processes:" + ps aux | grep "dotnet run" | grep -v grep || echo " (none found)" + echo "" + echo "๐Ÿ” All Managing.Api processes:" + ps aux | grep "Managing.Api" | grep -v grep || echo " (none found)" + echo "" + echo "๐Ÿ” All Managing.Workers processes:" + ps aux | grep "Managing.Workers" | grep -v grep || echo " (none found)" + echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + echo "" + echo "๐Ÿ’ก To list processes for a specific task, run: $0 " + exit 0 +fi + +echo "๐Ÿ“‹ Listing processes for task: $TASK_ID" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + +# Check API processes +if [ -f "$API_PID_FILE" ]; then + API_PID=$(cat "$API_PID_FILE") + echo "๐Ÿ“Š API Process (from PID file):" + echo " PID File: $API_PID_FILE" + echo " Stored PID: $API_PID" + + if ps -p "$API_PID" > /dev/null 2>&1; then + echo " โœ… Process is running" + echo " Process details:" + ps -p "$API_PID" -o pid,ppid,user,%cpu,%mem,etime,command | head -2 + echo "" + + # Find child processes + echo " Child processes:" + CHILD_PIDS=$(pgrep -P "$API_PID" 2>/dev/null) + if [ -n "$CHILD_PIDS" ]; then + for CHILD_PID in $CHILD_PIDS; do + ps -p "$CHILD_PID" -o pid,ppid,user,%cpu,%mem,etime,command 2>/dev/null | tail -1 + done + else + echo " (no child processes found)" + fi + else + echo " โš ๏ธ Process not running (stale PID file)" + fi +else + echo "๐Ÿ“Š API Process:" + echo " โš ๏ธ PID file not found: $API_PID_FILE" +fi + +echo "" + +# Check Workers processes +if [ -f "$WORKERS_PID_FILE" ]; then + WORKERS_PID=$(cat "$WORKERS_PID_FILE") + echo "๐Ÿ“Š Workers Process (from PID file):" + echo " PID File: $WORKERS_PID_FILE" + echo " Stored PID: $WORKERS_PID" + + if ps -p "$WORKERS_PID" > /dev/null 2>&1; then + echo " โœ… Process is running" + echo " Process details:" + ps -p "$WORKERS_PID" -o pid,ppid,user,%cpu,%mem,etime,command | head -2 + echo "" + + # Find child processes + echo " Child processes:" + CHILD_PIDS=$(pgrep -P "$WORKERS_PID" 2>/dev/null) + if [ -n "$CHILD_PIDS" ]; then + for CHILD_PID in $CHILD_PIDS; do + ps -p "$CHILD_PID" -o pid,ppid,user,%cpu,%mem,etime,command 2>/dev/null | tail -1 + done + else + echo " (no child processes found)" + fi + else + echo " โš ๏ธ Process not running (stale PID file)" + fi +else + echo "๐Ÿ“Š Workers Process:" + echo " โš ๏ธ PID file not found: $WORKERS_PID_FILE" +fi + +echo "" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + +# Find processes by executable name (in case PID files are missing) +echo "๐Ÿ” Searching for processes by executable name:" +echo "" + +API_PROCESSES=$(ps aux | grep "Managing.Api" | grep -v grep | grep "$TASK_ID\|worktree" || true) +if [ -n "$API_PROCESSES" ]; then + echo "๐Ÿ“Š Found Managing.Api processes:" + echo "$API_PROCESSES" | while read line; do + echo " $line" + done +else + echo "๐Ÿ“Š Managing.Api processes: (none found)" +fi + +echo "" + +WORKERS_PROCESSES=$(ps aux | grep "Managing.Workers" | grep -v grep | grep "$TASK_ID\|worktree" || true) +if [ -n "$WORKERS_PROCESSES" ]; then + echo "๐Ÿ“Š Found Managing.Workers processes:" + echo "$WORKERS_PROCESSES" | while read line; do + echo " $line" + done +else + echo "๐Ÿ“Š Managing.Workers processes: (none found)" +fi + +echo "" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + diff --git a/scripts/start-api-and-workers.sh b/scripts/start-api-and-workers.sh index 26854cf6..2d59c989 100755 --- a/scripts/start-api-and-workers.sh +++ b/scripts/start-api-and-workers.sh @@ -29,8 +29,23 @@ ORLEANS_GATEWAY_PORT=$((30000 + PORT_OFFSET)) DB_NAME="managing_$(echo "$TASK_ID" | tr '[:upper:]' '[:lower:]')" ORLEANS_DB_NAME="orleans_$(echo "$TASK_ID" | tr '[:upper:]' '[:lower:]')" -# Calculate unique task slot based on port offset (for Orleans clustering) -TASK_SLOT=$((PORT_OFFSET / 10 + 1)) +# Extract TASK_SLOT from TASK_ID numeric part (e.g., TASK-5439 -> 5439) +# This ensures unique Orleans ports for each task and prevents port conflicts +# Use TASK_SLOT from environment if already set (from vibe-setup.sh config), otherwise extract from TASK_ID +if [ -z "$TASK_SLOT" ] || [ "$TASK_SLOT" = "0" ]; then + TASK_SLOT=$(echo "$TASK_ID" | grep -oE '[0-9]+' | head -1) + if [ -z "$TASK_SLOT" ] || [ "$TASK_SLOT" = "0" ]; then + # Fallback: use port offset calculation if TASK_ID doesn't contain numbers + TASK_SLOT=$((PORT_OFFSET / 10 + 1)) + echo "โš ๏ธ TASK_ID doesn't contain a number, using port offset-based TASK_SLOT: $TASK_SLOT" + else + echo "๐Ÿ“Š TASK_SLOT extracted from TASK_ID: $TASK_SLOT" + fi +else + echo "๐Ÿ“Š Using TASK_SLOT from configuration: $TASK_SLOT" +fi + +# TASK_SLOT determines Orleans ports: Silo = 11111 + (TASK_SLOT - 1) * 10, Gateway = 30000 + (TASK_SLOT - 1) * 10 # PID files for process management PID_DIR="$PROJECT_ROOT/.task-pids" @@ -44,6 +59,10 @@ export ASPNETCORE_URLS="http://localhost:${API_PORT}" export RUN_ORLEANS_GRAINS=true export SILO_ROLE=Trading export TASK_SLOT=${TASK_SLOT} +export TASK_ID=${TASK_ID} +export PORT_OFFSET=${PORT_OFFSET} +# Orleans ports are calculated from TASK_SLOT in the application +# These exports are kept for reference but not used (TASK_SLOT is the source of truth) export PostgreSql__ConnectionString="Host=localhost;Port=${POSTGRES_PORT};Database=${DB_NAME};Username=postgres;Password=postgres" export PostgreSql__Orleans="Host=localhost;Port=${POSTGRES_PORT};Database=${ORLEANS_DB_NAME};Username=postgres;Password=postgres" export InfluxDb__Url="http://localhost:8086/" @@ -75,6 +94,7 @@ fi echo "๐Ÿš€ Starting API on port $API_PORT..." echo "๐Ÿ“ Running from: $PROJECT_ROOT" cd "$PROJECT_ROOT/src/Managing.Api" +# Write all output to log file (warnings will be filtered when displaying) dotnet run > "$PID_DIR/api-${TASK_ID}.log" 2>&1 & API_PID=$! echo $API_PID > "$API_PID_FILE" @@ -86,6 +106,7 @@ sleep 3 echo "๐Ÿš€ Starting Workers..." cd "$PROJECT_ROOT/src/Managing.Workers" # Set workers environment variables (separate from API) +# Write all output to log file (warnings will be filtered when displaying) ASPNETCORE_ENVIRONMENT=Development \ PostgreSql__ConnectionString="Host=localhost;Port=${POSTGRES_PORT};Database=${DB_NAME};Username=postgres;Password=postgres" \ InfluxDb__Url="http://localhost:8086/" \ diff --git a/scripts/vibe-kanban/README.md b/scripts/vibe-kanban/README.md new file mode 100644 index 00000000..f0c5fa73 --- /dev/null +++ b/scripts/vibe-kanban/README.md @@ -0,0 +1,126 @@ +# Vibe Kanban Scripts + +This directory contains all scripts specifically for Vibe Kanban integration. + +## Scripts + +### `vibe-setup.sh` +**Purpose:** Sets up the database and Docker services for a Vibe Kanban task environment. + +**Usage:** +```bash +bash scripts/vibe-kanban/vibe-setup.sh [TASK_ID] [PORT_OFFSET] +``` + +**What it does:** +- Detects or generates a consistent TASK_ID for the worktree +- Auto-detects available port offset +- Creates Docker Compose file for the task +- Starts PostgreSQL and Redis containers +- Copies database from main repository +- Saves configuration to `.vibe-setup.env` + +**Configuration saved:** +- Task ID +- Port offsets +- Database names +- All connection details + +**Note:** This script runs in the "setup" section of Vibe Kanban. + +--- + +### `vibe-dev-server.sh` +**Purpose:** Starts the API and Workers processes (assumes database is already set up). + +**Usage:** +```bash +bash scripts/vibe-kanban/vibe-dev-server.sh +``` + +**What it does:** +- Loads configuration from `.vibe-setup.env` (created by vibe-setup.sh) +- Verifies database is ready +- Starts API and Workers using `start-api-and-workers.sh` +- Displays logs with filtered warnings +- Shows API and Workers logs in real-time + +**Requirements:** +- Must run `vibe-setup.sh` first to create the database environment +- Configuration file `.vibe-setup.env` must exist + +**Note:** This script runs in the "dev server" section of Vibe Kanban. + +--- + +### `cleanup-api-workers.sh` +**Purpose:** Stops API and Workers processes for a specific task. + +**Usage:** +```bash +bash scripts/vibe-kanban/cleanup-api-workers.sh +``` + +**What it does:** +- Stops API process (and child processes) +- Stops Workers process (and child processes) +- Kills orphaned processes +- Removes PID files +- Preserves log files for debugging + +**Features:** +- Graceful shutdown (SIGTERM) with fallback to force kill (SIGKILL) +- Handles orphaned processes +- Works with Vibe Kanban worktrees +- Supports environment variables for TASK_ID + +**Note:** This script is used by Vibe Kanban for cleanup operations. + +--- + +## Workflow + +1. **Setup Phase** (Vibe Kanban "setup" section): + ```bash + bash scripts/vibe-kanban/vibe-setup.sh + ``` + - Sets up database and Docker services + - Creates configuration file + +2. **Dev Server Phase** (Vibe Kanban "dev server" section): + ```bash + bash scripts/vibe-kanban/vibe-dev-server.sh + ``` + - Starts API and Workers + - Shows logs + +3. **Cleanup Phase** (Vibe Kanban cleanup): + ```bash + bash scripts/vibe-kanban/cleanup-api-workers.sh + ``` + - Stops all processes + - Cleans up + +## Configuration Files + +These scripts create/use the following files in the worktree: + +- `.vibe-task-id` - Stores the persistent TASK_ID for the worktree +- `.vibe-setup.env` - Stores all setup configuration (ports, database names, etc.) +- `.task-pids/` - Directory containing PID files and logs + +## Paths + +All paths in these scripts are relative to the main repository: +- Main repo: `/Users/oda/Desktop/Projects/managing-apps` +- Scripts: `scripts/vibe-kanban/` +- Worktree: Detected automatically from current directory + +## Environment Variables + +These scripts support the following environment variables: + +- `VIBE_TASK_ID` - Task ID from Vibe Kanban +- `VIBE_TASK_NAME` - Task name from Vibe Kanban +- `VIBE_WORKTREE_ROOT` - Worktree root path (set automatically) + diff --git a/scripts/vibe-kanban/cleanup-api-workers.sh b/scripts/vibe-kanban/cleanup-api-workers.sh new file mode 100755 index 00000000..cd9770eb --- /dev/null +++ b/scripts/vibe-kanban/cleanup-api-workers.sh @@ -0,0 +1,361 @@ +#!/bin/bash +# scripts/vibe-kanban/cleanup-api-workers.sh +# Cleanup script for Vibe Kanban - stops API and Workers processes only +# Usage: bash scripts/vibe-kanban/cleanup-api-workers.sh + +TASK_ID=$1 + +# Detect worktree root (similar to vibe-setup.sh) +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 + WORKTREE_PROJECT_ROOT="" +fi + +# Determine project root +if [ -n "$VIBE_WORKTREE_ROOT" ] && [ -d "$VIBE_WORKTREE_ROOT/src/Managing.Api" ]; then + PROJECT_ROOT="$VIBE_WORKTREE_ROOT" + WORKTREE_PROJECT_ROOT="$VIBE_WORKTREE_ROOT" + echo "๐Ÿ“ Using Vibe Kanban worktree: $PROJECT_ROOT" +elif [ -n "$WORKTREE_PROJECT_ROOT" ]; then + PROJECT_ROOT="$WORKTREE_PROJECT_ROOT" + echo "๐Ÿ“ Using Vibe Kanban worktree: $PROJECT_ROOT" +elif [ -d "$(pwd)/scripts" ] && [ -f "$(pwd)/scripts/start-api-and-workers.sh" ]; then + PROJECT_ROOT="$(pwd)" + echo "๐Ÿ“ Using current directory: $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 project root" + exit 1 + fi +fi + +# TASK_ID file to ensure consistency (same as vibe-setup.sh) +TASK_ID_FILE="$WORKTREE_PROJECT_ROOT/.vibe-task-id" + +# Try to get TASK_ID from various sources +if [ -z "$TASK_ID" ]; then + # First, check if we have a stored TASK_ID for this worktree (ensures consistency) + if [ -n "$WORKTREE_PROJECT_ROOT" ] && [ -f "$TASK_ID_FILE" ]; then + STORED_TASK_ID=$(cat "$TASK_ID_FILE" 2>/dev/null | tr -d '[:space:]') + if [ -n "$STORED_TASK_ID" ]; then + TASK_ID="$STORED_TASK_ID" + echo "๐Ÿ“‹ Using stored TASK_ID from .vibe-task-id: $TASK_ID" + fi + fi + + # Try environment variables (Vibe Kanban might set these) + if [ -z "$TASK_ID" ]; then + if [ -n "$VIBE_TASK_ID" ]; then + TASK_ID="$VIBE_TASK_ID" + echo "๐Ÿ“‹ Found TASK_ID from VIBE_TASK_ID: $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 +fi + +# If TASK_ID still not found, try to detect from worktree path or PID files +if [ -z "$TASK_ID" ]; then + # Try to extract from worktree path (Vibe Kanban worktrees often contain task ID) + WORKTREE_PATH_TO_CHECK="$WORKTREE_ROOT" + if [ -z "$WORKTREE_PATH_TO_CHECK" ] && [ -n "$VIBE_WORKTREE_ROOT" ]; then + WORKTREE_PATH_TO_CHECK="$VIBE_WORKTREE_ROOT" + fi + + if [ -n "$WORKTREE_PATH_TO_CHECK" ]; then + # Try UUID format first (Vibe Kanban might use UUIDs) + DETECTED_TASK=$(echo "$WORKTREE_PATH_TO_CHECK" | 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_PATH_TO_CHECK" | 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_PATH_TO_CHECK") + # 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) + HASH=$(echo -n "$LAST_DIR" | shasum -a 256 | cut -d' ' -f1 | head -c 8) + NUMERIC_ID=$((0x$HASH % 9999 + 1)) + DETECTED_TASK="TASK-$NUMERIC_ID" + echo "๐Ÿ“‹ Generated numeric TASK_ID from worktree directory '$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 + + # Try to find from PID files in worktree + if [ -z "$TASK_ID" ] && [ -n "$WORKTREE_PROJECT_ROOT" ]; then + PID_DIR_CHECK="$WORKTREE_PROJECT_ROOT/.task-pids" + if [ -d "$PID_DIR_CHECK" ]; then + # Find the most recent PID file with a running process + for pid_file in $(ls -t "$PID_DIR_CHECK"/*.pid 2>/dev/null); do + pid=$(cat "$pid_file" 2>/dev/null | tr -d '[:space:]') + if [ -n "$pid" ] && ps -p "$pid" > /dev/null 2>&1; then + # Extract task ID from filename (e.g., api-DEV-123.pid -> DEV-123) + DETECTED_TASK=$(basename "$pid_file" .pid | sed 's/^api-//; s/^workers-//') + if [ -n "$DETECTED_TASK" ]; then + TASK_ID="$DETECTED_TASK" + echo "๐Ÿ“‹ Detected TASK_ID from running process PID file: $TASK_ID" + break + fi + fi + done + fi + fi + + # Try to find from PID files in main repo if still not found + if [ -z "$TASK_ID" ]; then + PID_DIR_CHECK="$PROJECT_ROOT/.task-pids" + if [ -d "$PID_DIR_CHECK" ]; then + # Find the most recent PID file with a running process + for pid_file in $(ls -t "$PID_DIR_CHECK"/*.pid 2>/dev/null); do + pid=$(cat "$pid_file" 2>/dev/null | tr -d '[:space:]') + if [ -n "$pid" ] && ps -p "$pid" > /dev/null 2>&1; then + # Extract task ID from filename (e.g., api-DEV-123.pid -> DEV-123) + DETECTED_TASK=$(basename "$pid_file" .pid | sed 's/^api-//; s/^workers-//') + if [ -n "$DETECTED_TASK" ]; then + TASK_ID="$DETECTED_TASK" + echo "๐Ÿ“‹ Detected TASK_ID from running process PID file: $TASK_ID" + break + fi + fi + done + fi + fi + + # Try to find from current directory if it's a worktree + if [ -z "$TASK_ID" ]; then + CURRENT_DIR="$(pwd)" + # Try UUID format first + DETECTED_TASK=$(echo "$CURRENT_DIR" | 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 + if [ -z "$DETECTED_TASK" ]; then + DETECTED_TASK=$(echo "$CURRENT_DIR" | grep -oE '[A-Z]+-[0-9]+' | head -1) + fi + + if [ -n "$DETECTED_TASK" ]; then + TASK_ID="$DETECTED_TASK" + echo "๐Ÿ“‹ Detected TASK_ID from current directory: $TASK_ID" + fi + fi +fi + +PID_DIR="$PROJECT_ROOT/.task-pids" +API_PID_FILE="$PID_DIR/api-${TASK_ID}.pid" +WORKERS_PID_FILE="$PID_DIR/workers-${TASK_ID}.pid" + +if [ -z "$TASK_ID" ]; then + echo "" + echo "โŒ Error: TASK_ID is required but could not be determined" + echo "" + echo "๐Ÿ’ก Usage: $0 " + echo "๐Ÿ’ก Or set one of these environment variables:" + echo " - VIBE_TASK_ID" + echo " - TASK_ID_ENV" + echo " - TASK" + echo "" + echo "๐Ÿ’ก Or ensure you're running from a Vibe Kanban worktree with task ID in the path" + echo "" + echo "๐Ÿ” Debug information:" + echo " Current directory: $(pwd)" + echo " WORKTREE_ROOT: ${WORKTREE_ROOT:-not set}" + echo " WORKTREE_PROJECT_ROOT: ${WORKTREE_PROJECT_ROOT:-not set}" + echo " VIBE_WORKTREE_ROOT: ${VIBE_WORKTREE_ROOT:-not set}" + echo " PROJECT_ROOT: $PROJECT_ROOT" + if [ -n "$WORKTREE_PROJECT_ROOT" ]; then + echo " TASK_ID_FILE: $TASK_ID_FILE" + if [ -f "$TASK_ID_FILE" ]; then + echo " Stored TASK_ID: $(cat "$TASK_ID_FILE" 2>/dev/null | tr -d '[:space:]')" + else + echo " TASK_ID_FILE: (not found)" + fi + fi + if [ -d "$PID_DIR" ]; then + echo " Available PID files in $PID_DIR:" + ls -1 "$PID_DIR"/*.pid 2>/dev/null | head -5 | while read file; do + pid=$(cat "$file" 2>/dev/null | tr -d '[:space:]') + task=$(basename "$file" .pid | sed 's/^api-//; s/^workers-//') + if [ -n "$pid" ] && ps -p "$pid" > /dev/null 2>&1; then + echo " โœ… $file (PID: $pid, Task: $task) - RUNNING" + else + echo " โš ๏ธ $file (PID: $pid, Task: $task) - not running" + fi + done || echo " (none found)" + fi + echo "" + echo "๐Ÿ’ก To clean up a specific task, run:" + echo " $0 " + echo "" + echo "๐Ÿ’ก Or set VIBE_TASK_ID environment variable before running the script" + exit 1 +fi + +echo "๐Ÿงน Cleaning up API and Workers for task: $TASK_ID" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + +# Function to kill process and its children +kill_process_tree() { + local pid=$1 + local name=$2 + + if [ -z "$pid" ] || [ "$pid" = "0" ]; then + return 0 + fi + + if ! ps -p "$pid" > /dev/null 2>&1; then + return 0 + fi + + echo " ๐Ÿ›‘ Stopping $name (PID: $pid)..." + + # First, try graceful shutdown + kill "$pid" 2>/dev/null || true + sleep 2 + + # Check if still running + if ps -p "$pid" > /dev/null 2>&1; then + echo " โš ๏ธ Process still running, force killing..." + kill -9 "$pid" 2>/dev/null || true + sleep 1 + fi + + # Kill any child processes + local child_pids=$(pgrep -P "$pid" 2>/dev/null) + if [ -n "$child_pids" ]; then + for child_pid in $child_pids; do + echo " ๐Ÿ›‘ Stopping child process (PID: $child_pid)..." + kill "$child_pid" 2>/dev/null || true + sleep 1 + if ps -p "$child_pid" > /dev/null 2>&1; then + kill -9 "$child_pid" 2>/dev/null || true + fi + done + fi + + # Verify process is stopped + if ps -p "$pid" > /dev/null 2>&1; then + echo " โš ๏ธ Warning: Process $pid may still be running" + return 1 + else + echo " โœ… $name stopped" + return 0 + fi +} + +# Function to find and kill orphaned processes by name +kill_orphaned_processes() { + local task_id=$1 + local process_name=$2 + local found_any=false + + # Find processes that match the executable name and worktree path + local processes=$(ps aux | grep "$process_name" | grep -v grep | grep -E "worktree|$task_id" || true) + + if [ -n "$processes" ]; then + echo " ๐Ÿ” Found orphaned $process_name processes:" + echo "$processes" | while read line; do + local pid=$(echo "$line" | awk '{print $2}') + if [ -n "$pid" ] && ps -p "$pid" > /dev/null 2>&1; then + echo " ๐Ÿ›‘ Killing orphaned process (PID: $pid)..." + kill "$pid" 2>/dev/null || true + sleep 1 + if ps -p "$pid" > /dev/null 2>&1; then + kill -9 "$pid" 2>/dev/null || true + fi + found_any=true + fi + done + fi +} + +# Stop API process +echo "๐Ÿ“Š Stopping API process..." +if [ -f "$API_PID_FILE" ]; then + API_PID=$(cat "$API_PID_FILE" 2>/dev/null | tr -d '[:space:]') + if [ -n "$API_PID" ] && [ "$API_PID" != "0" ]; then + kill_process_tree "$API_PID" "API" + else + echo " โš ๏ธ Invalid PID in file: $API_PID_FILE" + fi + rm -f "$API_PID_FILE" +else + echo " โš ๏ธ API PID file not found: $API_PID_FILE" +fi + +# Kill orphaned Managing.Api processes +kill_orphaned_processes "$TASK_ID" "Managing.Api" + +# Stop Workers process +echo "" +echo "๐Ÿ“Š Stopping Workers process..." +if [ -f "$WORKERS_PID_FILE" ]; then + WORKERS_PID=$(cat "$WORKERS_PID_FILE" 2>/dev/null | tr -d '[:space:]') + if [ -n "$WORKERS_PID" ] && [ "$WORKERS_PID" != "0" ]; then + kill_process_tree "$WORKERS_PID" "Workers" + else + echo " โš ๏ธ Invalid PID in file: $WORKERS_PID_FILE" + fi + rm -f "$WORKERS_PID_FILE" +else + echo " โš ๏ธ Workers PID file not found: $WORKERS_PID_FILE" +fi + +# Kill orphaned Managing.Workers processes +kill_orphaned_processes "$TASK_ID" "Managing.Workers" + +# Kill orphaned dotnet run processes that might be related +echo "" +echo "๐Ÿ“Š Checking for orphaned dotnet run processes..." +DOTNET_RUN_PIDS=$(ps aux | grep "dotnet run" | grep -v grep | awk '{print $2}' || true) +if [ -n "$DOTNET_RUN_PIDS" ]; then + for pid in $DOTNET_RUN_PIDS; do + # Check if this dotnet run is a parent of Managing.Api or Managing.Workers + local has_api_child=$(pgrep -P "$pid" | xargs ps -p 2>/dev/null | grep -c "Managing.Api" || echo "0") + local has_workers_child=$(pgrep -P "$pid" | xargs ps -p 2>/dev/null | grep -c "Managing.Workers" || echo "0") + + if [ "$has_api_child" != "0" ] || [ "$has_workers_child" != "0" ]; then + echo " ๐Ÿ›‘ Killing orphaned dotnet run process (PID: $pid)..." + kill "$pid" 2>/dev/null || true + sleep 1 + if ps -p "$pid" > /dev/null 2>&1; then + kill -9 "$pid" 2>/dev/null || true + fi + fi + done +fi + +# Clean up log files (optional - comment out if you want to keep logs) +# echo "" +# echo "๐Ÿ“Š Cleaning up log files..." +# rm -f "$PID_DIR/api-${TASK_ID}.log" "$PID_DIR/workers-${TASK_ID}.log" 2>/dev/null || true + +echo "" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" +echo "โœ… Cleanup complete for task: $TASK_ID" +echo "" +echo "๐Ÿ’ก Note: Log files are preserved in: $PID_DIR" +echo "๐Ÿ’ก To remove log files, uncomment the cleanup section in the script" + diff --git a/scripts/vibe-dev-server.sh b/scripts/vibe-kanban/vibe-dev-server.sh similarity index 54% rename from scripts/vibe-dev-server.sh rename to scripts/vibe-kanban/vibe-dev-server.sh index 92ff4b60..38ec34bc 100755 --- a/scripts/vibe-dev-server.sh +++ b/scripts/vibe-kanban/vibe-dev-server.sh @@ -1,10 +1,7 @@ #!/bin/bash -# scripts/vibe-dev-server.sh -# Minimal script for Vibe Kanban worktrees -# This script runs from the worktree and uses main repo scripts for Docker setup - -TASK_ID=${1:-"DEV-$(date +%Y%m%d-%H%M%S)"} -PORT_OFFSET=${2:-0} +# scripts/vibe-kanban/vibe-dev-server.sh +# Simplified script for Vibe Kanban - starts API and Workers only +# Assumes database setup is already done by vibe-setup.sh # Detect worktree root WORKTREE_ROOT="$(pwd)" @@ -22,16 +19,77 @@ fi echo "๐Ÿ“ Worktree project root: $WORKTREE_PROJECT_ROOT" -# Find main repository (try common locations) +# TASK_ID file to ensure consistency (same as vibe-setup.sh) +TASK_ID_FILE="$WORKTREE_PROJECT_ROOT/.vibe-task-id" + +# Load setup configuration if available +SETUP_CONFIG_FILE="$WORKTREE_PROJECT_ROOT/.vibe-setup.env" +if [ -f "$SETUP_CONFIG_FILE" ]; then + echo "๐Ÿ“‹ Loading setup configuration from: $SETUP_CONFIG_FILE" + source "$SETUP_CONFIG_FILE" + echo " Task ID: $TASK_ID" + echo " Task Slot: ${TASK_SLOT:-not set}" + echo " Port offset: $PORT_OFFSET" + echo " API Port: $API_PORT" +else + echo "โš ๏ธ Setup configuration not found: $SETUP_CONFIG_FILE" + echo "๐Ÿ’ก Run scripts/vibe-kanban/vibe-setup.sh first to set up the database" + + # Try to get TASK_ID from stored file (ensures consistency) + if [ -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: $TASK_ID" + fi + fi + + # Try command line argument + if [ -z "$TASK_ID" ]; then + TASK_ID=${1:-""} + fi + + # Try environment variables + if [ -z "$TASK_ID" ]; then + if [ -n "$VIBE_TASK_ID" ]; then + TASK_ID="$VIBE_TASK_ID" + elif [ -n "$VIBE_TASK_NAME" ]; then + TASK_ID="$VIBE_TASK_NAME" + fi + fi + + PORT_OFFSET=${2:-0} + + if [ -z "$TASK_ID" ]; then + echo "โŒ TASK_ID is required" + echo "๐Ÿ’ก Usage: $0 [PORT_OFFSET]" + echo "๐Ÿ’ก Or run scripts/vibe-kanban/vibe-setup.sh first to create setup configuration" + exit 1 + fi + + API_PORT=$((5000 + PORT_OFFSET)) + # Extract TASK_SLOT from TASK_ID if not in config + if [ -z "$TASK_SLOT" ]; then + TASK_SLOT=$(echo "$TASK_ID" | grep -oE '[0-9]+' | head -1) + if [ -z "$TASK_SLOT" ] || [ "$TASK_SLOT" = "0" ]; then + TASK_SLOT=$((PORT_OFFSET / 10 + 1)) + fi + fi + echo " Using Task ID: $TASK_ID" + echo " Using Task Slot: $TASK_SLOT" + echo " Using Port offset: $PORT_OFFSET" +fi + +# Find main repository 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:-}" ) 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 + if [ -n "$path" ] && [ -d "$path" ] && [ -d "$path/scripts" ] && [ -f "$path/scripts/start-api-and-workers.sh" ]; then MAIN_REPO="$path" break fi @@ -39,23 +97,30 @@ 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 "๐Ÿš€ Starting dev environment..." +echo "๐Ÿš€ Starting API and Workers..." echo " Task ID: $TASK_ID" echo " Port offset: $PORT_OFFSET" +# Verify database is ready +if [ -n "$POSTGRES_PORT" ]; then + echo "๐Ÿ” Verifying database is ready on port $POSTGRES_PORT..." + if ! PGPASSWORD=postgres psql -h localhost -p $POSTGRES_PORT -U postgres -d postgres -c '\q' 2>/dev/null; then + echo "โŒ Database is not ready on port $POSTGRES_PORT" + echo "๐Ÿ’ก Run scripts/vibe-kanban/vibe-setup.sh first to set up the database" + exit 1 + fi + echo "โœ… Database is ready" +fi + # Export worktree path so main repo scripts know where to run dotnet from export VIBE_WORKTREE_ROOT="$WORKTREE_PROJECT_ROOT" -# Call main repo's start script -bash "$MAIN_REPO/scripts/start-task-docker.sh" "$TASK_ID" "$PORT_OFFSET" +# Start API and Workers only (database setup is already done) +bash "$MAIN_REPO/scripts/start-api-and-workers.sh" "$TASK_ID" "$PORT_OFFSET" # Determine log file paths (logs are created in the worktree root) PID_DIR="$WORKTREE_PROJECT_ROOT/.task-pids" @@ -113,17 +178,17 @@ else # Simple approach: tail both files with prefixes # Use a subshell to manage background processes ( - # Start tailing API logs in background + # Start tailing API logs in background (filter out NuGet build warnings) if [ -f "$API_LOG" ]; then - tail -f "$API_LOG" 2>/dev/null | while IFS= read -r line; do + tail -f "$API_LOG" 2>/dev/null | grep -vE "(warning NU|\.csproj : warning)" | while IFS= read -r line; do echo "[API] $line" done & TAIL_API_PID=$! fi - # Start tailing Workers logs in background + # Start tailing Workers logs in background (filter out NuGet build warnings) if [ -f "$WORKERS_LOG" ]; then - tail -f "$WORKERS_LOG" 2>/dev/null | while IFS= read -r line; do + tail -f "$WORKERS_LOG" 2>/dev/null | grep -vE "(warning NU|\.csproj : warning)" | while IFS= read -r line; do echo "[WORKERS] $line" done & TAIL_WORKERS_PID=$! diff --git a/scripts/vibe-kanban/vibe-setup.sh b/scripts/vibe-kanban/vibe-setup.sh new file mode 100755 index 00000000..f76ee9c8 --- /dev/null +++ b/scripts/vibe-kanban/vibe-setup.sh @@ -0,0 +1,312 @@ +#!/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" <