226 lines
9.0 KiB
PowerShell
226 lines
9.0 KiB
PowerShell
param(
|
|
[string]$WebUrl = "http://192.168.3.90:3003",
|
|
[string]$ApiUrl = "http://192.168.3.90:8001/api/v1",
|
|
[string]$HealthUrl = "http://192.168.3.90:8001/health",
|
|
[string]$ReadinessUrl = "http://192.168.3.90:8001/api/v1/system/readiness",
|
|
[string]$ArtifactRoot = ""
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
$repoRoot = Resolve-Path (Join-Path $PSScriptRoot "..")
|
|
$frontendDir = Join-Path $repoRoot "frontend"
|
|
$timestamp = Get-Date -Format "yyyy-MM-ddTHH-mm-ss"
|
|
if ([string]::IsNullOrWhiteSpace($ArtifactRoot)) {
|
|
$ArtifactRoot = Join-Path $repoRoot "project-docs\qa-artifacts\phase5-production-regression"
|
|
}
|
|
if (-not [System.IO.Path]::IsPathRooted($ArtifactRoot)) {
|
|
$ArtifactRoot = Join-Path $repoRoot $ArtifactRoot
|
|
}
|
|
$artifactDir = Join-Path $ArtifactRoot $timestamp
|
|
$summaryJson = Join-Path $artifactDir "production-regression-summary.json"
|
|
$summaryMd = Join-Path $artifactDir "production-regression-summary.md"
|
|
$productionAdminPassword = if ([string]::IsNullOrWhiteSpace($env:AI_CONTRACT_ADMIN_PASSWORD)) { "123456" } else { $env:AI_CONTRACT_ADMIN_PASSWORD }
|
|
$usingDefaultAdminPassword = [string]::IsNullOrWhiteSpace($env:AI_CONTRACT_ADMIN_PASSWORD)
|
|
|
|
New-Item -ItemType Directory -Force -Path $artifactDir | Out-Null
|
|
|
|
if (-not (Test-Path "$HOME\.Codex\skills\gstack\bin")) {
|
|
throw "gstack is required for all AI-assisted work in this repo."
|
|
}
|
|
|
|
$checks = New-Object System.Collections.Generic.List[object]
|
|
|
|
function Add-Check {
|
|
param(
|
|
[string]$Key,
|
|
[string]$Label,
|
|
[ValidateSet("pass", "fail")]
|
|
[string]$Status,
|
|
[string]$Detail,
|
|
[hashtable]$Meta = @{}
|
|
)
|
|
|
|
$checks.Add([ordered]@{
|
|
key = $Key
|
|
label = $Label
|
|
status = $Status
|
|
required = $true
|
|
detail = $Detail
|
|
meta = $Meta
|
|
}) | Out-Null
|
|
}
|
|
|
|
function Save-Json {
|
|
param(
|
|
[string]$Path,
|
|
[object]$Value
|
|
)
|
|
$Value | ConvertTo-Json -Depth 12 | Set-Content -Encoding UTF8 -Path $Path
|
|
}
|
|
|
|
function Invoke-NodePhase {
|
|
param(
|
|
[string]$Phase,
|
|
[string]$ScriptName
|
|
)
|
|
|
|
$phaseArtifactDir = Join-Path $artifactDir $Phase
|
|
New-Item -ItemType Directory -Force -Path $phaseArtifactDir | Out-Null
|
|
$logPath = Join-Path $artifactDir "$Phase.log"
|
|
|
|
$previous = @{
|
|
PHASE1_WEB_URL = $env:PHASE1_WEB_URL
|
|
PHASE1_API_URL = $env:PHASE1_API_URL
|
|
PHASE1_HEALTH_URL = $env:PHASE1_HEALTH_URL
|
|
PHASE1_ARTIFACT_DIR = $env:PHASE1_ARTIFACT_DIR
|
|
PHASE2_WEB_URL = $env:PHASE2_WEB_URL
|
|
PHASE2_API_URL = $env:PHASE2_API_URL
|
|
PHASE2_HEALTH_URL = $env:PHASE2_HEALTH_URL
|
|
PHASE2_ARTIFACT_DIR = $env:PHASE2_ARTIFACT_DIR
|
|
PHASE3_WEB_URL = $env:PHASE3_WEB_URL
|
|
PHASE3_API_URL = $env:PHASE3_API_URL
|
|
PHASE3_HEALTH_URL = $env:PHASE3_HEALTH_URL
|
|
PHASE3_READINESS_URL = $env:PHASE3_READINESS_URL
|
|
PHASE3_ARTIFACT_DIR = $env:PHASE3_ARTIFACT_DIR
|
|
PHASE3_SEED_ADMIN = $env:PHASE3_SEED_ADMIN
|
|
KB_FAILED_UI_WEB_URL = $env:KB_FAILED_UI_WEB_URL
|
|
KB_FAILED_UI_API_URL = $env:KB_FAILED_UI_API_URL
|
|
KB_FAILED_UI_HEALTH_URL = $env:KB_FAILED_UI_HEALTH_URL
|
|
KB_FAILED_UI_ARTIFACT_DIR = $env:KB_FAILED_UI_ARTIFACT_DIR
|
|
AI_CONTRACT_ADMIN_PASSWORD = $env:AI_CONTRACT_ADMIN_PASSWORD
|
|
}
|
|
|
|
if ($Phase -eq "phase1") {
|
|
$env:PHASE1_WEB_URL = $WebUrl
|
|
$env:PHASE1_API_URL = $ApiUrl
|
|
$env:PHASE1_HEALTH_URL = $HealthUrl
|
|
$env:PHASE1_ARTIFACT_DIR = $phaseArtifactDir
|
|
} elseif ($Phase -eq "phase2") {
|
|
$env:PHASE2_WEB_URL = $WebUrl
|
|
$env:PHASE2_API_URL = $ApiUrl
|
|
$env:PHASE2_HEALTH_URL = $HealthUrl
|
|
$env:PHASE2_ARTIFACT_DIR = $phaseArtifactDir
|
|
} elseif ($Phase -eq "phase3") {
|
|
$env:PHASE3_WEB_URL = $WebUrl
|
|
$env:PHASE3_API_URL = $ApiUrl
|
|
$env:PHASE3_HEALTH_URL = $HealthUrl
|
|
$env:PHASE3_READINESS_URL = $ReadinessUrl
|
|
$env:PHASE3_ARTIFACT_DIR = $phaseArtifactDir
|
|
$env:PHASE3_SEED_ADMIN = "false"
|
|
} elseif ($Phase -eq "kb-failed-ui") {
|
|
$env:KB_FAILED_UI_WEB_URL = $WebUrl
|
|
$env:KB_FAILED_UI_API_URL = $ApiUrl
|
|
$env:KB_FAILED_UI_HEALTH_URL = $HealthUrl
|
|
$env:KB_FAILED_UI_ARTIFACT_DIR = $phaseArtifactDir
|
|
}
|
|
$env:AI_CONTRACT_ADMIN_PASSWORD = $productionAdminPassword
|
|
|
|
Push-Location $frontendDir
|
|
$previousErrorActionPreference = $ErrorActionPreference
|
|
$ErrorActionPreference = "Continue"
|
|
try {
|
|
$output = & node ".\scripts\$ScriptName" 2>&1
|
|
$exitCode = $LASTEXITCODE
|
|
} finally {
|
|
$ErrorActionPreference = $previousErrorActionPreference
|
|
Pop-Location
|
|
foreach ($key in $previous.Keys) {
|
|
Set-Item -Path "env:$key" -Value $previous[$key] -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
|
|
($output -join [Environment]::NewLine) | Set-Content -Encoding UTF8 -Path $logPath
|
|
return [ordered]@{
|
|
exit_code = $exitCode
|
|
log = $logPath
|
|
artifact_dir = $phaseArtifactDir
|
|
}
|
|
}
|
|
|
|
if ($usingDefaultAdminPassword) {
|
|
Add-Check -Key "admin_password_fallback" -Label "admin password fallback" -Status "pass" -Detail "AI_CONTRACT_ADMIN_PASSWORD not set; production regression used seeded admin fallback"
|
|
} else {
|
|
Add-Check -Key "admin_password_fallback" -Label "admin password source" -Status "pass" -Detail "AI_CONTRACT_ADMIN_PASSWORD provided by current process"
|
|
}
|
|
|
|
$phase1 = Invoke-NodePhase -Phase "phase1" -ScriptName "phase1-closed-loop-e2e.mjs"
|
|
if ($phase1.exit_code -eq 0) {
|
|
Add-Check -Key "phase1_production_e2e" -Label "phase1 production E2E" -Status "pass" -Detail "phase1 production E2E passed" -Meta @{ log = $phase1.log; artifact_dir = $phase1.artifact_dir }
|
|
} else {
|
|
Add-Check -Key "phase1_production_e2e" -Label "phase1 production E2E" -Status "fail" -Detail "phase1 production E2E failed" -Meta @{ log = $phase1.log; artifact_dir = $phase1.artifact_dir }
|
|
}
|
|
|
|
$phase2 = Invoke-NodePhase -Phase "phase2" -ScriptName "phase2-closed-loop-e2e.mjs"
|
|
if ($phase2.exit_code -eq 0) {
|
|
Add-Check -Key "phase2_production_e2e" -Label "phase2 production E2E" -Status "pass" -Detail "phase2 production E2E passed" -Meta @{ log = $phase2.log; artifact_dir = $phase2.artifact_dir }
|
|
} else {
|
|
Add-Check -Key "phase2_production_e2e" -Label "phase2 production E2E" -Status "fail" -Detail "phase2 production E2E failed" -Meta @{ log = $phase2.log; artifact_dir = $phase2.artifact_dir }
|
|
}
|
|
|
|
$kbFailedUi = Invoke-NodePhase -Phase "kb-failed-ui" -ScriptName "knowledge-base-failed-document-ui-e2e.mjs"
|
|
if ($kbFailedUi.exit_code -eq 0) {
|
|
Add-Check -Key "kb_failed_document_ui_e2e" -Label "knowledge base failed document UI E2E" -Status "pass" -Detail "knowledge base failed document UI E2E passed" -Meta @{ log = $kbFailedUi.log; artifact_dir = $kbFailedUi.artifact_dir }
|
|
} else {
|
|
Add-Check -Key "kb_failed_document_ui_e2e" -Label "knowledge base failed document UI E2E" -Status "fail" -Detail "knowledge base failed document UI E2E failed" -Meta @{ log = $kbFailedUi.log; artifact_dir = $kbFailedUi.artifact_dir }
|
|
}
|
|
|
|
$phase3 = Invoke-NodePhase -Phase "phase3" -ScriptName "phase3-closed-loop-e2e.mjs"
|
|
if ($phase3.exit_code -eq 0) {
|
|
Add-Check -Key "phase3_production_e2e" -Label "phase3 production E2E" -Status "pass" -Detail "phase3 production E2E passed" -Meta @{ log = $phase3.log; artifact_dir = $phase3.artifact_dir }
|
|
} else {
|
|
Add-Check -Key "phase3_production_e2e" -Label "phase3 production E2E" -Status "fail" -Detail "phase3 production E2E failed" -Meta @{ log = $phase3.log; artifact_dir = $phase3.artifact_dir }
|
|
}
|
|
|
|
$failedRequired = @($checks | Where-Object { $_.status -eq "fail" })
|
|
$overallStatus = if ($failedRequired.Count -gt 0) { "FAIL" } else { "PASS" }
|
|
|
|
$summary = [ordered]@{
|
|
name = "phase5-production-regression"
|
|
status = $overallStatus
|
|
generated_at = (Get-Date).ToUniversalTime().ToString("o")
|
|
artifact_dir = $artifactDir
|
|
targets = [ordered]@{
|
|
web_url = $WebUrl
|
|
api_url = $ApiUrl
|
|
health_url = $HealthUrl
|
|
readiness_url = $ReadinessUrl
|
|
}
|
|
checks = $checks
|
|
totals = [ordered]@{
|
|
total = $checks.Count
|
|
failed_required = $failedRequired.Count
|
|
}
|
|
}
|
|
Save-Json -Path $summaryJson -Value $summary
|
|
|
|
$markdown = New-Object System.Collections.Generic.List[string]
|
|
$markdown.Add("# Phase 5 Production Regression") | Out-Null
|
|
$markdown.Add("") | Out-Null
|
|
$markdown.Add("- Status: $overallStatus") | Out-Null
|
|
$markdown.Add("- Generated at: $($summary.generated_at)") | Out-Null
|
|
$markdown.Add("- Artifact dir: $artifactDir") | Out-Null
|
|
$markdown.Add("- Web URL: $WebUrl") | Out-Null
|
|
$markdown.Add("- API URL: $ApiUrl") | Out-Null
|
|
$markdown.Add("") | Out-Null
|
|
$markdown.Add("| Check | Required | Status | Detail |") | Out-Null
|
|
$markdown.Add("|---|---:|---|---|") | Out-Null
|
|
foreach ($check in $checks) {
|
|
$markdown.Add("| $($check.label) | $($check.required) | $($check.status.ToUpperInvariant()) | $($check.detail) |") | Out-Null
|
|
}
|
|
$markdown.Add("") | Out-Null
|
|
$markdown.Add("Local artifacts:") | Out-Null
|
|
$markdown.Add("") | Out-Null
|
|
$markdown.Add('- `production-regression-summary.json`') | Out-Null
|
|
$markdown.Add('- `phase1/`') | Out-Null
|
|
$markdown.Add('- `phase2/`') | Out-Null
|
|
$markdown.Add('- `kb-failed-ui/`') | Out-Null
|
|
$markdown.Add('- `phase3/`') | Out-Null
|
|
$markdown.Add('- `phase*.log`') | Out-Null
|
|
$markdown | Set-Content -Encoding UTF8 -Path $summaryMd
|
|
|
|
Write-Host "PHASE5_PRODUCTION_REGRESSION_$overallStatus $artifactDir"
|
|
if ($overallStatus -eq "FAIL") {
|
|
throw "Phase 5 production regression failed. See $summaryMd"
|
|
}
|