part #2
This commit is contained in:
94
scripts/build-all-solutions.ps1
Normal file
94
scripts/build-all-solutions.ps1
Normal file
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env pwsh
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Builds (and optionally tests) all module solutions under src/.
|
||||
|
||||
.DESCRIPTION
|
||||
Discovers all *.sln files under src/ (excluding the root StellaOps.sln)
|
||||
and runs dotnet build on each. Pass -Test to also run dotnet test.
|
||||
|
||||
.PARAMETER Test
|
||||
Also run dotnet test on each solution after building.
|
||||
|
||||
.PARAMETER Configuration
|
||||
Build configuration. Defaults to Debug.
|
||||
|
||||
.EXAMPLE
|
||||
.\scripts\build-all-solutions.ps1
|
||||
.\scripts\build-all-solutions.ps1 -Test
|
||||
.\scripts\build-all-solutions.ps1 -Test -Configuration Release
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[switch]$Test,
|
||||
[ValidateSet('Debug', 'Release')]
|
||||
[string]$Configuration = 'Debug'
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Continue'
|
||||
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
$srcDir = Join-Path $repoRoot 'src'
|
||||
|
||||
$solutions = Get-ChildItem -Path $srcDir -Filter '*.sln' -Recurse |
|
||||
Where-Object { $_.Name -ne 'StellaOps.sln' } |
|
||||
Sort-Object FullName
|
||||
|
||||
if ($solutions.Count -eq 0) {
|
||||
Write-Error 'No solution files found under src/.'
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "Found $($solutions.Count) solution(s) to build." -ForegroundColor Cyan
|
||||
Write-Host ''
|
||||
|
||||
$buildPass = @()
|
||||
$buildFail = @()
|
||||
$testPass = @()
|
||||
$testFail = @()
|
||||
$testSkipped = @()
|
||||
|
||||
foreach ($sln in $solutions) {
|
||||
$rel = [System.IO.Path]::GetRelativePath($repoRoot, $sln.FullName)
|
||||
Write-Host "--- BUILD: $rel ---" -ForegroundColor Yellow
|
||||
|
||||
dotnet build $sln.FullName --configuration $Configuration --nologo -v quiet
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
$buildPass += $rel
|
||||
} else {
|
||||
$buildFail += $rel
|
||||
Write-Host " FAILED" -ForegroundColor Red
|
||||
continue # skip test if build failed
|
||||
}
|
||||
|
||||
if ($Test) {
|
||||
Write-Host "--- TEST: $rel ---" -ForegroundColor Yellow
|
||||
dotnet test $sln.FullName --configuration $Configuration --nologo --no-build -v quiet
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
$testPass += $rel
|
||||
} else {
|
||||
$testFail += $rel
|
||||
Write-Host " TEST FAILED" -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ''
|
||||
Write-Host '========== Summary ==========' -ForegroundColor Cyan
|
||||
Write-Host "Build passed : $($buildPass.Count)" -ForegroundColor Green
|
||||
if ($buildFail.Count -gt 0) {
|
||||
Write-Host "Build failed : $($buildFail.Count)" -ForegroundColor Red
|
||||
$buildFail | ForEach-Object { Write-Host " - $_" -ForegroundColor Red }
|
||||
}
|
||||
if ($Test) {
|
||||
Write-Host "Test passed : $($testPass.Count)" -ForegroundColor Green
|
||||
if ($testFail.Count -gt 0) {
|
||||
Write-Host "Test failed : $($testFail.Count)" -ForegroundColor Red
|
||||
$testFail | ForEach-Object { Write-Host " - $_" -ForegroundColor Red }
|
||||
}
|
||||
}
|
||||
|
||||
if ($buildFail.Count -gt 0 -or $testFail.Count -gt 0) {
|
||||
exit 1
|
||||
}
|
||||
82
scripts/build-all-solutions.sh
Normal file
82
scripts/build-all-solutions.sh
Normal file
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Build (and optionally test) all module solutions under src/.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/build-all-solutions.sh # build only
|
||||
# ./scripts/build-all-solutions.sh --test # build + test
|
||||
# ./scripts/build-all-solutions.sh --test --configuration Release
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
SRC_DIR="$REPO_ROOT/src"
|
||||
|
||||
RUN_TESTS=false
|
||||
CONFIGURATION="Debug"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--test|-t) RUN_TESTS=true; shift ;;
|
||||
--configuration|-c) CONFIGURATION="$2"; shift 2 ;;
|
||||
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Discover solutions (exclude root StellaOps.sln)
|
||||
mapfile -t SOLUTIONS < <(find "$SRC_DIR" -name '*.sln' ! -name 'StellaOps.sln' | sort)
|
||||
|
||||
if [[ ${#SOLUTIONS[@]} -eq 0 ]]; then
|
||||
echo "ERROR: No solution files found under src/." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Found ${#SOLUTIONS[@]} solution(s) to build."
|
||||
echo ""
|
||||
|
||||
build_pass=()
|
||||
build_fail=()
|
||||
test_pass=()
|
||||
test_fail=()
|
||||
|
||||
for sln in "${SOLUTIONS[@]}"; do
|
||||
rel="${sln#"$REPO_ROOT/"}"
|
||||
echo "--- BUILD: $rel ---"
|
||||
|
||||
if dotnet build "$sln" --configuration "$CONFIGURATION" --nologo -v quiet; then
|
||||
build_pass+=("$rel")
|
||||
else
|
||||
build_fail+=("$rel")
|
||||
echo " FAILED"
|
||||
continue
|
||||
fi
|
||||
|
||||
if $RUN_TESTS; then
|
||||
echo "--- TEST: $rel ---"
|
||||
if dotnet test "$sln" --configuration "$CONFIGURATION" --nologo --no-build -v quiet; then
|
||||
test_pass+=("$rel")
|
||||
else
|
||||
test_fail+=("$rel")
|
||||
echo " TEST FAILED"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "========== Summary =========="
|
||||
echo "Build passed : ${#build_pass[@]}"
|
||||
if [[ ${#build_fail[@]} -gt 0 ]]; then
|
||||
echo "Build failed : ${#build_fail[@]}"
|
||||
for f in "${build_fail[@]}"; do echo " - $f"; done
|
||||
fi
|
||||
if $RUN_TESTS; then
|
||||
echo "Test passed : ${#test_pass[@]}"
|
||||
if [[ ${#test_fail[@]} -gt 0 ]]; then
|
||||
echo "Test failed : ${#test_fail[@]}"
|
||||
for f in "${test_fail[@]}"; do echo " - $f"; done
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ${#build_fail[@]} -gt 0 ]] || [[ ${#test_fail[@]} -gt 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
337
scripts/setup.ps1
Normal file
337
scripts/setup.ps1
Normal file
@@ -0,0 +1,337 @@
|
||||
#!/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
|
||||
294
scripts/setup.sh
Normal file
294
scripts/setup.sh
Normal file
@@ -0,0 +1,294 @@
|
||||
#!/usr/bin/env bash
|
||||
# Automated developer environment setup for Stella Ops (Linux/macOS).
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/setup.sh [--skip-build] [--infra-only] [--images-only] [--skip-images]
|
||||
set -euo pipefail
|
||||
|
||||
# ─── Parse flags ────────────────────────────────────────────────────────────
|
||||
|
||||
SKIP_BUILD=false
|
||||
INFRA_ONLY=false
|
||||
IMAGES_ONLY=false
|
||||
SKIP_IMAGES=false
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--skip-build) SKIP_BUILD=true ;;
|
||||
--infra-only) INFRA_ONLY=true ;;
|
||||
--images-only) IMAGES_ONLY=true ;;
|
||||
--skip-images) SKIP_IMAGES=true ;;
|
||||
-h|--help)
|
||||
echo "Usage: $0 [--skip-build] [--infra-only] [--images-only] [--skip-images]"
|
||||
exit 0
|
||||
;;
|
||||
*) echo "Unknown flag: $arg" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
ROOT=$(git rev-parse --show-toplevel 2>/dev/null || true)
|
||||
if [[ -z "$ROOT" ]]; then
|
||||
echo "ERROR: Not inside a git repository." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
COMPOSE_DIR="${ROOT}/devops/compose"
|
||||
|
||||
# ─── Helpers ────────────────────────────────────────────────────────────────
|
||||
|
||||
step() { printf '\n\033[1;36m>> %s\033[0m\n' "$1"; }
|
||||
ok() { printf ' \033[0;32m[OK]\033[0m %s\n' "$1"; }
|
||||
warn() { printf ' \033[0;33m[WARN]\033[0m %s\n' "$1"; }
|
||||
fail() { printf ' \033[0;31m[FAIL]\033[0m %s\n' "$1"; }
|
||||
|
||||
has_cmd() { command -v "$1" &>/dev/null; }
|
||||
|
||||
# ─── 1. Check prerequisites ────────────────────────────────────────────────
|
||||
|
||||
check_prerequisites() {
|
||||
step 'Checking prerequisites'
|
||||
local all_good=true
|
||||
|
||||
# dotnet
|
||||
if has_cmd dotnet; then
|
||||
local v; v=$(dotnet --version 2>/dev/null)
|
||||
if [[ "$v" =~ ^10\. ]]; then
|
||||
ok "dotnet $v"
|
||||
else
|
||||
fail "dotnet $v found, but 10.x is required"
|
||||
all_good=false
|
||||
fi
|
||||
else
|
||||
fail 'dotnet SDK not found. Install .NET 10 SDK.'
|
||||
all_good=false
|
||||
fi
|
||||
|
||||
# node
|
||||
if has_cmd node; then
|
||||
local v; v=$(node --version 2>/dev/null | sed 's/^v//')
|
||||
local major; major=$(echo "$v" | cut -d. -f1)
|
||||
if (( major >= 20 )); then
|
||||
ok "node $v"
|
||||
else
|
||||
fail "node $v found, but 20+ is required"
|
||||
all_good=false
|
||||
fi
|
||||
else
|
||||
fail 'node not found. Install Node.js 20+.'
|
||||
all_good=false
|
||||
fi
|
||||
|
||||
# npm
|
||||
if has_cmd npm; then
|
||||
local v; v=$(npm --version 2>/dev/null)
|
||||
local major; major=$(echo "$v" | cut -d. -f1)
|
||||
if (( major >= 10 )); then
|
||||
ok "npm $v"
|
||||
else
|
||||
fail "npm $v found, but 10+ is required"
|
||||
all_good=false
|
||||
fi
|
||||
else
|
||||
fail 'npm not found.'
|
||||
all_good=false
|
||||
fi
|
||||
|
||||
# docker
|
||||
if has_cmd docker; then
|
||||
ok "docker: $(docker --version 2>/dev/null)"
|
||||
else
|
||||
fail 'docker not found. Install Docker.'
|
||||
all_good=false
|
||||
fi
|
||||
|
||||
# docker compose
|
||||
if docker compose version &>/dev/null; then
|
||||
ok 'docker compose available'
|
||||
else
|
||||
fail 'docker compose not available. Install Compose V2.'
|
||||
all_good=false
|
||||
fi
|
||||
|
||||
# git
|
||||
if has_cmd git; then
|
||||
ok "$(git --version 2>/dev/null)"
|
||||
else
|
||||
fail 'git not found.'
|
||||
all_good=false
|
||||
fi
|
||||
|
||||
if [[ "$all_good" != "true" ]]; then
|
||||
echo 'ERROR: Prerequisites not met. Install missing tools and re-run.' >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── 2. Check hosts file ───────────────────────────────────────────────────
|
||||
|
||||
check_hosts() {
|
||||
step 'Checking hosts file for stella-ops.local entries'
|
||||
if grep -q 'stella-ops\.local' /etc/hosts 2>/dev/null; then
|
||||
ok 'stella-ops.local entries found in /etc/hosts'
|
||||
else
|
||||
warn 'stella-ops.local entries NOT found in /etc/hosts.'
|
||||
echo ' Add the hosts block from docs/dev/DEV_ENVIRONMENT_SETUP.md section 2'
|
||||
echo ' to /etc/hosts (use sudo).'
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── 3. Ensure .env ────────────────────────────────────────────────────────
|
||||
|
||||
ensure_env() {
|
||||
step 'Ensuring .env file exists'
|
||||
local env_file="${COMPOSE_DIR}/.env"
|
||||
local env_example="${COMPOSE_DIR}/env/stellaops.env.example"
|
||||
|
||||
if [[ -f "$env_file" ]]; then
|
||||
ok ".env already exists at $env_file"
|
||||
elif [[ -f "$env_example" ]]; then
|
||||
cp "$env_example" "$env_file"
|
||||
ok "Copied $env_example -> $env_file"
|
||||
warn 'Review .env and change POSTGRES_PASSWORD at minimum.'
|
||||
else
|
||||
fail "Neither .env nor env/stellaops.env.example found in $COMPOSE_DIR"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── 4. Start infrastructure ───────────────────────────────────────────────
|
||||
|
||||
start_infra() {
|
||||
step 'Starting infrastructure containers (docker-compose.dev.yml)'
|
||||
cd "$COMPOSE_DIR"
|
||||
|
||||
docker compose -f docker-compose.dev.yml up -d
|
||||
|
||||
echo ' Waiting for containers to become healthy...'
|
||||
local max_wait=120
|
||||
local elapsed=0
|
||||
while (( elapsed < max_wait )); do
|
||||
local all_healthy=true
|
||||
while IFS= read -r line; do
|
||||
[[ -z "$line" ]] && continue
|
||||
local health; health=$(echo "$line" | python3 -c "import sys,json; print(json.load(sys.stdin).get('Health',''))" 2>/dev/null || true)
|
||||
if [[ -n "$health" && "$health" != "healthy" ]]; then
|
||||
all_healthy=false
|
||||
fi
|
||||
done < <(docker compose -f docker-compose.dev.yml ps --format json 2>/dev/null)
|
||||
|
||||
if [[ "$all_healthy" == "true" && $elapsed -gt 5 ]]; then
|
||||
ok 'All infrastructure containers healthy'
|
||||
cd "$ROOT"
|
||||
return
|
||||
fi
|
||||
sleep 5
|
||||
elapsed=$((elapsed + 5))
|
||||
done
|
||||
warn "Timed out waiting for healthy status after ${max_wait}s."
|
||||
cd "$ROOT"
|
||||
}
|
||||
|
||||
# ─── 5. Build .NET solutions ───────────────────────────────────────────────
|
||||
|
||||
build_solutions() {
|
||||
step 'Building all .NET solutions'
|
||||
local script="${ROOT}/scripts/build-all-solutions.sh"
|
||||
if [[ -x "$script" ]]; then
|
||||
"$script"
|
||||
ok '.NET solutions built successfully'
|
||||
elif [[ -f "$script" ]]; then
|
||||
bash "$script"
|
||||
ok '.NET solutions built successfully'
|
||||
else
|
||||
warn "Build script not found at $script. Skipping .NET build."
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── 6. Build Docker images ────────────────────────────────────────────────
|
||||
|
||||
build_images() {
|
||||
step 'Building Docker images'
|
||||
local script="${ROOT}/devops/docker/build-all.sh"
|
||||
if [[ -x "$script" ]]; then
|
||||
"$script"
|
||||
ok 'Docker images built successfully'
|
||||
elif [[ -f "$script" ]]; then
|
||||
bash "$script"
|
||||
ok 'Docker images built successfully'
|
||||
else
|
||||
warn "Build script not found at $script. Skipping image build."
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── 7. Start full platform ────────────────────────────────────────────────
|
||||
|
||||
start_platform() {
|
||||
step 'Starting full Stella Ops platform'
|
||||
cd "$COMPOSE_DIR"
|
||||
docker compose -f docker-compose.stella-ops.yml up -d
|
||||
ok 'Platform services started'
|
||||
cd "$ROOT"
|
||||
}
|
||||
|
||||
# ─── 8. Smoke test ─────────────────────────────────────────────────────────
|
||||
|
||||
smoke_test() {
|
||||
step 'Running smoke tests'
|
||||
|
||||
if docker exec stellaops-dev-postgres pg_isready -U stellaops &>/dev/null; then
|
||||
ok 'PostgreSQL'
|
||||
else
|
||||
warn 'PostgreSQL not responding'
|
||||
fi
|
||||
|
||||
local pong; pong=$(docker exec stellaops-dev-valkey valkey-cli ping 2>/dev/null || true)
|
||||
if [[ "$pong" == "PONG" ]]; then
|
||||
ok 'Valkey'
|
||||
else
|
||||
warn 'Valkey not responding'
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── Main ───────────────────────────────────────────────────────────────────
|
||||
|
||||
echo '============================================='
|
||||
echo ' Stella Ops Developer Environment Setup'
|
||||
echo '============================================='
|
||||
|
||||
check_prerequisites
|
||||
check_hosts
|
||||
|
||||
if [[ "$IMAGES_ONLY" == "true" ]]; then
|
||||
build_images
|
||||
echo ''
|
||||
echo 'Done (images only).'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ensure_env
|
||||
start_infra
|
||||
|
||||
if [[ "$INFRA_ONLY" == "true" ]]; then
|
||||
smoke_test
|
||||
echo ''
|
||||
echo 'Done (infra only). Infrastructure is running.'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$SKIP_BUILD" != "true" ]]; then
|
||||
build_solutions
|
||||
fi
|
||||
|
||||
if [[ "$SKIP_IMAGES" != "true" ]]; then
|
||||
build_images
|
||||
fi
|
||||
|
||||
start_platform
|
||||
smoke_test
|
||||
|
||||
echo ''
|
||||
echo '============================================='
|
||||
echo ' Setup complete!'
|
||||
echo ' Platform: https://stella-ops.local'
|
||||
echo ' Docs: docs/dev/DEV_ENVIRONMENT_SETUP.md'
|
||||
echo '============================================='
|
||||
Reference in New Issue
Block a user