hasari-api / scripts /dev-up.ps1
erdoganpeker's picture
v0.3.0 — multimodal vehicle damage MVP
e327f0d
# scripts/dev-up.ps1
# ---------------------------------------------------------------------------
# Smoke up + health verification for the local docker compose stack.
#
# Usage (from repo root):
# pwsh -File scripts/dev-up.ps1
# pwsh -File scripts/dev-up.ps1 -Rebuild # force --build
# pwsh -File scripts/dev-up.ps1 -InfraOnly # only postgres/redis/minio
#
# What it does:
# 1. Pre-flight: docker daemon, port collisions (5432/6379/9000/9001/8000),
# ML model snapshot directory.
# 2. docker compose up -d (infra first, then backend + worker).
# 3. Poll health endpoints:
# - postgres pg_isready
# - redis PING
# - minio /minio/health/live
# - backend http://localhost:8000/health
# 4. Print summary URLs.
# ---------------------------------------------------------------------------
[CmdletBinding()]
param(
[switch]$Rebuild,
[switch]$InfraOnly
)
$ErrorActionPreference = "Stop"
$RepoRoot = (Resolve-Path "$PSScriptRoot\..").Path
Push-Location $RepoRoot
function Section($msg) { Write-Host "`n=== $msg ===" -ForegroundColor Cyan }
function OK($msg) { Write-Host " ok: $msg" -ForegroundColor Green }
function Warn($msg) { Write-Host " warn: $msg" -ForegroundColor Yellow }
function Fail($msg) { Write-Host " FAIL: $msg" -ForegroundColor Red }
function Test-Port($port) {
$listener = $null
try {
$listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, $port)
$listener.Start()
return $true # free
} catch {
return $false # in use
} finally {
if ($listener) { $listener.Stop() }
}
}
# ---------- 1. Pre-flight ----------
Section "Pre-flight checks"
try { docker info --format "{{.ServerVersion}}" | Out-Null; OK "docker daemon reachable" }
catch { Fail "docker daemon not reachable. Start Docker Desktop."; Pop-Location; exit 1 }
$ports = @(5432, 6379, 9000, 9001, 8000)
$collisions = @()
foreach ($p in $ports) {
if (-not (Test-Port $p)) {
$collisions += $p
Warn "port $p is already in use on the host"
} else {
OK "port $p free"
}
}
if ($collisions.Count -gt 0) {
Warn "Port collisions: $($collisions -join ', '). 'docker compose up' will fail to bind these."
Warn "Stop the conflicting process or edit docker-compose.yml port mappings."
}
# Model snapshot dir check (override via MODEL_SNAPSHOT_DIR)
$defaultSnapshot = Join-Path $RepoRoot "services\ml\runs\bundles\full_20260515_044630\_SNAPSHOT_FOR_BUILD"
$snapshot = if ($env:MODEL_SNAPSHOT_DIR) { $env:MODEL_SNAPSHOT_DIR } else { $defaultSnapshot }
if (-not (Test-Path $snapshot)) {
Warn "ML snapshot dir missing: $snapshot"
# Try fallback: most recent bundle under services/ml/runs/bundles/
$bundleRoot = Join-Path $RepoRoot "services\ml\runs\bundles"
if (Test-Path $bundleRoot) {
$candidate = Get-ChildItem $bundleRoot -Directory -Filter "full_*" |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1
if ($candidate) {
$fallback = Join-Path $candidate.FullName "_SNAPSHOT_FOR_BUILD"
if (Test-Path $fallback) {
$env:MODEL_SNAPSHOT_DIR = $fallback
OK "fallback snapshot dir set: $fallback"
} else {
Warn "No _SNAPSHOT_FOR_BUILD under $($candidate.FullName). Backend will start without ML weights."
}
}
}
} else {
OK "model snapshot dir: $snapshot"
}
# ---------- 2. Compose up ----------
Section "docker compose up (infrastructure)"
$buildFlag = if ($Rebuild) { "--build" } else { "" }
# Infra first so dependencies resolve cleanly.
$infraServices = @("postgres", "redis", "minio", "minio-init")
& docker compose up -d @infraServices
if ($LASTEXITCODE -ne 0) { Fail "docker compose up (infra) failed"; Pop-Location; exit 1 }
OK "infra services started"
if (-not $InfraOnly) {
Section "docker compose up (backend + worker)"
$appServices = @("backend", "worker")
if ($Rebuild) {
& docker compose up -d --build @appServices
} else {
& docker compose up -d @appServices
}
if ($LASTEXITCODE -ne 0) { Fail "docker compose up (app) failed"; Pop-Location; exit 1 }
OK "backend + worker started"
}
# ---------- 3. Health checks ----------
Section "Health checks"
function Wait-For($name, [ScriptBlock]$probe, [int]$timeoutSec = 90) {
$deadline = (Get-Date).AddSeconds($timeoutSec)
while ((Get-Date) -lt $deadline) {
try {
if (& $probe) { OK "$name healthy"; return $true }
} catch { }
Start-Sleep -Seconds 2
}
Fail "$name did not become healthy within ${timeoutSec}s"
return $false
}
$allHealthy = $true
$allHealthy = (Wait-For "postgres" {
docker exec hasarui-postgres pg_isready -U postgres -d arac_hasar 2>&1 | Out-Null
$LASTEXITCODE -eq 0
} 60) -and $allHealthy
$allHealthy = (Wait-For "redis" {
(docker exec hasarui-redis redis-cli ping 2>&1).Trim() -eq "PONG"
} 30) -and $allHealthy
$allHealthy = (Wait-For "minio" {
try {
$r = Invoke-WebRequest "http://localhost:9000/minio/health/live" -UseBasicParsing -TimeoutSec 3
$r.StatusCode -eq 200
} catch { $false }
} 45) -and $allHealthy
if (-not $InfraOnly) {
$allHealthy = (Wait-For "backend /health" {
try {
$r = Invoke-WebRequest "http://localhost:8000/health" -UseBasicParsing -TimeoutSec 3
$r.StatusCode -eq 200
} catch { $false }
} 180) -and $allHealthy
}
# ---------- 4. Summary ----------
Section "Summary"
docker compose ps
Write-Host ""
Write-Host "URLs:" -ForegroundColor Cyan
Write-Host " API health -> http://localhost:8000/health"
Write-Host " API docs -> http://localhost:8000/docs"
Write-Host " MinIO console -> http://localhost:9001 (minioadmin / minioadmin)"
Write-Host " Postgres -> postgresql://postgres:postgres@localhost:5432/arac_hasar"
Write-Host ""
Write-Host "Next: pnpm --filter @arac-hasar/web dev # http://localhost:3000"
Pop-Location
if (-not $allHealthy) { exit 1 }