Widen scratch iteration 011 with fixture-backed integrations QA

This commit is contained in:
master
2026-03-14 03:11:45 +02:00
parent 3b1b7dad80
commit bd78523564
40 changed files with 3478 additions and 2173 deletions

View File

@@ -8,7 +8,9 @@ param(
[string]$ImplId = (Get-Date).ToUniversalTime().ToString('yyyyMMdd'),
[int]$StartingBatchId = 0
[int]$StartingBatchId = 0,
[bool]$QaIntegrationFixtures = $true
)
Set-StrictMode -Version Latest
@@ -400,6 +402,9 @@ for ($iteration = $StartIteration; $iteration -le $EndIteration; $iteration++) {
$state.Decisions.Add('Decision: each scratch iteration remains a full wipe -> setup -> route/page/action audit -> grouped remediation loop; if the audit comes back clean, that still counts as a completed iteration because the full loop was executed.')
$state.Decisions.Add('Decision: changed or newly discovered user flows must be converted into retained Playwright coverage before the next scratch iteration starts so the audit surface expands instead of rediscovering the same gaps manually.')
$state.Decisions.Add('Risk: scratch rebuilds remain expensive, so verification stays Playwright-first with focused test/build slices rather than indiscriminate full-solution test runs.')
if ($QaIntegrationFixtures) {
$state.Decisions.Add('Decision: scratch setup starts the Harbor and GitHub App QA fixtures so the full audit proves real success-path integration onboarding from the UI, not just graceful failure handling.')
}
$state.NextCheckpoints.Add('Finish the Stella-only wipe and capture the next zero-state setup outcome.')
$state.NextCheckpoints.Add('Run the full Playwright route/page/action audit, including changed-surface and ownership checks, on the rebuilt stack before diagnosing any new fixes.')
Add-SprintLog -State $state -Update "Sprint created for the next scratch iteration after local commit ``$baselineCommit`` closed the previous clean baseline." -Owner 'QA'
@@ -413,12 +418,18 @@ for ($iteration = $StartIteration; $iteration -le $EndIteration; $iteration++) {
Add-SprintLog -State $state -Update 'Removed Stella-only containers, `stellaops/*:dev` images, Stella compose volumes, and the `stellaops` / `stellaops_frontdoor` networks to return the machine to zero Stella runtime state for the new iteration.' -Owner 'QA / 3rd line support'
Write-SprintState -State $state -Path $sprintPath
Invoke-External -FilePath (Join-Path $repoRoot 'scripts/setup.ps1') -WorkingDirectory $repoRoot
$setupArguments = @()
if ($QaIntegrationFixtures) {
$setupArguments += '-QaIntegrationFixtures'
}
Invoke-External -FilePath (Join-Path $repoRoot 'scripts/setup.ps1') -ArgumentList $setupArguments -WorkingDirectory $repoRoot
$state.Criteria12 = $true
$state.Criteria13 = $true
$state.Status1 = 'DONE'
$healthySummary = Get-HealthyContainerSummary
Add-SprintLog -State $state -Update "The zero-state setup rerun completed cleanly: ``36/36`` solution builds passed, the full image matrix rebuilt, platform services converged, and ``$healthySummary`` Stella containers are healthy on ``https://stella-ops.local``." -Owner 'QA / 3rd line support'
$fixtureModeText = if ($QaIntegrationFixtures) { ' with Harbor/GitHub App QA fixtures enabled' } else { '' }
Add-SprintLog -State $state -Update "The zero-state setup rerun completed cleanly$fixtureModeText: ``36/36`` solution builds passed, the full image matrix rebuilt, platform services converged, and ``$healthySummary`` Stella containers are healthy on ``https://stella-ops.local``." -Owner 'QA / 3rd line support'
$state.Status2 = 'DOING'
Write-SprintState -State $state -Path $sprintPath

View File

@@ -13,13 +13,16 @@
Only build Docker images (skip infra start and .NET build).
.PARAMETER SkipImages
Skip Docker image builds.
.PARAMETER QaIntegrationFixtures
Start the optional Harbor and GitHub App QA fixtures used for successful Integrations Hub onboarding checks.
#>
[CmdletBinding()]
param(
[switch]$SkipBuild,
[switch]$InfraOnly,
[switch]$ImagesOnly,
[switch]$SkipImages
[switch]$SkipImages,
[switch]$QaIntegrationFixtures
)
$ErrorActionPreference = 'Stop'
@@ -391,7 +394,7 @@ function Test-Prerequisites {
# ─── 2. Check and install hosts file ─────────────────────────────────────
function Test-HostsFile {
Write-Step 'Checking hosts file for stella-ops.local entries'
Write-Step 'Checking hosts file for required Stella Ops entries'
$hostsPath = 'C:\Windows\System32\drivers\etc\hosts'
$hostsSource = Join-Path $Root 'devops/compose/hosts.stellaops.local'
@@ -400,20 +403,49 @@ function Test-HostsFile {
return
}
$content = Get-Content $hostsPath -Raw
if ($content -match 'stella-ops\.local') {
Write-Ok 'stella-ops.local entries found in hosts file'
return
}
Write-Warn 'stella-ops.local entries NOT found in hosts file.'
if (-not (Test-Path $hostsSource)) {
Write-Warn "Hosts source file not found at $hostsSource"
Write-Host ' Add the hosts block from docs/dev/DEV_ENVIRONMENT_SETUP.md section 2' -ForegroundColor Yellow
return
}
$content = Get-Content $hostsPath -Raw
$sourceLines = Get-Content $hostsSource | Where-Object {
$trimmed = $_.Trim()
$trimmed -and -not $trimmed.StartsWith('#')
}
$missingLines = @()
$missingHosts = @()
foreach ($line in $sourceLines) {
$tokens = $line -split '\s+' | Where-Object { $_ }
if ($tokens.Count -lt 2) {
continue
}
$hostnames = $tokens | Select-Object -Skip 1
$lineMissing = $false
foreach ($hostname in $hostnames) {
$escapedHostname = [regex]::Escape($hostname)
if ($content -notmatch "(?m)(^|\s)$escapedHostname($|\s)") {
$missingHosts += $hostname
$lineMissing = $true
}
}
if ($lineMissing) {
$missingLines += $line
}
}
if ($missingHosts.Count -eq 0) {
Write-Ok 'All required Stella Ops host entries are present in the hosts file'
return
}
$missingHosts = $missingHosts | Sort-Object -Unique
Write-Warn "Missing Stella Ops host aliases: $([string]::Join(', ', $missingHosts))"
# Check if running as Administrator
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
@@ -424,9 +456,9 @@ function Test-HostsFile {
Write-Host ''
$answer = Read-Host ' Add entries to hosts file now? (Y/n)'
if ($answer -eq '' -or $answer -match '^[Yy]') {
$hostsBlock = Get-Content $hostsSource -Raw
Add-Content -Path $hostsPath -Value "`n$hostsBlock"
Write-Ok 'Hosts entries added successfully'
Add-Content -Path $hostsPath -Value ''
Add-Content -Path $hostsPath -Value ($missingLines -join [Environment]::NewLine)
Write-Ok "Added $($missingLines.Count) missing host entry line(s) successfully"
} else {
Write-Warn 'Skipped. Add them manually before accessing the platform.'
Write-Host " Copy from: $hostsSource" -ForegroundColor Yellow
@@ -597,6 +629,27 @@ function Start-Platform {
-restartAfterSeconds 45)
}
function Start-QaIntegrationFixtures {
Write-Step 'Starting QA integration fixtures'
Push-Location $ComposeDir
try {
docker compose -f docker-compose.integration-fixtures.yml up -d
if ($LASTEXITCODE -ne 0) {
Write-Fail 'Failed to start QA integration fixtures.'
exit 1
}
[void](Wait-ForComposeConvergence `
-composeFiles @('docker-compose.integration-fixtures.yml') `
-successMessage 'QA integration fixtures are healthy' `
-maxWaitSeconds 90 `
-restartAfterSeconds 30)
}
finally {
Pop-Location
}
}
function Test-ExpectedHttpStatus([string]$url, [int[]]$allowedStatusCodes, [int]$timeoutSeconds = 5, [int]$attempts = 6, [int]$retryDelaySeconds = 2) {
for ($attempt = 1; $attempt -le $attempts; $attempt++) {
$statusCode = $null
@@ -770,6 +823,24 @@ function Test-Smoke {
$hasBlockingFailures = $true
}
if ($QaIntegrationFixtures) {
$harborFixtureStatus = Test-ExpectedHttpStatus 'http://127.1.1.6/api/v2.0/health' @(200)
if ($null -ne $harborFixtureStatus) {
Write-Ok "Harbor QA fixture (HTTP $harborFixtureStatus)"
} else {
Write-Fail 'Harbor QA fixture did not respond with HTTP 200 on /api/v2.0/health'
$hasBlockingFailures = $true
}
$githubFixtureStatus = Test-ExpectedHttpStatus 'http://127.1.1.7/api/v3/app' @(200)
if ($null -ne $githubFixtureStatus) {
Write-Ok "GitHub App QA fixture (HTTP $githubFixtureStatus)"
} else {
Write-Fail 'GitHub App QA fixture did not respond with HTTP 200 on /api/v3/app'
$hasBlockingFailures = $true
}
}
if (-not $InfraOnly) {
if (Test-FrontdoorBootstrap) {
Write-Ok 'Frontdoor bootstrap path is ready for first-user sign-in'
@@ -794,8 +865,15 @@ function Test-Smoke {
@('docker-compose.stella-ops.yml')
}
if ($QaIntegrationFixtures) {
$composeFiles += 'docker-compose.integration-fixtures.yml'
}
if (-not ($composeFiles | Where-Object { Test-Path $_ })) {
$composeFiles = @('docker-compose.dev.yml', 'docker-compose.stella-ops.yml')
if ($QaIntegrationFixtures) {
$composeFiles += 'docker-compose.integration-fixtures.yml'
}
}
$totalContainers = 0
@@ -902,6 +980,9 @@ Initialize-EnvFile
if ($InfraOnly) {
Start-Infrastructure
if ($QaIntegrationFixtures) {
Start-QaIntegrationFixtures
}
$infraSmokeFailed = Test-Smoke
if ($infraSmokeFailed) {
Write-Fail 'Infrastructure setup did not pass blocking smoke tests. Review output and docker compose logs.'
@@ -920,6 +1001,9 @@ if (-not $SkipImages) {
}
Start-Platform
if ($QaIntegrationFixtures) {
Start-QaIntegrationFixtures
}
$platformSmokeFailed = Test-Smoke
if ($platformSmokeFailed) {
Write-Fail 'Setup did not pass blocking smoke tests. Review output and docker compose logs.'
@@ -929,6 +1013,10 @@ if ($platformSmokeFailed) {
Write-Host "`n=============================================" -ForegroundColor Green
Write-Host ' Setup complete!' -ForegroundColor Green
Write-Host ' Platform: https://stella-ops.local' -ForegroundColor Green
if ($QaIntegrationFixtures) {
Write-Host ' Harbor QA fixture: http://harbor-fixture.stella-ops.local/api/v2.0/health' -ForegroundColor Green
Write-Host ' GitHub App QA fixture: http://github-app-fixture.stella-ops.local/api/v3/app' -ForegroundColor Green
}
Write-Host ' Docs: docs/dev/DEV_ENVIRONMENT_SETUP.md' -ForegroundColor Green
Write-Host '=============================================' -ForegroundColor Green
exit 0

View File

@@ -11,6 +11,7 @@ SKIP_BUILD=false
INFRA_ONLY=false
IMAGES_ONLY=false
SKIP_IMAGES=false
QA_INTEGRATION_FIXTURES=false
for arg in "$@"; do
case "$arg" in
@@ -18,8 +19,9 @@ for arg in "$@"; do
--infra-only) INFRA_ONLY=true ;;
--images-only) IMAGES_ONLY=true ;;
--skip-images) SKIP_IMAGES=true ;;
--qa-integration-fixtures) QA_INTEGRATION_FIXTURES=true ;;
-h|--help)
echo "Usage: $0 [--skip-build] [--infra-only] [--images-only] [--skip-images]"
echo "Usage: $0 [--skip-build] [--infra-only] [--images-only] [--skip-images] [--qa-integration-fixtures]"
exit 0
;;
*) echo "Unknown flag: $arg" >&2; exit 1 ;;
@@ -326,16 +328,9 @@ check_prerequisites() {
# ─── 2. Check and install hosts file ─────────────────────────────────────
check_hosts() {
step 'Checking hosts file for stella-ops.local entries'
step 'Checking hosts file for required Stella Ops entries'
local hosts_source="${ROOT}/devops/compose/hosts.stellaops.local"
if grep -q 'stella-ops\.local' /etc/hosts 2>/dev/null; then
ok 'stella-ops.local entries found in /etc/hosts'
return
fi
warn 'stella-ops.local entries NOT found in /etc/hosts.'
if [[ ! -f "$hosts_source" ]]; then
warn "Hosts source file not found at $hosts_source"
echo ' Add the hosts block from docs/dev/DEV_ENVIRONMENT_SETUP.md section 2'
@@ -343,6 +338,42 @@ check_hosts() {
return
fi
local source_lines=()
while IFS= read -r line; do
[[ -z "${line// }" ]] && continue
[[ "$line" =~ ^[[:space:]]*# ]] && continue
source_lines+=("$line")
done < "$hosts_source"
local missing_lines=()
local missing_hosts=()
local line hostname
for line in "${source_lines[@]}"; do
read -r -a tokens <<< "$line"
(( ${#tokens[@]} < 2 )) && continue
local line_missing=false
for hostname in "${tokens[@]:1}"; do
if ! grep -Eq "(^|[[:space:]])${hostname}($|[[:space:]])" /etc/hosts 2>/dev/null; then
missing_hosts+=("$hostname")
line_missing=true
fi
done
if [[ "$line_missing" == "true" ]]; then
missing_lines+=("$line")
fi
done
if (( ${#missing_hosts[@]} == 0 )); then
ok 'All required Stella Ops host entries are present in /etc/hosts'
return
fi
local unique_missing_hosts
unique_missing_hosts=$(printf '%s\n' "${missing_hosts[@]}" | awk 'NF { print }' | sort -u | paste -sd ', ' -)
warn "Missing Stella Ops host aliases: ${unique_missing_hosts}"
echo ''
echo ' Stella Ops needs ~50 hosts file entries for local development.'
echo " Source: devops/compose/hosts.stellaops.local"
@@ -353,21 +384,21 @@ check_hosts() {
if [[ -z "$answer" || "$answer" =~ ^[Yy] ]]; then
if [[ "$(id -u)" -eq 0 ]]; then
printf '\n' >> /etc/hosts
cat "$hosts_source" >> /etc/hosts
ok 'Hosts entries added successfully'
printf '%s\n' "${missing_lines[@]}" >> /etc/hosts
ok "Added ${#missing_lines[@]} missing host entry line(s) successfully"
else
echo ''
echo ' Adding hosts entries requires sudo...'
if sudo sh -c "printf '\n' >> /etc/hosts && cat '$hosts_source' >> /etc/hosts"; then
ok 'Hosts entries added successfully'
if printf '%s\n' "${missing_lines[@]}" | sudo tee -a /etc/hosts >/dev/null; then
ok "Added ${#missing_lines[@]} missing host entry line(s) successfully"
else
warn 'Failed to add hosts entries. Add them manually:'
echo " sudo sh -c 'cat $hosts_source >> /etc/hosts'"
echo " printf '%s\n' \"${missing_lines[@]}\" | sudo tee -a /etc/hosts"
fi
fi
else
warn 'Skipped. Add them manually before accessing the platform:'
echo " sudo sh -c 'cat $hosts_source >> /etc/hosts'"
printf ' %s\n' "${missing_lines[@]}"
fi
}
@@ -486,6 +517,15 @@ start_platform() {
wait_for_compose_convergence 'Platform services converged from zero-state startup' true 180 45 docker-compose.stella-ops.yml || true
}
start_qa_integration_fixtures() {
step 'Starting QA integration fixtures'
cd "$COMPOSE_DIR"
docker compose -f docker-compose.integration-fixtures.yml up -d
ok 'QA integration fixtures started'
cd "$ROOT"
wait_for_compose_convergence 'QA integration fixtures are healthy' false 90 30 docker-compose.integration-fixtures.yml || true
}
http_status() {
local url="$1"
local attempts="${2:-6}"
@@ -597,6 +637,25 @@ smoke_test() {
has_blocking_failures=true
fi
if [[ "$QA_INTEGRATION_FIXTURES" == "true" ]]; then
local harbor_fixture_status github_fixture_status
harbor_fixture_status=$(http_status 'http://127.1.1.6/api/v2.0/health')
if [[ "$harbor_fixture_status" == "200" ]]; then
ok "Harbor QA fixture (HTTP $harbor_fixture_status)"
else
warn 'Harbor QA fixture did not respond with HTTP 200 on /api/v2.0/health'
has_blocking_failures=true
fi
github_fixture_status=$(http_status 'http://127.1.1.7/api/v3/app')
if [[ "$github_fixture_status" == "200" ]]; then
ok "GitHub App QA fixture (HTTP $github_fixture_status)"
else
warn 'GitHub App QA fixture did not respond with HTTP 200 on /api/v3/app'
has_blocking_failures=true
fi
fi
if [[ "$INFRA_ONLY" != "true" ]]; then
if ! frontdoor_bootstrap_ready; then
has_blocking_failures=true
@@ -614,8 +673,19 @@ smoke_test() {
local total=0
local healthy=0
local unhealthy_names=""
local compose_files=()
for cf in docker-compose.dev.yml docker-compose.stella-ops.yml; do
if [[ "$INFRA_ONLY" == "true" ]]; then
compose_files+=(docker-compose.dev.yml)
else
compose_files+=(docker-compose.stella-ops.yml)
fi
if [[ "$QA_INTEGRATION_FIXTURES" == "true" ]]; then
compose_files+=(docker-compose.integration-fixtures.yml)
fi
for cf in "${compose_files[@]}"; do
[[ ! -f "$cf" ]] && continue
while IFS= read -r line; do
[[ -z "$line" ]] && continue
@@ -676,6 +746,9 @@ ensure_env
start_infra
if [[ "$INFRA_ONLY" == "true" ]]; then
if [[ "$QA_INTEGRATION_FIXTURES" == "true" ]]; then
start_qa_integration_fixtures
fi
if ! smoke_test; then
fail 'Infrastructure setup did not pass blocking smoke tests. Review output and docker compose logs.'
exit 1
@@ -698,6 +771,9 @@ if [[ "$SKIP_IMAGES" != "true" ]]; then
fi
start_platform
if [[ "$QA_INTEGRATION_FIXTURES" == "true" ]]; then
start_qa_integration_fixtures
fi
if ! smoke_test; then
fail 'Setup did not pass blocking smoke tests. Review output and docker compose logs.'
exit 1
@@ -707,6 +783,10 @@ echo ''
echo '============================================='
echo ' Setup complete!'
echo ' Platform: https://stella-ops.local'
if [[ "$QA_INTEGRATION_FIXTURES" == "true" ]]; then
echo ' Harbor QA fixture: http://harbor-fixture.stella-ops.local/api/v2.0/health'
echo ' GitHub App QA fixture: http://github-app-fixture.stella-ops.local/api/v3/app'
fi
echo ' Docs: docs/dev/DEV_ENVIRONMENT_SETUP.md'
echo '============================================='
exit 0