<# .SYNOPSIS Starts the Likwid development environment. .DESCRIPTION Starts PostgreSQL (via Podman), backend (Rust/Axum), and frontend (Astro). Use dev-stop.ps1 to stop all services. #> [CmdletBinding()] param() $ErrorActionPreference = 'Stop' # Paths $root = Split-Path -Parent $PSScriptRoot $stateDir = Join-Path $PSScriptRoot '.dev' $stateFile = Join-Path $stateDir 'state.json' $rootPattern = [regex]::Escape($root) $backendLog = Join-Path $stateDir 'backend.log' $backendErrLog = Join-Path $stateDir 'backend.err.log' $frontendLog = Join-Path $stateDir 'frontend.log' $frontendErrLog = Join-Path $stateDir 'frontend.err.log' New-Item -ItemType Directory -Force -Path $stateDir | Out-Null function Invoke-Compose { param( [Parameter(Mandatory=$true)][string[]]$Args ) $podmanCompose = Get-Command podman-compose -ErrorAction SilentlyContinue if ($podmanCompose) { & podman-compose @Args return $LASTEXITCODE } $podman = Get-Command podman -ErrorAction SilentlyContinue if ($podman) { & podman compose @Args return $LASTEXITCODE } throw 'Neither podman-compose nor podman was found in PATH.' } function Get-CommandLine { param( [Parameter(Mandatory=$true)][int]$ProcessId ) try { $p = Get-CimInstance Win32_Process -Filter "ProcessId=$ProcessId" -ErrorAction Stop return $p.CommandLine } catch { return $null } } function Get-ListeningProcessIdsOnPort { param( [Parameter(Mandatory=$true)][int]$Port ) $connections = netstat -ano 2>$null | Select-String ":$Port\s+.*LISTENING" $pids = @() foreach ($conn in $connections) { $parts = ($conn.Line -replace '\s+', ' ').Trim().Split(' ') $listenerPid = 0 [int]::TryParse($parts[-1], [ref]$listenerPid) | Out-Null if ($listenerPid -gt 0) { $pids += $listenerPid } } return ($pids | Select-Object -Unique) } function Test-LikwidProcess { param( [Parameter(Mandatory=$true)][int]$ProcessId ) $cmd = Get-CommandLine -ProcessId $ProcessId if (-not $cmd) { return $false } if ($cmd -match $rootPattern) { return $true } if ($cmd -match 'npm run dev') { return $true } if ($cmd -match 'astro') { return $true } if ($cmd -match 'cargo') { return $true } return $false } # Environment if (-not $env:POSTGRES_USER) { $env:POSTGRES_USER = 'likwid' } if (-not $env:POSTGRES_PASSWORD) { $env:POSTGRES_PASSWORD = 'likwid' } if (-not $env:POSTGRES_DB) { $env:POSTGRES_DB = 'likwid' } if (-not $env:JWT_SECRET) { $env:JWT_SECRET = 'dev_jwt_secret_not_for_production' } $env:DATABASE_URL = "postgres://$($env:POSTGRES_USER):$($env:POSTGRES_PASSWORD)@127.0.0.1:5432/$($env:POSTGRES_DB)" # Check if already running if (Test-Path $stateFile) { $state = Get-Content -Raw $stateFile | ConvertFrom-Json -ErrorAction SilentlyContinue $backendAlive = $false $frontendAlive = $false if ($state -and $state.backendPid) { $p = Get-Process -Id $state.backendPid -ErrorAction SilentlyContinue if ($p) { $backendAlive = $true } } if ($state -and $state.frontendPid) { $p = Get-Process -Id $state.frontendPid -ErrorAction SilentlyContinue if ($p) { $frontendAlive = $true } } if ($backendAlive -or $frontendAlive) { Write-Host "Already running. Run dev-stop.ps1 first." exit 0 } Remove-Item -Force $stateFile -ErrorAction SilentlyContinue } # Guard against duplicate starts / port conflicts foreach ($port in @(3000, 4321)) { $pids = Get-ListeningProcessIdsOnPort -Port $port if (-not $pids -or $pids.Count -eq 0) { continue } $likwidPid = $pids | Where-Object { Test-LikwidProcess -ProcessId $_ } | Select-Object -First 1 if ($likwidPid) { Write-Host "Already running (detected Likwid process PID $likwidPid listening on port $port). Run dev-stop.ps1 first." exit 0 } $pidList = ($pids -join ', ') throw "Port $port is already in use by PID(s): $pidList. Stop the process or change ports before starting Likwid." } # Start Podman machine if needed try { $machines = podman machine list --format json 2>$null | ConvertFrom-Json if (-not $machines) { throw 'No Podman machines found.' } $running = $machines | Where-Object { $_.Running -eq $true } if (-not $running) { $name = ($machines | Select-Object -First 1).Name if (-not $name) { throw 'No Podman machine name found.' } Write-Host "Starting Podman machine..." podman machine start $name 2>$null } } catch { Write-Host "Podman machine check failed (may already be running)" } # Start PostgreSQL container Write-Host "Starting PostgreSQL..." $composeFile = Join-Path $root 'compose/dev.yml' $composeExitCode = 0 try { $composeExitCode = Invoke-Compose -Args @('-f', $composeFile, 'up', '-d') } catch { $composeExitCode = $LASTEXITCODE } if ($composeExitCode -ne 0) { $containerExistsExitCode = 0 try { podman container exists likwid-postgres 2>$null $containerExistsExitCode = $LASTEXITCODE } catch { $containerExistsExitCode = $LASTEXITCODE } if ($containerExistsExitCode -ne 0) { throw "Failed to start PostgreSQL via Podman Compose (exit code: $composeExitCode)." } Write-Host "Warning: Podman Compose returned exit code $composeExitCode, but likwid-postgres exists. Continuing." } # Wait for PostgreSQL to be ready $maxWait = 30 for ($i = 0; $i -lt $maxWait; $i++) { podman exec likwid-postgres pg_isready -U $env:POSTGRES_USER -d $env:POSTGRES_DB 2>$null | Out-Null if ($LASTEXITCODE -eq 0) { break } Start-Sleep -Seconds 1 } podman exec likwid-postgres pg_isready -U $env:POSTGRES_USER -d $env:POSTGRES_DB 2>$null | Out-Null if ($LASTEXITCODE -ne 0) { Write-Host "PostgreSQL did not become ready." podman logs --tail 60 likwid-postgres 2>$null throw "PostgreSQL startup timed out." } Write-Host "Running database migrations..." Push-Location (Join-Path $root 'backend') try { if (-not (Get-Command sqlx -ErrorAction SilentlyContinue)) { throw "sqlx CLI not found in PATH. Install with: cargo install sqlx-cli --no-default-features --features postgres" } sqlx migrate run --ignore-missing if ($LASTEXITCODE -ne 0) { throw "Failed to run database migrations (sqlx migrate run)." } } finally { Pop-Location } # Start backend Write-Host "Starting backend..." $backend = Start-Process -FilePath 'cmd.exe' ` -ArgumentList '/c', 'cargo', 'run' ` -WorkingDirectory (Join-Path $root 'backend') ` -PassThru -WindowStyle Hidden ` -RedirectStandardOutput $backendLog ` -RedirectStandardError $backendErrLog # Start frontend Write-Host "Starting frontend..." $frontend = Start-Process -FilePath 'cmd.exe' ` -ArgumentList '/c', "set PUBLIC_API_BASE=http://127.0.0.1:3000&& npm run dev" ` -WorkingDirectory (Join-Path $root 'frontend') ` -PassThru -WindowStyle Hidden ` -RedirectStandardOutput $frontendLog ` -RedirectStandardError $frontendErrLog # Save state @{ backendPid = $backend.Id frontendPid = $frontend.Id startedAt = (Get-Date).ToString('o') } | ConvertTo-Json | Set-Content -Encoding UTF8 $stateFile # Brief wait for startup Start-Sleep -Seconds 3 # Status report Write-Host "" Write-Host "=== Likwid Dev Environment ===" if (-not $backend.HasExited) { Write-Host "Backend: Running (PID $($backend.Id)) - http://127.0.0.1:3000" } else { Write-Host "Backend: FAILED - check $backendLog" } if (-not $frontend.HasExited) { Write-Host "Frontend: Running (PID $($frontend.Id)) - http://localhost:4321" } else { Write-Host "Frontend: FAILED - check $frontendLog" } Write-Host "PostgreSQL: Running on port 5432" Write-Host "" Write-Host "Logs: $stateDir" Write-Host "Stop: .\scripts\dev-stop.ps1"