"""Summarize reproducible evidence; never label skipped work as passed.""" import html,json,re,datetime from pathlib import Path ROOT=Path(__file__).resolve().parents[1] OUT=ROOT/'reports/v828' OUT.mkdir(parents=True,exist_ok=True) path=ROOT/'reports/v828-regression/result.json' if not path.exists(): raise SystemExit('Full regression has not finished; report will not guess counts.') r=json.loads(path.read_text()) groups=r.get('groups',[]) records=[] for g in groups: match=re.search(r'(\d+) passed',g.get('summary','')) records.append({**g,'passed_count':int(match.group(1)) if match else 0}) passed=sum(g['passed_count'] for g in records) errors=[g for g in records if g.get('status')!='passed'] retest=ROOT/'reports/v828-regression/batch-22-retest.log' retest_ok=(ROOT/'reports/v828-regression/batch-22-retest.exit').exists() and (ROOT/'reports/v828-regression/batch-22-retest.exit').read_text().strip()=='0' retested=[] if retest_ok: retest_count=int(re.search(r'(\d+) passed',retest.read_text()).group(1)) original=next((g for g in records if g['batch']==22 and g['status']=='failed'),None) if original: passed+=retest_count-original['passed_count'] retested.append({'batch':22,'passed':retest_count,'reason':'旧 UI 测试断言仍要求“构建与交付”菜单;已更新为“生成与预览 / 检查并上线”,原样保留失败日志并独立重新测试。'}) unresolved=[g for g in errors if g['batch'] not in [x['batch'] for x in retested]] verified_batches=len(records)-len(unresolved) report={ 'version':'8.28.0-dev.1','created_utc':datetime.datetime.now(datetime.timezone.utc).isoformat(), 'regression_modules':sum(len(g['files']) for g in records), 'regression_batches':len(records),'regression_passed_batches':sum(g['status']=='passed' for g in records), 'regression_test_cases_passed':passed,'regression_unpassed_batches_initial':len(errors),'regression_unresolved_batches':len(unresolved),'regression_retests':retested,'regression_verified_batches':verified_batches, 'browser_test': 'PASS: Chromium with mock API, no real server deployment', 'http_e2e':'PASS: real local HTTP + Worker with isolated SQLite', 'real_art_pro_build':'NOT_VERIFIED', 'production_postgres_redis_multi_worker':'NOT_VERIFIED', 'real_remote_release_rollback':'NOT_VERIFIED', 'notes':['Explicitly separate local tests from production acceptance.', 'Release process reuses server-side quality gates, independent approval and worker API.'], 'batches':records} (OUT/'V8_28_evidence.json').write_text(json.dumps(report,ensure_ascii=False,indent=2),encoding='utf-8') status='全部验证通过(含一次修正旧版断言后的独立复测)' if not unresolved else '仍有失败或超时' rows=''.join('{}{}{}{}{:.1f}s'.format( x['batch'],html.escape(', '.join(x['files'])),html.escape(x['status']),x['passed_count'],x['seconds']) for x in records) page=f'''SiteForge V8.28 集成验收
SITEFORGE / V8.28.0-dev.1

新手友好上线中心 · 集成验收

结论:历史回归{status}。本报告仅说明当前隔离环境测试;不代表生产上线完成。

{passed}Python 用例通过
{verified_batches}/{len(records)}最终验证批次
{len(unresolved)}尚未关闭的失败/超时批次

回归差异说明

初次分组回归第 22 批中,V8.10 旧测试仍以“构建与交付”为固定导航名称,产生 1 项断言失败;按 V8.28 已确认的新菜单“生成与预览 / 检查并上线”更新测试后,第 22 批独立复测 13 项全部通过。最初失败日志保留,不将其隐藏为初次通过。

本版开发改动

侧边菜单统一可点击;新增“检查并上线”六步操作中心;修正审批与发布前检查的顺序;上线版本选择、人工预览确认、审批状态、发布条件、部署任务与外网核验有清晰区分;同一版重复提交审批和部署有防护;移动端 390px 适配。

原有数据结构、登录 CSRF、角色、发布审批和远程 Worker 协议均继续保留。

验证边界

HTTP + Worker 构建、ZIP 与预览通过,隔离 SQLite
Chromium 后台 UI 流程通过,使用模拟接口、无真实部署
JavaScript 专项及历史测试详见 Node.js 测试日志
Art Design Pro 官方工程 pnpm build尚未验证
生产 PostgreSQL、Redis、多 Worker尚未验证
真实 HTTPS/SFTP/S3 发布、回滚、DNS、CDN尚未验证

分批回归详情

{rows}
批次模块状态通过用例耗时

交付材料

docs/V8_28_GUIDED_LAUNCH_CENTER.md、scripts/browser_launch_v828.py、scripts/regression_v828.py、tests_js/workspace_launch_v828.test.cjs 和 HTML 离线演示。

''' (OUT/'V8_28_集成验收报告.html').write_text(page,encoding='utf-8') (OUT/'V8_28_集成验收摘要.md').write_text(f'# SiteForge V8.28 集成验收\n\n- Python 模块:{report["regression_modules"]}\n- 最终验证批次:{report["regression_verified_batches"]}/{report["regression_batches"]} 通过(第 22 批旧断言更新后复测)\n- 测试用例通过:{passed}\n- 初次运行失败批次:{len(errors)};最终未关闭批次:{len(unresolved)}\n- Chromium 模拟接口工作台:通过\n- HTTP + Worker 隔离构建:通过\n- 正式 PostgreSQL/Redis/Art Design Pro 编译/远程部署:未实测\n\n本结果不可替代生产验收。\n',encoding='utf-8') print('Generated v828 reports; passed:',passed,'unpassed batches:',len(errors))