This commit is contained in:
master
2026-02-04 19:59:20 +02:00
parent 557feefdc3
commit 5548cf83bf
1479 changed files with 53557 additions and 40339 deletions

View 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
}

View 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
View 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
View 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 '============================================='