#!/usr/bin/env pwsh <# .SYNOPSIS Automated developer environment setup for Stella Ops (Windows). .DESCRIPTION Validates prerequisites, starts infrastructure, builds solutions and Docker images, and launches the full platform. .PARAMETER SkipBuild Skip .NET solution builds. .PARAMETER InfraOnly Only start infrastructure containers (PostgreSQL, Valkey, SeaweedFS, Rekor, Zot). .PARAMETER ImagesOnly Only build Docker images (skip infra start and .NET build). .PARAMETER SkipImages Skip Docker image builds. #> [CmdletBinding()] param( [switch]$SkipBuild, [switch]$InfraOnly, [switch]$ImagesOnly, [switch]$SkipImages ) $ErrorActionPreference = 'Stop' $Root = git rev-parse --show-toplevel 2>$null if (-not $Root) { Write-Error 'Not inside a git repository. Run this script from within the Stella Ops repo.' exit 1 } $Root = $Root.Trim() $ComposeDir = Join-Path $Root 'devops/compose' # ─── Helpers ──────────────────────────────────────────────────────────────── function Write-Step([string]$msg) { Write-Host "`n>> $msg" -ForegroundColor Cyan } function Write-Ok([string]$msg) { Write-Host " [OK] $msg" -ForegroundColor Green } function Write-Warn([string]$msg) { Write-Host " [WARN] $msg" -ForegroundColor Yellow } function Write-Fail([string]$msg) { Write-Host " [FAIL] $msg" -ForegroundColor Red } function Test-Command([string]$cmd) { return [bool](Get-Command $cmd -ErrorAction SilentlyContinue) } # ─── 1. Check prerequisites ──────────────────────────────────────────────── function Test-Prerequisites { Write-Step 'Checking prerequisites' $allGood = $true # dotnet if (Test-Command 'dotnet') { $v = (dotnet --version 2>$null) if ($v -match '^10\.') { Write-Ok "dotnet $v" } else { Write-Fail "dotnet $v found, but 10.x is required" $allGood = $false } } else { Write-Fail 'dotnet SDK not found. Install .NET 10 SDK.' $allGood = $false } # node if (Test-Command 'node') { $v = (node --version 2>$null).TrimStart('v') $major = [int]($v -split '\.')[0] if ($major -ge 20) { Write-Ok "node $v" } else { Write-Fail "node $v found, but 20+ is required" $allGood = $false } } else { Write-Fail 'node not found. Install Node.js 20+.' $allGood = $false } # npm if (Test-Command 'npm') { $v = (npm --version 2>$null) $major = [int]($v -split '\.')[0] if ($major -ge 10) { Write-Ok "npm $v" } else { Write-Fail "npm $v found, but 10+ is required" $allGood = $false } } else { Write-Fail 'npm not found.' $allGood = $false } # docker if (Test-Command 'docker') { $v = (docker --version 2>$null) Write-Ok "docker: $v" } else { Write-Fail 'docker not found. Install Docker Desktop.' $allGood = $false } # docker compose $composeOk = $false try { $null = docker compose version 2>$null if ($LASTEXITCODE -eq 0) { $composeOk = $true } } catch {} if ($composeOk) { Write-Ok 'docker compose available' } else { Write-Fail 'docker compose not available. Ensure Docker Desktop includes Compose V2.' $allGood = $false } # git if (Test-Command 'git') { Write-Ok "git $(git --version 2>$null)" } else { Write-Fail 'git not found.' $allGood = $false } if (-not $allGood) { Write-Error 'Prerequisites not met. Install missing tools and re-run.' exit 1 } } # ─── 2. Check hosts file ─────────────────────────────────────────────────── function Test-HostsFile { Write-Step 'Checking hosts file for stella-ops.local entries' $hostsPath = 'C:\Windows\System32\drivers\etc\hosts' if (Test-Path $hostsPath) { $content = Get-Content $hostsPath -Raw if ($content -match 'stella-ops\.local') { Write-Ok 'stella-ops.local entries found in hosts file' } else { Write-Warn 'stella-ops.local entries NOT found in hosts file.' Write-Host ' Add the hosts block from docs/dev/DEV_ENVIRONMENT_SETUP.md section 2' -ForegroundColor Yellow Write-Host ' to C:\Windows\System32\drivers\etc\hosts (run editor as Administrator)' -ForegroundColor Yellow } } else { Write-Warn "Cannot read hosts file at $hostsPath" } } # ─── 3. Ensure .env ──────────────────────────────────────────────────────── function Initialize-EnvFile { Write-Step 'Ensuring .env file exists' $envFile = Join-Path $ComposeDir '.env' $envExample = Join-Path $ComposeDir 'env/stellaops.env.example' if (Test-Path $envFile) { Write-Ok ".env already exists at $envFile" } elseif (Test-Path $envExample) { Copy-Item $envExample $envFile Write-Ok "Copied $envExample -> $envFile" Write-Warn 'Review .env and change POSTGRES_PASSWORD at minimum.' } else { Write-Fail "Neither .env nor env/stellaops.env.example found in $ComposeDir" exit 1 } } # ─── 4. Start infrastructure ─────────────────────────────────────────────── function Start-Infrastructure { Write-Step 'Starting infrastructure containers (docker-compose.dev.yml)' Push-Location $ComposeDir try { docker compose -f docker-compose.dev.yml up -d if ($LASTEXITCODE -ne 0) { Write-Fail 'Failed to start infrastructure containers.' exit 1 } Write-Host ' Waiting for containers to become healthy...' -ForegroundColor Gray $maxWait = 120 $elapsed = 0 while ($elapsed -lt $maxWait) { $ps = docker compose -f docker-compose.dev.yml ps --format json 2>$null if ($ps) { $allHealthy = $true # docker compose ps --format json outputs one JSON object per line foreach ($line in $ps -split "`n") { $line = $line.Trim() if (-not $line) { continue } try { $svc = $line | ConvertFrom-Json if ($svc.Health -and $svc.Health -ne 'healthy') { $allHealthy = $false } } catch {} } if ($allHealthy -and $elapsed -gt 5) { Write-Ok 'All infrastructure containers healthy' return } } Start-Sleep -Seconds 5 $elapsed += 5 } Write-Warn "Timed out waiting for healthy status after ${maxWait}s. Check with: docker compose -f docker-compose.dev.yml ps" } finally { Pop-Location } } # ─── 5. Build .NET solutions ─────────────────────────────────────────────── function Build-Solutions { Write-Step 'Building all .NET solutions' $buildScript = Join-Path $Root 'scripts/build-all-solutions.ps1' if (Test-Path $buildScript) { & $buildScript if ($LASTEXITCODE -ne 0) { Write-Fail '.NET solution build failed.' exit 1 } Write-Ok '.NET solutions built successfully' } else { Write-Warn "Build script not found at $buildScript. Skipping .NET build." } } # ─── 6. Build Docker images ──────────────────────────────────────────────── function Build-Images { Write-Step 'Building Docker images' $buildScript = Join-Path $Root 'devops/docker/build-all.ps1' if (Test-Path $buildScript) { & $buildScript if ($LASTEXITCODE -ne 0) { Write-Fail 'Docker image build failed.' exit 1 } Write-Ok 'Docker images built successfully' } else { Write-Warn "Build script not found at $buildScript. Skipping image build." } } # ─── 7. Start full platform ──────────────────────────────────────────────── function Start-Platform { Write-Step 'Starting full Stella Ops platform' Push-Location $ComposeDir try { docker compose -f docker-compose.stella-ops.yml up -d if ($LASTEXITCODE -ne 0) { Write-Fail 'Failed to start platform services.' exit 1 } Write-Ok 'Platform services started' } finally { Pop-Location } } # ─── 8. Smoke test ───────────────────────────────────────────────────────── function Test-Smoke { Write-Step 'Running smoke tests' $endpoints = @( @{ Name = 'PostgreSQL'; Cmd = { docker exec stellaops-dev-postgres pg_isready -U stellaops 2>$null; $LASTEXITCODE -eq 0 } }, @{ Name = 'Valkey'; Cmd = { $r = docker exec stellaops-dev-valkey valkey-cli ping 2>$null; $r -eq 'PONG' } } ) foreach ($ep in $endpoints) { try { $ok = & $ep.Cmd if ($ok) { Write-Ok $ep.Name } else { Write-Warn "$($ep.Name) not responding" } } catch { Write-Warn "$($ep.Name) check failed: $_" } } } # ─── Main ─────────────────────────────────────────────────────────────────── Write-Host '=============================================' -ForegroundColor Cyan Write-Host ' Stella Ops Developer Environment Setup' -ForegroundColor Cyan Write-Host '=============================================' -ForegroundColor Cyan Test-Prerequisites Test-HostsFile if ($ImagesOnly) { Build-Images Write-Host "`nDone (images only)." -ForegroundColor Green exit 0 } Initialize-EnvFile Start-Infrastructure if ($InfraOnly) { Test-Smoke Write-Host "`nDone (infra only). Infrastructure is running." -ForegroundColor Green exit 0 } if (-not $SkipBuild) { Build-Solutions } if (-not $SkipImages) { Build-Images } Start-Platform Test-Smoke Write-Host "`n=============================================" -ForegroundColor Green Write-Host ' Setup complete!' -ForegroundColor Green Write-Host ' Platform: https://stella-ops.local' -ForegroundColor Green Write-Host ' Docs: docs/dev/DEV_ENVIRONMENT_SETUP.md' -ForegroundColor Green Write-Host '=============================================' -ForegroundColor Green