929 lines
37 KiB
Bash
Executable File
929 lines
37 KiB
Bash
Executable File
#!/bin/bash
|
|
# scripts/vibe-kanban/vibe-dev-server.sh
|
|
# Simplified script for Vibe Kanban - starts API and Workers using Aspire
|
|
# Assumes database setup is already done by vibe-setup.sh
|
|
#
|
|
# PORT CONSISTENCY:
|
|
# - Ports are calculated from PORT_OFFSET, which is stored in .vibe-setup.env
|
|
# - The same TASK_ID always uses the same PORT_OFFSET (set by vibe-setup.sh)
|
|
# - This ensures ports are consistent across runs for the same task
|
|
# - Port calculation: API=5000+OFFSET, Dashboard=15000+OFFSET, OTLP=19000+OFFSET, Resource=20000+OFFSET
|
|
|
|
# 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 (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 <TASK_ID> [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/src/Managing.AppHost" ]; then
|
|
MAIN_REPO="$path"
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ -z "$MAIN_REPO" ]; then
|
|
echo "❌ Cannot find main repository with Aspire AppHost"
|
|
exit 1
|
|
fi
|
|
|
|
echo "📁 Main repository: $MAIN_REPO"
|
|
echo "🚀 Starting API and Workers using Aspire..."
|
|
echo " Task ID: $TASK_ID"
|
|
echo " Port offset: $PORT_OFFSET"
|
|
echo " Task Slot: $TASK_SLOT"
|
|
|
|
# Restore launchSettings.json function
|
|
restore_launch_settings() {
|
|
# Only restore if variables are set (they're set later in the script)
|
|
if [ -z "$LAUNCH_SETTINGS" ]; then
|
|
return 0
|
|
fi
|
|
if [ -n "$LAUNCH_SETTINGS_BACKUP" ] && [ -f "$LAUNCH_SETTINGS_BACKUP" ]; then
|
|
cp "$LAUNCH_SETTINGS_BACKUP" "$LAUNCH_SETTINGS" 2>/dev/null || true
|
|
fi
|
|
if [ -n "$LAUNCH_SETTINGS_TEMP" ]; then
|
|
rm -f "$LAUNCH_SETTINGS_TEMP" 2>/dev/null || true
|
|
fi
|
|
}
|
|
|
|
# Cleanup function to stop Aspire and related processes
|
|
cleanup_aspire() {
|
|
echo ""
|
|
echo "🧹 Cleaning up Aspire processes for task $TASK_ID..."
|
|
|
|
# Kill processes using task-specific ports (if ports are set)
|
|
if [ -n "$API_PORT" ]; then
|
|
echo " Cleaning up port $API_PORT..."
|
|
lsof -ti :${API_PORT} | xargs kill -9 2>/dev/null || true
|
|
fi
|
|
if [ -n "$ASPIRE_DASHBOARD_PORT" ]; then
|
|
echo " Cleaning up port $ASPIRE_DASHBOARD_PORT..."
|
|
lsof -ti :${ASPIRE_DASHBOARD_PORT} | xargs kill -9 2>/dev/null || true
|
|
fi
|
|
if [ -n "$ASPIRE_OTLP_PORT" ]; then
|
|
echo " Cleaning up port $ASPIRE_OTLP_PORT..."
|
|
lsof -ti :${ASPIRE_OTLP_PORT} | xargs kill -9 2>/dev/null || true
|
|
fi
|
|
if [ -n "$ASPIRE_RESOURCE_SERVICE_PORT" ]; then
|
|
echo " Cleaning up port $ASPIRE_RESOURCE_SERVICE_PORT..."
|
|
lsof -ti :${ASPIRE_RESOURCE_SERVICE_PORT} | xargs kill -9 2>/dev/null || true
|
|
fi
|
|
|
|
# Kill Aspire process if PID file exists
|
|
ASPIRE_PID_FILE="$WORKTREE_PROJECT_ROOT/.task-pids/aspire-${TASK_ID}.pid"
|
|
if [ -f "$ASPIRE_PID_FILE" ]; then
|
|
ASPIRE_PID=$(cat "$ASPIRE_PID_FILE" 2>/dev/null)
|
|
if [ -n "$ASPIRE_PID" ] && ps -p "$ASPIRE_PID" > /dev/null 2>&1; then
|
|
echo " Stopping Aspire process (PID: $ASPIRE_PID)..."
|
|
# Kill all child processes first (they might be holding ports)
|
|
pkill -P "$ASPIRE_PID" 2>/dev/null || true
|
|
sleep 1
|
|
# Kill the main process
|
|
kill -TERM "$ASPIRE_PID" 2>/dev/null || true
|
|
sleep 2
|
|
# Force kill if still running
|
|
if ps -p "$ASPIRE_PID" > /dev/null 2>&1; then
|
|
kill -KILL "$ASPIRE_PID" 2>/dev/null || true
|
|
fi
|
|
# Kill any remaining child processes
|
|
pkill -P "$ASPIRE_PID" 2>/dev/null || true
|
|
fi
|
|
rm -f "$ASPIRE_PID_FILE"
|
|
fi
|
|
|
|
# Also kill any processes that might be children of previous Aspire runs
|
|
# Find all dotnet processes and check if they're related to our task ports
|
|
ps aux | grep "dotnet" | grep -v grep | while read line; do
|
|
PID=$(echo "$line" | awk '{print $2}')
|
|
# Check if this process is using any of our task ports
|
|
if lsof -p "$PID" 2>/dev/null | grep -E ":(15005|19005|20005|5005)" > /dev/null 2>&1; then
|
|
echo " Killing dotnet process $PID (using task ports)..."
|
|
# Kill the process and its children
|
|
pkill -P "$PID" 2>/dev/null || true
|
|
kill -9 "$PID" 2>/dev/null || true
|
|
fi
|
|
done
|
|
|
|
# Kill dotnet processes related to AppHost
|
|
# Kill processes that match AppHost patterns
|
|
pkill -9 -f "dotnet.*AppHost" 2>/dev/null || true
|
|
pkill -9 -f "dotnet run.*AppHost" 2>/dev/null || true
|
|
|
|
# Kill processes running from the AppHost directory specifically
|
|
# This catches processes that are running from that directory even if command doesn't show it
|
|
if [ -n "$MAIN_REPO" ]; then
|
|
APPHOST_DIR="$MAIN_REPO/src/Managing.AppHost"
|
|
# Use pwdx or lsof to find processes in this directory
|
|
ps aux | grep -E "dotnet.*run" | grep -v grep | while read line; do
|
|
PID=$(echo "$line" | awk '{print $2}')
|
|
# Check if this process has files open in AppHost directory or is using our ports
|
|
if lsof -p "$PID" 2>/dev/null | grep -q "$APPHOST_DIR"; then
|
|
echo " Killing dotnet process $PID (running from AppHost directory)..."
|
|
kill -9 "$PID" 2>/dev/null || true
|
|
elif lsof -p "$PID" 2>/dev/null | grep -E ":(15005|19005|20005|5005)" > /dev/null 2>&1; then
|
|
echo " Killing dotnet process $PID (using task ports)..."
|
|
kill -9 "$PID" 2>/dev/null || true
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Kill any Aspire dashboard processes and orchestration processes
|
|
# These processes can hold onto ports even after the main process is killed
|
|
# Kill by process name patterns
|
|
pkill -9 -f "Aspire.Dashboard" 2>/dev/null || true
|
|
pkill -9 -f "dcpctrl" 2>/dev/null || true
|
|
pkill -9 -f "dcp start-apiserver" 2>/dev/null || true
|
|
pkill -9 -f "dcpproc" 2>/dev/null || true
|
|
pkill -9 -f "AspireWorker" 2>/dev/null || true
|
|
|
|
# Also kill by executable name (Aspire dashboard runs as a separate process)
|
|
pkill -9 -f "Aspire.Dashboard.dll" 2>/dev/null || true
|
|
|
|
# Kill all Managing.* processes (AppHost, Api, Workers) - these can hold ports
|
|
# These are the actual executables that Aspire spawns
|
|
echo " Killing all Managing.* processes..."
|
|
ps aux | grep -E "Managing\.(AppHost|Api|Workers)" | grep -v grep | while read line; do
|
|
PID=$(echo "$line" | awk '{print $2}')
|
|
if [ -n "$PID" ]; then
|
|
echo " Killing Managing.* process $PID..."
|
|
pkill -P "$PID" 2>/dev/null || true
|
|
kill -9 "$PID" 2>/dev/null || true
|
|
fi
|
|
done
|
|
# Also kill by pattern (more aggressive)
|
|
pkill -9 -f "Managing.AppHost" 2>/dev/null || true
|
|
pkill -9 -f "Managing.Api" 2>/dev/null || true
|
|
pkill -9 -f "Managing.Workers" 2>/dev/null || true
|
|
|
|
# Kill any dotnet processes that might be running Aspire dashboard
|
|
# Find processes using our ports and kill them
|
|
for port in ${API_PORT} ${ASPIRE_DASHBOARD_PORT} ${ASPIRE_OTLP_PORT} ${ASPIRE_RESOURCE_SERVICE_PORT}; do
|
|
if [ -n "$port" ]; then
|
|
lsof -ti :${port} 2>/dev/null | xargs kill -9 2>/dev/null || true
|
|
fi
|
|
done
|
|
|
|
# Kill any tail processes that might be following the log file
|
|
TAIL_PID_FILE="$WORKTREE_PROJECT_ROOT/.task-pids/tail-${TASK_ID}.pid"
|
|
if [ -f "$TAIL_PID_FILE" ]; then
|
|
TAIL_PID=$(cat "$TAIL_PID_FILE" 2>/dev/null)
|
|
if [ -n "$TAIL_PID" ] && ps -p "$TAIL_PID" > /dev/null 2>&1; then
|
|
echo " Killing log tailing process (PID: $TAIL_PID)..."
|
|
kill -9 "$TAIL_PID" 2>/dev/null || true
|
|
fi
|
|
rm -f "$TAIL_PID_FILE"
|
|
fi
|
|
|
|
# Also kill any tail processes that might be following the log file (fallback)
|
|
if [ -n "$ASPIRE_LOG" ]; then
|
|
echo " Killing any remaining log tailing processes..."
|
|
pkill -f "tail.*aspire.*${TASK_ID}" 2>/dev/null || true
|
|
pkill -f "tail -f.*${ASPIRE_LOG}" 2>/dev/null || true
|
|
# Also kill any tail processes that have the log file open
|
|
if [ -d "$(dirname "$ASPIRE_LOG")" ]; then
|
|
ps aux | grep "tail" | grep -v grep | while read line; do
|
|
PID=$(echo "$line" | awk '{print $2}')
|
|
if lsof -p "$PID" 2>/dev/null | grep -q "$ASPIRE_LOG"; then
|
|
echo " Killing tail process $PID..."
|
|
kill -9 "$PID" 2>/dev/null || true
|
|
fi
|
|
done
|
|
fi
|
|
fi
|
|
|
|
# Wait a moment for processes to fully terminate
|
|
sleep 2
|
|
|
|
# Restore launchSettings.json
|
|
restore_launch_settings
|
|
|
|
echo "✅ Cleanup complete"
|
|
}
|
|
|
|
# Function to find an available port
|
|
find_available_port() {
|
|
local start_port=$1
|
|
local end_port=$((start_port + 100)) # Search in a range of 100 ports
|
|
|
|
for port in $(seq $start_port $end_port); do
|
|
if ! lsof -ti :${port} > /dev/null 2>&1; then
|
|
echo $port
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
# If no port found in range, return a random high port
|
|
echo $((20000 + RANDOM % 10000))
|
|
}
|
|
|
|
# Ensure API_PORT is set (should be from config, but fallback if needed)
|
|
if [ -z "$API_PORT" ]; then
|
|
API_PORT=$((5000 + PORT_OFFSET))
|
|
fi
|
|
|
|
# DYNAMIC PORT ALLOCATION: Find available ports each time instead of using fixed offsets
|
|
# This completely eliminates port conflict race conditions
|
|
echo "🔍 Finding available ports for Aspire..."
|
|
ASPIRE_DASHBOARD_PORT=$(find_available_port 15000)
|
|
ASPIRE_OTLP_PORT=$(find_available_port 19000)
|
|
ASPIRE_RESOURCE_SERVICE_PORT=$(find_available_port 20000)
|
|
|
|
echo " Dashboard will use port: $ASPIRE_DASHBOARD_PORT"
|
|
echo " OTLP will use port: $ASPIRE_OTLP_PORT"
|
|
echo " Resource Service will use port: $ASPIRE_RESOURCE_SERVICE_PORT"
|
|
|
|
# Function to verify and free a port
|
|
verify_and_free_port() {
|
|
local port=$1
|
|
local port_name=$2
|
|
local max_attempts=5
|
|
local attempt=0
|
|
|
|
while [ $attempt -lt $max_attempts ]; do
|
|
attempt=$((attempt + 1))
|
|
|
|
# Check if port is in use
|
|
PIDS_USING_PORT=$(lsof -ti :${port} 2>/dev/null)
|
|
|
|
if [ -z "$PIDS_USING_PORT" ]; then
|
|
echo " ✅ Port $port ($port_name) is free"
|
|
return 0
|
|
fi
|
|
|
|
# Port is in use, show what's using it
|
|
echo " ⚠️ Port $port ($port_name) is in use by PIDs: $PIDS_USING_PORT"
|
|
|
|
# Show process details
|
|
for pid in $PIDS_USING_PORT; do
|
|
if ps -p "$pid" > /dev/null 2>&1; then
|
|
PROCESS_INFO=$(ps -p "$pid" -o command= 2>/dev/null | head -1)
|
|
echo " PID $pid: $PROCESS_INFO"
|
|
fi
|
|
done
|
|
|
|
# Kill processes using this port
|
|
echo " 🔪 Killing processes using port $port..."
|
|
for pid in $PIDS_USING_PORT; do
|
|
# Kill children first
|
|
pkill -P "$pid" 2>/dev/null || true
|
|
# Kill the process
|
|
kill -9 "$pid" 2>/dev/null || true
|
|
done
|
|
|
|
# Also kill by process name if it's Aspire-related
|
|
if echo "$PIDS_USING_PORT" | xargs ps -p 2>/dev/null | grep -qiE "(Aspire|AppHost|dcp)"; then
|
|
pkill -9 -f "Aspire.Dashboard" 2>/dev/null || true
|
|
pkill -9 -f "dcpctrl" 2>/dev/null || true
|
|
pkill -9 -f "dcp" 2>/dev/null || true
|
|
fi
|
|
|
|
# Wait for port to be released
|
|
sleep 2
|
|
|
|
# Verify port is now free
|
|
if ! lsof -ti :${port} > /dev/null 2>&1; then
|
|
echo " ✅ Port $port ($port_name) is now free"
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
# Port still in use after max attempts
|
|
echo " ❌ Port $port ($port_name) is still in use after $max_attempts attempts"
|
|
return 1
|
|
}
|
|
|
|
# Set up signal handlers for cleanup on exit
|
|
trap cleanup_aspire EXIT INT TERM
|
|
|
|
# Clean up any existing processes for this task before starting
|
|
echo ""
|
|
echo "🧹 Cleaning up any existing processes for task $TASK_ID..."
|
|
cleanup_aspire
|
|
|
|
# Wait for ports to be released (TIME_WAIT state can take a few seconds)
|
|
echo "⏳ Waiting for ports to be released..."
|
|
for i in {1..10}; do
|
|
PORTS_IN_USE=0
|
|
if [ -n "$API_PORT" ] && lsof -ti :${API_PORT} > /dev/null 2>&1; then
|
|
PORTS_IN_USE=$((PORTS_IN_USE + 1))
|
|
fi
|
|
if [ -n "$ASPIRE_DASHBOARD_PORT" ] && lsof -ti :${ASPIRE_DASHBOARD_PORT} > /dev/null 2>&1; then
|
|
PORTS_IN_USE=$((PORTS_IN_USE + 1))
|
|
fi
|
|
if [ -n "$ASPIRE_OTLP_PORT" ] && lsof -ti :${ASPIRE_OTLP_PORT} > /dev/null 2>&1; then
|
|
PORTS_IN_USE=$((PORTS_IN_USE + 1))
|
|
fi
|
|
if [ -n "$ASPIRE_RESOURCE_SERVICE_PORT" ] && lsof -ti :${ASPIRE_RESOURCE_SERVICE_PORT} > /dev/null 2>&1; then
|
|
PORTS_IN_USE=$((PORTS_IN_USE + 1))
|
|
fi
|
|
|
|
if [ $PORTS_IN_USE -eq 0 ]; then
|
|
echo "✅ All ports are free"
|
|
break
|
|
else
|
|
if [ $i -lt 10 ]; then
|
|
echo " Ports still in use, waiting... (${i}/10)"
|
|
sleep 1
|
|
else
|
|
echo "⚠️ Some ports are still in use after cleanup"
|
|
echo " Attempting to force kill processes on ports..."
|
|
# Force kill one more time
|
|
if [ -n "$API_PORT" ]; then lsof -ti :${API_PORT} | xargs kill -9 2>/dev/null || true; fi
|
|
if [ -n "$ASPIRE_DASHBOARD_PORT" ]; then lsof -ti :${ASPIRE_DASHBOARD_PORT} | xargs kill -9 2>/dev/null || true; fi
|
|
if [ -n "$ASPIRE_OTLP_PORT" ]; then lsof -ti :${ASPIRE_OTLP_PORT} | xargs kill -9 2>/dev/null || true; fi
|
|
if [ -n "$ASPIRE_RESOURCE_SERVICE_PORT" ]; then lsof -ti :${ASPIRE_RESOURCE_SERVICE_PORT} | xargs kill -9 2>/dev/null || true; fi
|
|
sleep 2
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# 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
|
|
|
|
echo "📊 Aspire Dashboard Port: $ASPIRE_DASHBOARD_PORT"
|
|
echo "📊 Aspire OTLP Port: $ASPIRE_OTLP_PORT"
|
|
echo "📊 Aspire Resource Service Port: $ASPIRE_RESOURCE_SERVICE_PORT"
|
|
|
|
# Set environment variables for Aspire
|
|
export TASK_ID="$TASK_ID"
|
|
export PORT_OFFSET="$PORT_OFFSET"
|
|
export TASK_SLOT="$TASK_SLOT"
|
|
|
|
# Ensure HTTPS dev certificate is available (Aspire may need it even for HTTP mode)
|
|
echo "🔐 Ensuring HTTPS developer certificate is available..."
|
|
if ! dotnet dev-certs https --check > /dev/null 2>&1; then
|
|
echo " Generating HTTPS developer certificate..."
|
|
dotnet dev-certs https --trust > /dev/null 2>&1 || {
|
|
echo "⚠️ Could not generate/trust certificate"
|
|
echo " Will try to use HTTP-only profile"
|
|
}
|
|
fi
|
|
|
|
# Configure Aspire to use HTTP only (avoid certificate issues)
|
|
# IMPORTANT: We MUST set OTLP endpoint (Aspire requires it), but we only set the HTTP one (not both)
|
|
# Setting both DOTNET_DASHBOARD_OTLP_ENDPOINT_URL and DOTNET_DASHBOARD_OTLP_HTTP_ENDPOINT_URL
|
|
# can cause double-binding issues
|
|
export ASPIRE_ALLOW_UNSECURED_TRANSPORT="true"
|
|
export ASPNETCORE_URLS="http://localhost:${ASPIRE_DASHBOARD_PORT}"
|
|
export DOTNET_DASHBOARD_OTLP_HTTP_ENDPOINT_URL="http://localhost:${ASPIRE_OTLP_PORT}"
|
|
export ASPNETCORE_ENVIRONMENT="Development"
|
|
export DOTNET_ENVIRONMENT="Development"
|
|
|
|
# NOTE: We do NOT set DOTNET_RESOURCE_SERVICE_ENDPOINT_URL - let Aspire choose its own port
|
|
# We also do NOT set DOTNET_DASHBOARD_OTLP_ENDPOINT_URL (only HTTP version)
|
|
|
|
# Restore packages in the worktree first to ensure all dependencies are available
|
|
# This is important because Aspire will build projects that may reference worktree paths
|
|
echo ""
|
|
echo "📦 Restoring NuGet packages..."
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
|
|
# Restore at solution level in worktree if it exists
|
|
if [ -f "$WORKTREE_PROJECT_ROOT/src/Managing.sln" ]; then
|
|
echo " Restoring in worktree solution..."
|
|
cd "$WORKTREE_PROJECT_ROOT/src"
|
|
# Suppress all warnings and only show errors
|
|
dotnet restore Managing.sln --verbosity quiet --nologo 2>&1 | \
|
|
grep -vE "(warning|Warning|WARNING|NU[0-9]|\.csproj :)" || true
|
|
fi
|
|
|
|
# Restore at solution level in main repo (where we'll actually run from)
|
|
echo " Restoring in main repo solution..."
|
|
cd "$MAIN_REPO/src"
|
|
# Suppress all warnings and only show errors
|
|
RESTORE_OUTPUT=$(dotnet restore Managing.sln --verbosity quiet --nologo 2>&1 | \
|
|
grep -vE "(warning|Warning|WARNING|NU[0-9]|\.csproj :)" || true)
|
|
if echo "$RESTORE_OUTPUT" | grep -qE "(error|Error|ERROR|failed|Failed)"; then
|
|
echo "❌ Package restore failed:"
|
|
echo "$RESTORE_OUTPUT"
|
|
exit 1
|
|
else
|
|
echo "✅ Packages restored successfully"
|
|
fi
|
|
|
|
# Ensure we're in the AppHost directory for running Aspire
|
|
cd "$MAIN_REPO/src/Managing.AppHost"
|
|
echo ""
|
|
|
|
# Create a temporary launchSettings.json with task-specific port
|
|
# This ensures Aspire uses the correct port for this task
|
|
LAUNCH_SETTINGS="$MAIN_REPO/src/Managing.AppHost/Properties/launchSettings.json"
|
|
LAUNCH_SETTINGS_BACKUP="$MAIN_REPO/src/Managing.AppHost/Properties/launchSettings.json.backup"
|
|
LAUNCH_SETTINGS_TEMP="$MAIN_REPO/src/Managing.AppHost/Properties/launchSettings.json.task-${TASK_ID}"
|
|
|
|
# Backup original launchSettings.json if not already backed up
|
|
if [ ! -f "$LAUNCH_SETTINGS_BACKUP" ]; then
|
|
cp "$LAUNCH_SETTINGS" "$LAUNCH_SETTINGS_BACKUP" 2>/dev/null || true
|
|
fi
|
|
|
|
# Create task-specific launchSettings.json with custom port
|
|
# NOTE: Only set DOTNET_DASHBOARD_OTLP_HTTP_ENDPOINT_URL (not both HTTP and non-HTTP versions)
|
|
cat > "$LAUNCH_SETTINGS_TEMP" <<EOF
|
|
{
|
|
"\$schema": "https://json.schemastore.org/launchsettings.json",
|
|
"profiles": {
|
|
"http": {
|
|
"commandName": "Project",
|
|
"dotnetRunMessages": true,
|
|
"launchBrowser": true,
|
|
"applicationUrl": "http://localhost:${ASPIRE_DASHBOARD_PORT}",
|
|
"environmentVariables": {
|
|
"ASPNETCORE_ENVIRONMENT": "Development",
|
|
"DOTNET_ENVIRONMENT": "Development",
|
|
"DOTNET_DASHBOARD_OTLP_HTTP_ENDPOINT_URL": "http://localhost:${ASPIRE_OTLP_PORT}",
|
|
"ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
EOF
|
|
|
|
# Use the task-specific launchSettings.json
|
|
cp "$LAUNCH_SETTINGS_TEMP" "$LAUNCH_SETTINGS"
|
|
|
|
# Final comprehensive port verification before starting Aspire
|
|
echo ""
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "🔍 Comprehensive Port Verification for Task: $TASK_ID"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "📊 Required ports for this task:"
|
|
echo " - Dashboard: $ASPIRE_DASHBOARD_PORT"
|
|
echo " - OTLP: $ASPIRE_OTLP_PORT"
|
|
echo " - Resource Service: $ASPIRE_RESOURCE_SERVICE_PORT"
|
|
echo " - API: $API_PORT"
|
|
echo ""
|
|
|
|
# Kill all Aspire-related processes first (comprehensive cleanup)
|
|
echo "🧹 Step 1: Killing all Aspire-related processes..."
|
|
pkill -9 -f "Aspire.Dashboard" 2>/dev/null || true
|
|
pkill -9 -f "dcpctrl" 2>/dev/null || true
|
|
pkill -9 -f "dcp start-apiserver" 2>/dev/null || true
|
|
pkill -9 -f "dcpproc" 2>/dev/null || true
|
|
pkill -9 -f "Managing.AppHost" 2>/dev/null || true
|
|
pkill -9 -f "Managing.Workers" 2>/dev/null || true
|
|
pkill -9 -f "Managing.Api" 2>/dev/null || true
|
|
sleep 2
|
|
|
|
# Verify each port individually
|
|
echo ""
|
|
echo "🔍 Step 2: Verifying each port is free..."
|
|
ALL_PORTS_FREE=true
|
|
|
|
if ! verify_and_free_port "$ASPIRE_DASHBOARD_PORT" "Aspire Dashboard"; then
|
|
ALL_PORTS_FREE=false
|
|
fi
|
|
|
|
if ! verify_and_free_port "$ASPIRE_OTLP_PORT" "Aspire OTLP"; then
|
|
ALL_PORTS_FREE=false
|
|
fi
|
|
|
|
if ! verify_and_free_port "$ASPIRE_RESOURCE_SERVICE_PORT" "Aspire Resource Service"; then
|
|
ALL_PORTS_FREE=false
|
|
fi
|
|
|
|
if ! verify_and_free_port "$API_PORT" "API"; then
|
|
ALL_PORTS_FREE=false
|
|
fi
|
|
|
|
# Final verification - check all ports one more time
|
|
echo ""
|
|
echo "🔍 Step 3: Final verification - all ports must be free..."
|
|
FINAL_CHECK_FAILED=false
|
|
for port in "$ASPIRE_DASHBOARD_PORT" "$ASPIRE_OTLP_PORT" "$ASPIRE_RESOURCE_SERVICE_PORT" "$API_PORT"; do
|
|
if lsof -ti :${port} > /dev/null 2>&1; then
|
|
echo " ❌ Port $port is still in use!"
|
|
FINAL_CHECK_FAILED=true
|
|
fi
|
|
done
|
|
|
|
if [ "$FINAL_CHECK_FAILED" = true ] || [ "$ALL_PORTS_FREE" = false ]; then
|
|
echo ""
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "❌ ERROR: Cannot start Aspire - ports are still in use"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "💡 This usually means:"
|
|
echo " 1. Another instance of Aspire is running"
|
|
echo " 2. A previous instance didn't shut down properly"
|
|
echo " 3. Another application is using these ports"
|
|
echo ""
|
|
echo "💡 Try running the cleanup script:"
|
|
echo " bash scripts/vibe-kanban/cleanup-api-workers.sh $TASK_ID"
|
|
echo ""
|
|
echo "💡 Or manually kill processes using these ports:"
|
|
for port in "$ASPIRE_DASHBOARD_PORT" "$ASPIRE_OTLP_PORT" "$ASPIRE_RESOURCE_SERVICE_PORT" "$API_PORT"; do
|
|
PIDS=$(lsof -ti :${port} 2>/dev/null)
|
|
if [ -n "$PIDS" ]; then
|
|
echo " Port $port: kill -9 $PIDS"
|
|
fi
|
|
done
|
|
exit 1
|
|
fi
|
|
|
|
echo ""
|
|
echo "✅ All ports are verified and free!"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
|
|
# One final aggressive port check right before starting (race condition prevention)
|
|
echo "🔍 Final port check (race condition prevention)..."
|
|
# Kill any existing Aspire processes that might have started
|
|
echo " Killing any existing Aspire orchestration processes..."
|
|
pkill -9 -f "dcpctrl" 2>/dev/null || true
|
|
pkill -9 -f "dcpproc" 2>/dev/null || true
|
|
pkill -9 -f "dcp start-apiserver" 2>/dev/null || true
|
|
pkill -9 -f "dotnet run.*http" 2>/dev/null || true
|
|
pkill -9 -f "Managing.AppHost" 2>/dev/null || true
|
|
pkill -9 -f "Managing.Api" 2>/dev/null || true
|
|
pkill -9 -f "Managing.Workers" 2>/dev/null || true
|
|
|
|
# Kill any processes using our specific ports (most important)
|
|
echo " Checking and killing processes using task ports..."
|
|
for port in "$ASPIRE_DASHBOARD_PORT" "$ASPIRE_OTLP_PORT" "$ASPIRE_RESOURCE_SERVICE_PORT" "$API_PORT"; do
|
|
PIDS=$(lsof -ti :${port} 2>/dev/null)
|
|
if [ -n "$PIDS" ]; then
|
|
echo " ⚠️ Port $port is in use by PIDs: $PIDS - killing..."
|
|
for pid in $PIDS; do
|
|
# Kill children first
|
|
pkill -P "$pid" 2>/dev/null || true
|
|
# Kill the process
|
|
kill -9 "$pid" 2>/dev/null || true
|
|
done
|
|
sleep 1
|
|
fi
|
|
done
|
|
|
|
# Wait longer for ports to be fully released (OS might hold them in TIME_WAIT)
|
|
echo " Waiting for OS to fully release ports (TIME_WAIT state)..."
|
|
sleep 5
|
|
|
|
# One more pre-emptive cleanup to catch any new processes
|
|
echo " Pre-emptive cleanup of any new processes..."
|
|
pkill -9 -f "Aspire.Dashboard" 2>/dev/null || true
|
|
pkill -9 -f "dcpctrl" 2>/dev/null || true
|
|
pkill -9 -f "dcp" 2>/dev/null || true
|
|
for port in "$ASPIRE_DASHBOARD_PORT" "$ASPIRE_OTLP_PORT" "$ASPIRE_RESOURCE_SERVICE_PORT" "$API_PORT"; do
|
|
lsof -ti :${port} 2>/dev/null | xargs kill -9 2>/dev/null || true
|
|
done
|
|
sleep 2
|
|
|
|
# Final verification - all ports must be free
|
|
echo " Verifying all ports are free..."
|
|
PORTS_STILL_IN_USE=0
|
|
for port in "$ASPIRE_DASHBOARD_PORT" "$ASPIRE_OTLP_PORT" "$ASPIRE_RESOURCE_SERVICE_PORT" "$API_PORT"; do
|
|
if lsof -ti :${port} > /dev/null 2>&1; then
|
|
echo " ❌ Port $port is still in use!"
|
|
PORTS_STILL_IN_USE=$((PORTS_STILL_IN_USE + 1))
|
|
fi
|
|
done
|
|
|
|
if [ $PORTS_STILL_IN_USE -gt 0 ]; then
|
|
echo " ⚠️ Some ports are still in use. Attempting final aggressive cleanup..."
|
|
# Final aggressive kill
|
|
for port in "$ASPIRE_DASHBOARD_PORT" "$ASPIRE_OTLP_PORT" "$ASPIRE_RESOURCE_SERVICE_PORT" "$API_PORT"; do
|
|
lsof -ti :${port} 2>/dev/null | xargs kill -9 2>/dev/null || true
|
|
done
|
|
pkill -9 -f "Aspire" 2>/dev/null || true
|
|
pkill -9 -f "dcp" 2>/dev/null || true
|
|
sleep 3
|
|
fi
|
|
|
|
echo "✅ Final port check complete"
|
|
echo ""
|
|
|
|
# Run Aspire (this will start the API and Workers)
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "🚀 Starting Aspire on port $ASPIRE_DASHBOARD_PORT..."
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
|
|
# Run Aspire in the background and capture output
|
|
ASPIRE_LOG="$WORKTREE_PROJECT_ROOT/.task-pids/aspire-${TASK_ID}.log"
|
|
mkdir -p "$(dirname "$ASPIRE_LOG")"
|
|
|
|
# CRITICAL: Kill any DCP processes that might interfere
|
|
# DCP (Distributed Control Plane) is Aspire's orchestrator and can hold ports
|
|
echo "🔧 Ensuring no DCP processes are running..."
|
|
pkill -9 -f "dcpctrl" 2>/dev/null || true
|
|
pkill -9 -f "dcpproc" 2>/dev/null || true
|
|
pkill -9 -f "dcp start-apiserver" 2>/dev/null || true
|
|
pkill -9 -f "Aspire.Hosting.Orchestration" 2>/dev/null || true
|
|
sleep 1
|
|
|
|
# Final port verification right before starting (within 1 second of starting Aspire)
|
|
for port in "$ASPIRE_DASHBOARD_PORT" "$ASPIRE_OTLP_PORT" "$ASPIRE_RESOURCE_SERVICE_PORT" "$API_PORT"; do
|
|
lsof -ti :${port} 2>/dev/null | xargs kill -9 2>/dev/null || true
|
|
done
|
|
|
|
# CRITICAL: Kill ALL Aspire-related processes system-wide before starting
|
|
# This prevents any zombie processes from previous runs from interfering
|
|
echo "🧹 Final system-wide Aspire cleanup..."
|
|
pkill -9 -f "Aspire" 2>/dev/null || true
|
|
pkill -9 -f "dcp" 2>/dev/null || true
|
|
pkill -9 -f "Managing.AppHost" 2>/dev/null || true
|
|
pkill -9 -f "dotnet run.*AppHost" 2>/dev/null || true
|
|
pkill -9 -f "dotnet run.*http" 2>/dev/null || true
|
|
sleep 2
|
|
|
|
# One final verification that our ports are free
|
|
echo "🔍 Final pre-flight port check..."
|
|
for port in "$ASPIRE_DASHBOARD_PORT" "$ASPIRE_OTLP_PORT" "$ASPIRE_RESOURCE_SERVICE_PORT" "$API_PORT"; do
|
|
PIDS=$(lsof -ti :${port} 2>/dev/null)
|
|
if [ -n "$PIDS" ]; then
|
|
echo "⚠️ Port $port is in use by PIDs: $PIDS - killing..."
|
|
for pid in $PIDS; do
|
|
kill -9 "$pid" 2>/dev/null || true
|
|
done
|
|
fi
|
|
done
|
|
sleep 1
|
|
|
|
# Start Aspire with the http launch profile (now configured with task-specific port)
|
|
# All output goes to log file (warnings will be filtered when displaying)
|
|
dotnet run --launch-profile http > "$ASPIRE_LOG" 2>&1 &
|
|
ASPIRE_PID=$!
|
|
|
|
# Save PID
|
|
echo $ASPIRE_PID > "$WORKTREE_PROJECT_ROOT/.task-pids/aspire-${TASK_ID}.pid"
|
|
|
|
echo "✅ Aspire started (PID: $ASPIRE_PID)"
|
|
echo "📋 Log: $ASPIRE_LOG"
|
|
echo ""
|
|
echo "⏳ Aspire is starting (waiting up to 30 seconds)..."
|
|
echo " Building projects and starting services..."
|
|
|
|
# Wait a bit for Aspire to start writing to the log
|
|
sleep 3
|
|
|
|
# Immediately check for binding errors in the log
|
|
echo "🔍 Checking for port binding errors..."
|
|
for i in {1..5}; do
|
|
sleep 1
|
|
if [ -f "$ASPIRE_LOG" ]; then
|
|
# Check for port binding errors (use actual ports, not hardcoded)
|
|
PORT_ERROR_PATTERN="address already in use|Failed to bind|bind.*${ASPIRE_DASHBOARD_PORT}|bind.*${ASPIRE_OTLP_PORT}|bind.*${ASPIRE_RESOURCE_SERVICE_PORT}|bind.*${API_PORT}"
|
|
if grep -qiE "$PORT_ERROR_PATTERN" "$ASPIRE_LOG" 2>/dev/null; then
|
|
echo "❌ Port binding error detected in log!"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "📋 Error details:"
|
|
grep -iE "$PORT_ERROR_PATTERN" "$ASPIRE_LOG" 2>/dev/null | head -5
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
echo "🔧 Attempting to fix: killing processes and restarting..."
|
|
|
|
# Kill Aspire process
|
|
kill -9 "$ASPIRE_PID" 2>/dev/null || true
|
|
pkill -P "$ASPIRE_PID" 2>/dev/null || true
|
|
|
|
# Aggressively free all ports
|
|
for port in "$ASPIRE_DASHBOARD_PORT" "$ASPIRE_OTLP_PORT" "$ASPIRE_RESOURCE_SERVICE_PORT" "$API_PORT"; do
|
|
lsof -ti :${port} 2>/dev/null | xargs kill -9 2>/dev/null || true
|
|
done
|
|
|
|
# Kill all Aspire processes
|
|
pkill -9 -f "Aspire.Dashboard" 2>/dev/null || true
|
|
pkill -9 -f "dcpctrl" 2>/dev/null || true
|
|
pkill -9 -f "dcp" 2>/dev/null || true
|
|
pkill -9 -f "Managing.AppHost" 2>/dev/null || true
|
|
|
|
sleep 3
|
|
|
|
# Verify ports are free
|
|
PORTS_FREE=true
|
|
for port in "$ASPIRE_DASHBOARD_PORT" "$ASPIRE_OTLP_PORT" "$ASPIRE_RESOURCE_SERVICE_PORT" "$API_PORT"; do
|
|
if lsof -ti :${port} > /dev/null 2>&1; then
|
|
echo " ❌ Port $port is still in use!"
|
|
PORTS_FREE=false
|
|
fi
|
|
done
|
|
|
|
if [ "$PORTS_FREE" = false ]; then
|
|
echo "❌ Cannot free ports. Please run cleanup script manually."
|
|
cleanup_aspire
|
|
exit 1
|
|
fi
|
|
|
|
# Clear the log and restart
|
|
echo "" > "$ASPIRE_LOG"
|
|
echo "🔄 Restarting Aspire..."
|
|
dotnet run --launch-profile http > "$ASPIRE_LOG" 2>&1 &
|
|
ASPIRE_PID=$!
|
|
echo $ASPIRE_PID > "$WORKTREE_PROJECT_ROOT/.task-pids/aspire-${TASK_ID}.pid"
|
|
echo "✅ Aspire restarted (PID: $ASPIRE_PID)"
|
|
sleep 3
|
|
break
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# Use the configured port (should match our launchSettings.json)
|
|
ASPIRE_DASHBOARD_URL="http://localhost:${ASPIRE_DASHBOARD_PORT}"
|
|
|
|
echo ""
|
|
echo "⏳ Waiting for Aspire dashboard to be ready on port $ASPIRE_DASHBOARD_PORT..."
|
|
for i in {1..30}; do
|
|
# Check the configured port
|
|
if curl -s -f "$ASPIRE_DASHBOARD_URL" > /dev/null 2>&1; then
|
|
echo "✅ Aspire dashboard is ready at $ASPIRE_DASHBOARD_URL!"
|
|
break
|
|
fi
|
|
|
|
# Show progress every 5 seconds
|
|
if [ $((i % 5)) -eq 0 ]; then
|
|
echo " Still starting... (${i}/30 seconds)"
|
|
# Show last few lines of log for progress (filter warnings)
|
|
if [ -f "$ASPIRE_LOG" ]; then
|
|
LAST_LINE=$(tail -20 "$ASPIRE_LOG" 2>/dev/null | grep -vE "(warning|Warning|WARNING|NU[0-9]|\.csproj :)" | tail -1 | cut -c1-80)
|
|
if [ -n "$LAST_LINE" ]; then
|
|
echo " Latest: $LAST_LINE"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [ $i -eq 30 ]; then
|
|
echo "⚠️ Aspire dashboard did not become ready after 30 seconds"
|
|
echo "💡 Check the log: $ASPIRE_LOG"
|
|
echo ""
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "📋 Last 50 lines of log (warnings filtered, errors highlighted):"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
# Show last 50 lines, highlight errors
|
|
tail -200 "$ASPIRE_LOG" 2>/dev/null | grep -vE "(warning|Warning|WARNING|NU[0-9]|\.csproj :)" | tail -50 || echo " (log file not found)"
|
|
echo ""
|
|
# Check for specific errors
|
|
if grep -qiE "(error|exception|failed|unhandled|address already|bind)" "$ASPIRE_LOG" 2>/dev/null; then
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "❌ ERRORS FOUND IN LOG:"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
tail -500 "$ASPIRE_LOG" 2>/dev/null | grep -iE "(error|exception|failed|unhandled|address already|bind)" | tail -20
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
fi
|
|
# Try to extract port from log
|
|
if [ -f "$ASPIRE_LOG" ]; then
|
|
LOG_PORT=$(grep -i "listening\|Now listening" "$ASPIRE_LOG" 2>/dev/null | grep -oE 'localhost:[0-9]+' | head -1 | cut -d: -f2)
|
|
if [ -n "$LOG_PORT" ]; then
|
|
ASPIRE_DASHBOARD_URL="http://localhost:${LOG_PORT}"
|
|
echo "💡 Dashboard may be at: $ASPIRE_DASHBOARD_URL (from log)"
|
|
else
|
|
echo "💡 Dashboard should be at: $ASPIRE_DASHBOARD_URL"
|
|
fi
|
|
else
|
|
echo "💡 Dashboard should be at: $ASPIRE_DASHBOARD_URL"
|
|
fi
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
# Wait for API to be ready (give it more time since Aspire needs to build first)
|
|
echo ""
|
|
echo "⏳ Waiting for API to be ready..."
|
|
API_READY=false
|
|
for i in {1..90}; do
|
|
if curl -s -f "http://localhost:${API_PORT}/alive" > /dev/null 2>&1; then
|
|
API_READY=true
|
|
echo "✅ API is ready!"
|
|
break
|
|
fi
|
|
|
|
if [ $i -eq 90 ]; then
|
|
echo "⚠️ API did not become ready after 90 seconds"
|
|
echo "💡 Check the log: $ASPIRE_LOG"
|
|
echo "💡 The API may still be building or starting"
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
# Print the Aspire dashboard URL in the format Vibe Kanban expects
|
|
# This must be printed so Vibe Kanban can detect the server is running
|
|
echo ""
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
if [ "$API_READY" = true ]; then
|
|
echo "✅ Dev server is running"
|
|
else
|
|
echo "⚠️ Dev server started (API may still be initializing)"
|
|
fi
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "$ASPIRE_DASHBOARD_URL"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
echo "📊 Additional URLs:"
|
|
echo " API: http://localhost:${API_PORT}"
|
|
echo " Swagger UI: http://localhost:${API_PORT}/swagger"
|
|
echo " Health check: http://localhost:${API_PORT}/alive"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
|
|
# Tail the Aspire log (filter out warnings for cleaner output)
|
|
echo "📋 Showing Aspire logs (Press Ctrl+C to stop and cleanup)"
|
|
echo " (Warnings are hidden for cleaner output - full logs in: $ASPIRE_LOG)"
|
|
echo ""
|
|
|
|
# Use a background process group for tail so we can kill it properly
|
|
# This ensures cleanup can kill the tail process when interrupted
|
|
(
|
|
tail -f "$ASPIRE_LOG" 2>/dev/null | grep -vE "(warning|Warning|WARNING|NU[0-9]|\.csproj :)" || {
|
|
echo "❌ Cannot read Aspire log: $ASPIRE_LOG"
|
|
echo "💡 Aspire may still be starting. Check the log manually."
|
|
cleanup_aspire
|
|
exit 1
|
|
}
|
|
) &
|
|
TAIL_PID=$!
|
|
|
|
# Save tail PID so cleanup can kill it
|
|
echo $TAIL_PID > "$WORKTREE_PROJECT_ROOT/.task-pids/tail-${TASK_ID}.pid" 2>/dev/null || true
|
|
|
|
# Wait for tail process (will be interrupted by Ctrl+C)
|
|
wait $TAIL_PID 2>/dev/null || true
|
|
|
|
# Cleanup will be called by trap, but also ensure tail is killed
|
|
kill $TAIL_PID 2>/dev/null || true
|
|
rm -f "$WORKTREE_PROJECT_ROOT/.task-pids/tail-${TASK_ID}.pid" 2>/dev/null || true
|