Widen scratch iteration 011 with fixture-backed integrations QA
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
112
scripts/setup.sh
112
scripts/setup.sh
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user