338 lines
11 KiB
PowerShell
338 lines
11 KiB
PowerShell
#!/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
|