chore: close production operations automation

This commit is contained in:
Codex
2026-05-01 09:12:35 +08:00
parent dc101585ee
commit 384de1e687
9 changed files with 398 additions and 31 deletions
+48
View File
@@ -23,6 +23,7 @@
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "16.2.4",
"playwright": "1.59.1",
"typescript": "^5"
}
},
@@ -4312,6 +4313,21 @@
"node": ">= 6"
}
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@@ -5744,6 +5760,38 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/playwright": {
"version": "1.59.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz",
"integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.59.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.59.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz",
"integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/possible-typed-array-names": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+1
View File
@@ -30,6 +30,7 @@
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "16.2.4",
"playwright": "1.59.1",
"typescript": "^5"
}
}
+42
View File
@@ -187,6 +187,48 @@ powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts\rotate-admin-pas
curl http://192.168.3.90:8001/api/v1/system/readiness
```
## 6.6 阶段 5 生产运维门禁
生产健康巡检:
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts\run-production-health-check.ps1
```
备份与恢复演练:
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts\run-backup-drill.ps1
```
依赖治理:
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts\run-dependency-governance.ps1
```
安全基线:
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts\run-security-baseline.ps1
```
生产阶段 1/2/3 关键 E2E 回归:
```powershell
$env:AI_CONTRACT_ADMIN_PASSWORD = '<由管理员输入的实际密码>'
powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts\run-production-regression.ps1
Remove-Item Env:\AI_CONTRACT_ADMIN_PASSWORD
```
当前阶段 5 已完成闭环验证:
- 生产健康巡检:PASS
- 备份恢复演练:PASS
- 依赖治理:PASS
- 安全基线:PASS
- 生产阶段 1/2/3 关键 E2E:PASS
## 7. 生产部署建议(当前阶段)
当前试点服务器部署口径:
+34 -1
View File
@@ -174,7 +174,39 @@ backend\.venv\Scripts\python.exe -m pytest -q backend/tests
---
## 六、常见故障处理
## 六、生产回归口径
执行生产阶段 1/2/3 关键 E2E 回归:
```powershell
$env:AI_CONTRACT_ADMIN_PASSWORD = '<由管理员输入的实际密码>'
powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts\run-production-regression.ps1
Remove-Item Env:\AI_CONTRACT_ADMIN_PASSWORD
```
默认产物目录:
```text
project-docs/qa-artifacts/phase5-production-regression/<timestamp>/
```
回归内容:
1. 阶段 1 生产关键 E2E。
2. 阶段 2 生产关键 E2E。
3. 阶段 3 生产关键 E2E。
4. 每个阶段保留独立日志和产物目录。
门禁:
1. 管理员真实密码只允许在当前终端进程中临时存在。
2. 密码不得写入脚本、文档、Git、`.env` 或日志。
3. 任一阶段 E2E 失败时,本次生产回归失败。
4. 依赖升级、服务重启、密钥轮换、部署变更后必须复跑生产健康巡检、安全基线和生产回归。
---
## 七、常见故障处理
查看容器状态:
@@ -205,3 +237,4 @@ docker compose -f docker-compose.server.yml up -d backend frontend
```
重启后必须重新执行生产健康巡检。
如 backend 被重新创建后前端出现 API 代理解析失败,应同时重启 frontend,并重新执行生产健康巡检、安全基线和生产回归。
@@ -14,11 +14,11 @@
| 阶段 2:深度开发与交付体验补强 | PASS | `project-docs/阶段2-深度开发与交付体验补强执行记录-2026-05-01.md` |
| 阶段 3:试点交付准备 | PASS | `project-docs/阶段3-试点交付准备执行记录-2026-05-01.md` |
| 阶段 4:试点运行监控与安全加固 | PASS | `project-docs/阶段4-试点运行监控与安全加固执行记录-2026-05-01.md` |
| 阶段 5:生产运行、安全、监控、备份与依赖治理 | IN PROGRESS | `project-docs/阶段5-生产运行安全监控备份与依赖治理执行记录-2026-05-01.md` |
| 阶段 5:生产运行、安全、监控、备份与依赖治理 | PASS | `project-docs/阶段5-生产运行安全监控备份与依赖治理执行记录-2026-05-01.md` |
阶段 1 已完成源码闭环与生产入口闭环,生产无人 E2E `18/18` 通过。阶段 2 已完成 Review 重试、Knowledge Base 文档重试、Review -> Chat -> Contract 上下文联动、首页交付工作台和阶段 2 自动化无人闭环,本地与生产 E2E 均 `7/7` 通过。阶段 3 已完成系统 readiness、试点交付准备页、默认账号说明、演示样本数据、依赖健康门禁和阶段 3 自动化无人闭环,本地与生产 E2E 均 `5/5` 通过。
当前后续重点为“生产运行、安全、监控、备份与依赖治理”:默认密码轮换已完成,阶段 5 Step 1 生产健康巡检、Step 2 备份恢复演练、Step 3 依赖治理、Step 4 安全基线均已 PASS,下一步进入生产回归闭环。
当前后续重点为“生产运行、安全、监控、备份与依赖治理”:默认密码轮换已完成,阶段 5 Step 1 生产健康巡检、Step 2 备份恢复演练、Step 3 依赖治理、Step 4 安全基线、Step 5 生产回归均已 PASS,阶段 5 已形成生产运行治理闭环。
---
@@ -174,7 +174,7 @@ AI合同平台当前已经不是从零建设阶段。登录、合同审查、Rev
---
## 四、阶段 5 立即执行清单
## 四、阶段 5 已执行清单
第一批先建立生产运行基线:
@@ -184,7 +184,7 @@ AI合同平台当前已经不是从零建设阶段。登录、合同审查、Rev
4. 验证默认密码不可登录,管理员真实密码只通过运行时环境变量注入。
5. 生成 `production-health-summary.json` 和 `production-health-summary.md`。
第二批进入治理演练:
第二批治理演练:
1. 新增或完善备份恢复演练脚本。
2. 运行前端 `npm audit --json` 和后端 `pip check`。
@@ -192,6 +192,14 @@ AI合同平台当前已经不是从零建设阶段。登录、合同审查、Rev
4. 复跑生产阶段 1/2/3 核心 E2E。
5. 更新运维手册和阶段 5 执行记录。
当前结果:
1. 生产健康巡检 PASS:`project-docs/qa-artifacts/phase5-production-health/2026-05-01T09-10-20/`
2. 备份恢复演练 PASS:`project-docs/qa-artifacts/phase5-backup-drill/2026-05-01T08-40-59/`
3. 依赖治理 PASS:`project-docs/qa-artifacts/phase5-dependency-governance/2026-05-01T09-08-52/`
4. 安全基线 PASS:`project-docs/qa-artifacts/phase5-security-baseline/2026-05-01T09-10-28/`
5. 生产回归 PASS:`project-docs/qa-artifacts/phase5-production-regression/2026-05-01T09-10-34/`
---
## 五、交付完成定义
@@ -210,4 +218,4 @@ AI合同平台当前已经不是从零建设阶段。登录、合同审查、Rev
## 六、当前一句话结论
AI合同平台阶段 1、阶段 2 和阶段 3 均已完成闭环交付验证,阶段 4 已完成默认管理员密码轮换闭环,阶段 5 已启动并完成生产健康巡检、备份恢复演练、依赖治理和安全基线,下一步进入生产回归闭环。
AI合同平台阶段 1、阶段 2、阶段 3、阶段 4、阶段 5 均已完成闭环交付验证。当前系统已具备主业务闭环、试点交付准备、默认管理员密码轮换、生产健康巡检、备份恢复演练、依赖治理、安全基线和生产关键 E2E 回归证据。
@@ -14,9 +14,9 @@
| Step 2:备份与恢复演练 | PASS | `scripts/run-backup-drill.ps1` 已实现并完成生产备份/隔离恢复验证 |
| Step 3:依赖治理专项 | PASS | Next/PostCSS moderate 漏洞已清零,前后端依赖门禁通过 |
| Step 4:安全基线检查 | PASS | 生产 `SECRET_KEY` 已改为远端 `.env` 注入,安全基线脚本 PASS |
| Step 5:生产回归闭环 | PENDING | 下一步进入,复跑阶段 1/2/3 关键 E2E |
| Step 5:生产回归闭环 | PASS | `scripts/run-production-regression.ps1` 已实现,生产阶段 1/2/3 关键 E2E 全部通过 |
阶段 5 当前判定:Step 1、Step 2、Step 3、Step 4 均已 PASS。
阶段 5 当前判定:Step 1、Step 2、Step 3、Step 4、Step 5 均已 PASS,已形成生产运行治理闭环。
---
@@ -84,17 +84,43 @@ default-password-login.json
---
## 五、下一步
## 五、Step 5 生产回归闭环
阶段 5 下一步进入 Step 5:生产回归闭环。
新增脚本:
优先交付:
```powershell
scripts\run-production-regression.ps1
```
1. 阶段 1 生产关键 E2E
2. 阶段 2 生产关键 E2E
3. 阶段 3 生产关键 E2E
4. 生产健康巡检
5. 最终阶段 5 闭环报告
本次回归产物:
```text
project-docs/qa-artifacts/phase5-production-regression/2026-05-01T09-10-34/
```
最终复验产物:
```text
project-docs/qa-artifacts/phase5-production-health/2026-05-01T09-10-20/
project-docs/qa-artifacts/phase5-security-baseline/2026-05-01T09-10-28/
```
回归结果:
| 检查项 | 结果 | 说明 |
|---|---|---|
| 阶段 1 生产关键 E2E | PASS | 生产入口登录、合同审查、报告、合同生成等核心闭环通过 |
| 阶段 2 生产关键 E2E | PASS | Review 重试、Knowledge Base 重试、Review -> Chat -> Contract 联动通过 |
| 阶段 3 生产关键 E2E | PASS | Readiness、试点交付准备、默认账号说明、演示数据与生产门禁通过 |
| 生产健康巡检复验 | PASS | 容器、端口、`/health`、readiness、关键页面、默认密码拒绝通过 |
| 安全基线复验 | PASS | 强密钥、默认密码拒绝、member 权限边界、敏感文件扫描通过 |
执行口径:
1. 管理员真实密码只通过当前进程环境变量读取。
2. 脚本、文档、Git、`.env` 均不保存管理员真实密码。
3. 生产回归执行完成后应移除当前终端进程中的密码环境变量。
4. 任何阶段 E2E 失败时,脚本退出失败并保留阶段级日志与产物目录。
---
@@ -167,7 +193,7 @@ scripts\run-dependency-governance.ps1
本次依赖治理产物:
```text
project-docs/qa-artifacts/phase5-dependency-governance/2026-05-01T08-46-29/
project-docs/qa-artifacts/phase5-dependency-governance/2026-05-01T09-08-52/
```
验证记录:
@@ -203,7 +229,7 @@ scripts\run-security-baseline.ps1
安全基线产物:
```text
project-docs/qa-artifacts/phase5-security-baseline/2026-05-01T08-55-24/
project-docs/qa-artifacts/phase5-security-baseline/2026-05-01T09-10-28/
```
生产健康复验产物:
@@ -156,21 +156,25 @@ $env:AI_CONTRACT_ADMIN_PASSWORD = '<管理员实际密码>'
---
## 六、建议立即进入的第一步
## 六、阶段闭环状态
优先执行 Step 1:生产健康巡检自动化。
阶段 5 已完成闭环。后续运维应把生产健康巡检、安全基线和生产回归作为上线、重启、依赖升级和密钥轮换后的固定门禁。
理由:
原因:
1. 风险低,不改业务逻辑。
2. 可以先建立阶段 5 的观测基线。
3. 后续备份、依赖治理、安全基线和回归都可以复用同一套健康产物。
2. 可以快速确认生产入口、readiness、默认密码拒绝和关键页面状态。
3. 可以在安全、依赖、部署变更后立即发现回归。
第一批文件建议:
当前阶段 5 已交付文件:
1. `scripts/run-production-health-check.ps1`
2. `project-docs/阶段5-生产运行安全监控备份与依赖治理执行记录-2026-05-01.md`
3. `project-docs/运维手册-2026-05-01.md`
2. `scripts/run-backup-drill.ps1`
3. `scripts/run-dependency-governance.ps1`
4. `scripts/run-security-baseline.ps1`
5. `scripts/run-production-regression.ps1`
6. `project-docs/阶段5-生产运行安全监控备份与依赖治理执行记录-2026-05-01.md`
7. `project-docs/运维手册-2026-05-01.md`
当前执行状态:
@@ -182,14 +186,17 @@ $env:AI_CONTRACT_ADMIN_PASSWORD = '<管理员实际密码>'
- 备份演练产物:`project-docs/qa-artifacts/phase5-backup-drill/2026-05-01T08-40-59/`
- Step 3 已完成:`scripts/run-dependency-governance.ps1`
- 依赖治理结果:PASS,`npm audit` 0 vulnerabilities,后端 `pip check` PASS
- 依赖治理产物:`project-docs/qa-artifacts/phase5-dependency-governance/2026-05-01T08-46-29/`
- 依赖治理产物:`project-docs/qa-artifacts/phase5-dependency-governance/2026-05-01T09-08-52/`
- Step 4 已完成:`scripts/run-security-baseline.ps1`
- 安全基线结果:PASS,生产 `SECRET_KEY` 已改为远端 `.env` 强密钥注入
- 安全基线产物:`project-docs/qa-artifacts/phase5-security-baseline/2026-05-01T08-55-24/`
- 下一步:Step 5 生产回归闭环
- 安全基线最终复验产物:`project-docs/qa-artifacts/phase5-security-baseline/2026-05-01T09-10-28/`
- Step 5 已完成:`scripts/run-production-regression.ps1`
- 生产回归结果:PASS,阶段 1/2/3 生产关键 E2E 全部通过
- 生产回归产物:`project-docs/qa-artifacts/phase5-production-regression/2026-05-01T09-10-34/`
- 阶段 5 最终状态:PASS,生产运行、安全、监控、备份、依赖治理与回归已闭环
---
## 七、一句话结论
阶段 5 的核心不是再证明“业务能跑”,而是证明“系统能长期、安全、可恢复地跑”。下一步直接进入生产健康巡检自动化,先把运行状态变成每日可复查的事实。
阶段 5 的核心不是再证明“业务能跑”,而是证明“系统能长期、安全、可恢复地跑”。当前阶段 5 已完成闭环,后续应进入常态化巡检、告警接入和发布前门禁固化。
+196
View File
@@ -0,0 +1,196 @@
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"
}
$artifactDir = Join-Path $ArtifactRoot $timestamp
$summaryJson = Join-Path $artifactDir "production-regression-summary.json"
$summaryMd = Join-Path $artifactDir "production-regression-summary.md"
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."
}
if ([string]::IsNullOrWhiteSpace($env:AI_CONTRACT_ADMIN_PASSWORD)) {
throw "AI_CONTRACT_ADMIN_PASSWORD must be set for production regression. The value is read from the current process only."
}
$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
}
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
}
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
}
}
$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 }
}
$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('- `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"
}
+7 -1
View File
@@ -278,7 +278,13 @@ if ($trackedSensitive) {
}
$secretScanPath = Join-Path $artifactDir "source-secret-scan.txt"
$secretScan = git -C $repoRoot grep -n -I -E 'BEGIN (RSA|OPENSSH|PRIVATE) KEY|zmfCSR|AI_CONTRACT_ADMIN_PASSWORD[[:space:]]*=' -- backend frontend scripts docker-compose.server.yml 2>$null
$secretPatterns = @(
"BEGIN (RSA|OPENSSH|PRIVATE) KEY",
("z" + "mfCSR"),
("AI_CONTRACT_ADMIN_PASSWORD" + "[[:space:]]*=")
)
$secretRegex = $secretPatterns -join "|"
$secretScan = git -C $repoRoot grep -n -I -E $secretRegex -- backend frontend scripts docker-compose.server.yml 2>$null
if ($secretScan) {
($secretScan | ForEach-Object { $_ }) | Set-Content -Encoding UTF8 -Path $secretScanPath
} else {