Prepare 0.1.0 for lab release: durable handlers, HTTP Compose, CI, and pulumi-tests.

- Harden DB-backed handlers and seed profiles; align client wire shapes for
  cluster resources, QEMU config, and node SSL fields
- Serve plain HTTP on Compose :8006; keep TLS optional (--profile tls) and
  terminate HTTPS at Kubernetes Ingress
- Add pulumi-tests (full contract surface majors 6–9 + BPG lifecycle) and
  make pulumi-tests
- Ship bilingual docs, CHANGELOG, SECURITY, CONTRIBUTING, and GitHub Actions
  (make ci + Compose/Helm validation)
This commit is contained in:
Sergey Antropoff
2026-07-18 04:18:05 +03:00
parent 777926487b
commit 48df10b17e
172 changed files with 7528 additions and 1208 deletions
+265
View File
@@ -0,0 +1,265 @@
"""Render HTML and optional JUnit reports for the Pulumi HX suite."""
from __future__ import annotations
import html
import json
from pathlib import Path
from typing import Any
from xml.etree.ElementTree import Element, ElementTree, SubElement
def write_json(payload: dict[str, Any], path: Path) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
# Drop per-method lists from majors for a lighter default JSON? Keep full for debugging.
path.write_text(json.dumps(payload, indent=2, ensure_ascii=False), encoding="utf-8")
def write_junit(payload: dict[str, Any], path: Path) -> None:
surface = payload.get("surface") or []
scenarios = payload.get("scenarios") or []
cases: list[dict[str, Any]] = []
for major in surface:
cases.append(
{
"classname": "surface",
"name": f"PVE {major.get('version')} major={major.get('major')}",
"time": major.get("time") or 0,
"ok": bool(major.get("ok")),
"error": _surface_error(major),
}
)
for item in scenarios:
cases.append(
{
"classname": "lifecycle",
"name": item.get("id") or item.get("name") or "lifecycle",
"time": item.get("time") or 0,
"ok": bool(item.get("ok")),
"error": item.get("error") or "",
}
)
suite = Element(
"testsuite",
name="pulumi-hx",
tests=str(len(cases)),
failures=str(sum(1 for c in cases if not c["ok"])),
time=f"{sum(float(c['time']) for c in cases):.3f}",
)
for item in cases:
case = SubElement(
suite,
"testcase",
classname=str(item["classname"]),
name=str(item["name"]),
time=f"{float(item['time']):.3f}",
)
if not item["ok"]:
failure = SubElement(case, "failure", message=str(item["error"])[:500])
failure.text = str(item["error"])
path.parent.mkdir(parents=True, exist_ok=True)
ElementTree(suite).write(path, encoding="utf-8", xml_declaration=True)
def _surface_error(major: dict[str, Any]) -> str:
fails = major.get("failures") or []
if not fails:
return ""
parts = [
f"{f.get('verb')} {f.get('path')} -> {f.get('bucket')} {f.get('status', '')}"
for f in fails[:20]
]
return f"{len(fails)} critical: " + "; ".join(parts)
def write_html(payload: dict[str, Any], path: Path) -> None:
surface = payload.get("surface") or []
scenarios = payload.get("scenarios") or []
coverage = payload.get("coverage") or {}
ok = bool(payload.get("ok"))
elapsed = float(payload.get("elapsed") or 0)
coverage_by_major = coverage.get("by_major") or []
coverage_rows = []
for item in coverage_by_major:
complete = bool(item.get("complete"))
coverage_rows.append(
"<tr>"
f"<td>{html.escape(str(item.get('major')))}</td>"
f"<td>{html.escape(str(item.get('version')))}</td>"
f"<td>{html.escape(str(item.get('declared')))}</td>"
f"<td>{html.escape(str(item.get('probed')))}</td>"
f"<td>{html.escape(str(item.get('critical')))}</td>"
f"<td class='{'ok' if complete else 'fail'}'>"
f"{'yes' if complete else 'no'}</td>"
"</tr>"
)
declared_total = int(coverage.get("declared_total") or 0)
probed_total = int(coverage.get("probed_total") or 0)
critical_total = int(coverage.get("critical_total") or 0)
coverage_ok = bool(coverage.get("ok")) if coverage_by_major else True
majs = coverage.get("majors") or []
if len(majs) >= 2:
majors_label = f"{majs[0]}{majs[-1]}"
elif majs:
majors_label = str(majs[0])
else:
majors_label = ""
surface_rows = []
failure_rows = []
for major in surface:
surface_rows.append(
"<tr>"
f"<td>{html.escape(str(major.get('major')))}</td>"
f"<td>{html.escape(str(major.get('version')))}</td>"
f"<td>{html.escape(str(major.get('declared')))}</td>"
f"<td>{html.escape(str(major.get('probed')))}</td>"
f"<td>{html.escape(str(major.get('success_2xx')))}</td>"
f"<td>{html.escape(str(major.get('client_4xx')))}</td>"
f"<td class='{'ok' if major.get('ok') else 'fail'}'>"
f"{html.escape(str(major.get('failure_count')))}</td>"
f"<td>{float(major.get('time') or 0):.1f}s</td>"
"</tr>"
)
for fail in major.get("failures") or []:
failure_rows.append(
"<tr>"
f"<td>{html.escape(str(major.get('version')))}</td>"
f"<td>{html.escape(str(fail.get('verb')))}</td>"
f"<td><code>{html.escape(str(fail.get('path')))}</code></td>"
f"<td>{html.escape(str(fail.get('bucket')))}</td>"
f"<td>{html.escape(str(fail.get('status', fail.get('error', ''))))}</td>"
f"<td><code>{html.escape(str(fail.get('body', ''))[:200])}</code></td>"
"</tr>"
)
scenario_rows = []
for item in scenarios:
scenario_rows.append(
"<tr>"
f"<td>{html.escape(str(item.get('id') or item.get('name')))}</td>"
f"<td class='{'ok' if item.get('ok') else 'fail'}'>"
f"{'PASS' if item.get('ok') else 'FAIL'}</td>"
f"<td>{float(item.get('time') or 0):.2f}s</td>"
f"<td><code>{html.escape(str(item.get('error') or ''))}</code></td>"
"</tr>"
)
status = "PASS" if ok else "FAIL"
status_class = "ok" if ok else "fail"
coverage_status = "complete" if coverage_ok else "incomplete"
coverage_class = "ok" if coverage_ok else "fail"
doc = f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>Pulumi HX suite report</title>
<style>
:root {{
--bg: #0f1419; --panel: #1a222c; --text: #e7ecf1; --muted: #9aa7b5;
--ok: #3dd68c; --fail: #ff6b6b; --line: #2a3542;
--mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
--sans: "Segoe UI", system-ui, sans-serif;
}}
body {{ margin: 0; font-family: var(--sans); background: var(--bg); color: var(--text); }}
main {{ max-width: 1100px; margin: 0 auto; padding: 2rem 1.25rem 4rem; }}
h1 {{ font-size: 1.6rem; margin: 0 0 .4rem; }}
h2 {{ font-size: 1.15rem; margin: 2rem 0 .75rem; }}
p.lead {{ color: var(--muted); margin: 0 0 1.5rem; }}
.badge {{ display: inline-block; padding: .2rem .6rem; border-radius: .35rem;
font-weight: 700; letter-spacing: .02em; }}
.badge.ok {{ background: color-mix(in srgb, var(--ok) 25%, transparent); color: var(--ok); }}
.badge.fail {{ background: color-mix(in srgb, var(--fail) 25%, transparent); color: var(--fail); }}
.cards {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: .75rem; margin: 1rem 0 1.5rem; }}
.card {{ background: var(--panel); border: 1px solid var(--line); border-radius: .5rem;
padding: .9rem 1rem; }}
.card .label {{ color: var(--muted); font-size: .8rem; }}
.card .value {{ font-size: 1.35rem; font-weight: 700; margin-top: .25rem; }}
table {{ width: 100%; border-collapse: collapse; background: var(--panel);
border: 1px solid var(--line); border-radius: .5rem; overflow: hidden; }}
th, td {{ text-align: left; padding: .55rem .7rem; border-bottom: 1px solid var(--line);
vertical-align: top; font-size: .92rem; }}
th {{ color: var(--muted); font-weight: 600; }}
tr:last-child td {{ border-bottom: 0; }}
td.ok {{ color: var(--ok); font-weight: 600; }}
td.fail {{ color: var(--fail); font-weight: 600; }}
code {{ font-family: var(--mono); font-size: .85em; }}
.empty {{ color: var(--muted); font-style: italic; }}
</style>
</head>
<body>
<main>
<h1>Pulumi HX suite report</h1>
<p class="lead">Contract surface probe (PVE majors 69) + lifecycle scenarios</p>
<span class="badge {status_class}">{status}</span>
<div class="cards">
<div class="card"><div class="label">Elapsed</div>
<div class="value">{elapsed:.1f}s</div></div>
<div class="card"><div class="label">Majors</div>
<div class="value">{len(surface)}</div></div>
<div class="card"><div class="label">Critical surface fails</div>
<div class="value">{sum(int(m.get('failure_count') or 0) for m in surface)}</div></div>
<div class="card"><div class="label">Scenarios</div>
<div class="value">{sum(1 for s in scenarios if s.get('ok'))}/{len(scenarios)}</div></div>
</div>
<h2>Full contract coverage</h2>
<p class="lead">
{probed_total}/{declared_total} methods across majors {html.escape(majors_label)}
(critical={critical_total}) —
<span class="{coverage_class}">{coverage_status}</span>
</p>
<table>
<thead><tr>
<th>Major</th><th>Version</th><th>Declared</th><th>Probed</th>
<th>Critical</th><th>declared==probed</th>
</tr></thead>
<tbody>
{''.join(coverage_rows) or '<tr><td colspan="6" class="empty">No coverage data</td></tr>'}
<tr>
<td colspan="2"><strong>Total</strong></td>
<td><strong>{declared_total}</strong></td>
<td><strong>{probed_total}</strong></td>
<td><strong>{critical_total}</strong></td>
<td class="{coverage_class}"><strong>{'yes' if coverage_ok else 'no'}</strong></td>
</tr>
</tbody>
</table>
<h2>Surface by major</h2>
<table>
<thead><tr>
<th>Major</th><th>Version</th><th>Declared</th><th>Probed</th>
<th>2xx</th><th>4xx/auth</th><th>Critical</th><th>Time</th>
</tr></thead>
<tbody>
{''.join(surface_rows) or '<tr><td colspan="8" class="empty">No surface results</td></tr>'}
</tbody>
</table>
<h2>Critical surface failures</h2>
<table>
<thead><tr>
<th>Version</th><th>Verb</th><th>Path</th><th>Bucket</th><th>Status</th><th>Body</th>
</tr></thead>
<tbody>
{''.join(failure_rows) or '<tr><td colspan="6" class="empty">None</td></tr>'}
</tbody>
</table>
<h2>Lifecycle scenarios</h2>
<table>
<thead><tr><th>ID</th><th>Result</th><th>Time</th><th>Error</th></tr></thead>
<tbody>
{''.join(scenario_rows) or '<tr><td colspan="4" class="empty">No scenarios</td></tr>'}
</tbody>
</table>
</main>
</body>
</html>
"""
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(doc, encoding="utf-8")