Files
ai-contract-platform/scripts/run-phase1-closed-loop.ps1
T

159 lines
5.3 KiB
PowerShell

param(
[string]$BackendBaseUrl = "http://192.168.3.90:8001",
[int]$FrontendPort = 3004,
[switch]$SkipBaseline,
[string]$AdminPasswordFile = ""
)
$ErrorActionPreference = "Stop"
$repoRoot = Resolve-Path (Join-Path $PSScriptRoot "..")
$frontendDir = Join-Path $repoRoot "frontend"
$backendDir = Join-Path $repoRoot "backend"
$timestamp = Get-Date -Format "yyyy-MM-ddTHH-mm-ss"
$artifactDir = Join-Path $repoRoot "project-docs\qa-artifacts\phase1-closed-loop\$timestamp"
$serverLog = Join-Path $artifactDir "frontend-dev-server.log"
$webUrl = "http://127.0.0.1:$FrontendPort"
if ([string]::IsNullOrWhiteSpace($AdminPasswordFile)) {
$AdminPasswordFile = Join-Path $HOME ".ai-contract-platform\production-admin-password.txt"
}
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."
}
function Wait-HttpReady {
param(
[string]$Url,
[int]$TimeoutSeconds = 90
)
$deadline = (Get-Date).AddSeconds($TimeoutSeconds)
do {
try {
$response = Invoke-WebRequest -Uri $Url -TimeoutSec 3
if ($response.StatusCode -ge 200 -and $response.StatusCode -lt 500) {
return
}
} catch {
Start-Sleep -Seconds 2
}
} while ((Get-Date) -lt $deadline)
throw "Timed out waiting for $Url"
}
function Stop-Listener {
param([int]$Port)
$listener = Get-NetTCPConnection -LocalPort $Port -ErrorAction SilentlyContinue |
Where-Object { $_.State -eq "Listen" } |
Select-Object -First 1
if ($listener) {
Stop-Process -Id $listener.OwningProcess -Force -ErrorAction SilentlyContinue
}
}
function Stop-ExistingNextDevProcess {
param([string]$TargetDirectory)
$normalizedTarget = $TargetDirectory.ToLowerInvariant()
$processes = Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
Where-Object {
$_.Name -eq "node.exe" -and
$_.CommandLine -and
$_.CommandLine.ToLowerInvariant().Contains($normalizedTarget) -and
(
$_.CommandLine -match "next\\dist\\bin\\next" -or
$_.CommandLine -match "start-server\.js"
)
}
foreach ($process in $processes) {
Stop-Process -Id $process.ProcessId -Force -ErrorAction SilentlyContinue
}
}
$frontendProcess = $null
try {
if (-not (Test-Path (Join-Path $frontendDir "node_modules"))) {
Push-Location $frontendDir
npm.cmd install --no-package-lock
if ($LASTEXITCODE -ne 0) { throw "npm install failed" }
Pop-Location
}
if (-not (Test-Path (Join-Path $frontendDir "node_modules\playwright"))) {
Push-Location $frontendDir
npm.cmd install --no-save --no-package-lock playwright
if ($LASTEXITCODE -ne 0) { throw "playwright package install failed" }
Pop-Location
}
Push-Location $frontendDir
npx.cmd playwright install chromium
if ($LASTEXITCODE -ne 0) { throw "playwright browser install failed" }
Pop-Location
if (-not $SkipBaseline) {
Push-Location $frontendDir
npm.cmd run lint
if ($LASTEXITCODE -ne 0) { throw "frontend lint failed" }
npm.cmd run build
if ($LASTEXITCODE -ne 0) { throw "frontend build failed" }
Pop-Location
Push-Location $repoRoot
.\backend\.venv\Scripts\python.exe -m pytest -q `
backend/tests/test_auth_api.py `
backend/tests/test_auth_service.py `
backend/tests/test_chat_api.py `
backend/tests/test_contracts_api.py `
backend/tests/test_files_api.py `
backend/tests/test_knowledge_bases_api.py `
backend/tests/test_review_rules_api.py `
backend/tests/test_review_rule_service.py `
backend/tests/test_review_service.py `
backend/tests/test_review_service_rules.py `
backend/tests/test_reviews_api.py `
backend/tests/test_templates_api.py `
backend/tests/test_users_api.py
if ($LASTEXITCODE -ne 0) { throw "backend pytest failed" }
Pop-Location
}
Stop-ExistingNextDevProcess -TargetDirectory $frontendDir
Stop-Listener -Port $FrontendPort
$serverCommand = "`$env:NEXT_PUBLIC_API_BASE_URL='/api/v1'; `$env:BACKEND_BASE_URL='$BackendBaseUrl'; npm.cmd run dev -- --hostname 127.0.0.1 --port $FrontendPort *> '$serverLog'"
$frontendProcess = Start-Process -FilePath "powershell.exe" `
-ArgumentList @("-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", $serverCommand) `
-WorkingDirectory $frontendDir `
-WindowStyle Hidden `
-PassThru
Wait-HttpReady -Url "$webUrl/login" -TimeoutSeconds 120
Wait-HttpReady -Url "$BackendBaseUrl/health" -TimeoutSeconds 30
Push-Location $frontendDir
if ([string]::IsNullOrWhiteSpace($env:AI_CONTRACT_ADMIN_PASSWORD) -and (Test-Path -LiteralPath $AdminPasswordFile -PathType Leaf)) {
$env:AI_CONTRACT_ADMIN_PASSWORD = (Get-Content -LiteralPath $AdminPasswordFile -Raw -Encoding UTF8).Trim()
}
$env:PHASE1_WEB_URL = $webUrl
$env:PHASE1_API_URL = "$BackendBaseUrl/api/v1"
$env:PHASE1_HEALTH_URL = "$BackendBaseUrl/health"
$env:PHASE1_ARTIFACT_DIR = $artifactDir
node .\scripts\phase1-closed-loop-e2e.mjs
if ($LASTEXITCODE -ne 0) { throw "phase1 closed-loop e2e failed" }
Pop-Location
Write-Host "PHASE1_CLOSED_LOOP_PASS $artifactDir"
} finally {
if ($frontendProcess -and -not $frontendProcess.HasExited) {
Stop-Process -Id $frontendProcess.Id -Force -ErrorAction SilentlyContinue
}
Stop-Listener -Port $FrontendPort
}