426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
241 lines
9.1 KiB
TypeScript
241 lines
9.1 KiB
TypeScript
#!/usr/bin/env bun
|
|
/**
|
|
* E2E coverage matrix report CLI (issue #8802).
|
|
*
|
|
* Builds the canonical coverage matrix (slash commands, #8791 shortcuts, plugin
|
|
* routes, views) from real source, writes `reports/coverage/e2e-matrix.json` +
|
|
* a self-contained HTML contact sheet + a markdown summary, prints a one-line
|
|
* status, and exits non-zero on a blocking gap when enforcement is on.
|
|
*
|
|
* Advisory-then-required (mirrors `.github/workflows/coverage-gate.yml`):
|
|
* - default: report only, exit 0 (advisory).
|
|
* - `--fail-on-missing` or `E2E_COVERAGE_GATE_ENFORCE=1`: exit 1 on a blocking
|
|
* gap (required).
|
|
*
|
|
* Usage:
|
|
* bun packages/scripts/e2e-coverage/write-coverage-matrix-report.ts [--report-dir <dir>] [--json]
|
|
* [--fail-on-missing]
|
|
*/
|
|
|
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
import path from "node:path";
|
|
import {
|
|
buildCoverageMatrix,
|
|
type CoverageMatrix,
|
|
REPO_ROOT,
|
|
} from "./inventory.ts";
|
|
|
|
interface CliOptions {
|
|
reportDir: string;
|
|
json: boolean;
|
|
failOnMissing: boolean;
|
|
}
|
|
|
|
function parseArgs(argv: string[]): CliOptions {
|
|
const options: CliOptions = {
|
|
reportDir: path.join(REPO_ROOT, "reports/coverage"),
|
|
json: false,
|
|
failOnMissing:
|
|
process.env.E2E_COVERAGE_GATE_ENFORCE === "1" ||
|
|
process.env.E2E_COVERAGE_GATE_ENFORCE === "true",
|
|
};
|
|
for (let i = 0; i < argv.length; i += 1) {
|
|
const arg = argv[i];
|
|
if (arg === "--report-dir") {
|
|
options.reportDir = path.resolve(argv[++i] ?? options.reportDir);
|
|
} else if (arg === "--json") {
|
|
options.json = true;
|
|
} else if (arg === "--fail-on-missing") {
|
|
options.failOnMissing = true;
|
|
} else if (arg === "--no-fail") {
|
|
options.failOnMissing = false;
|
|
}
|
|
}
|
|
return options;
|
|
}
|
|
|
|
function escapeHtml(value: string): string {
|
|
return value
|
|
.replace(/&/g, "&")
|
|
.replace(/</g, "<")
|
|
.replace(/>/g, ">")
|
|
.replace(/"/g, """);
|
|
}
|
|
|
|
function renderViewerHtml(): string {
|
|
// A self-contained contact sheet that reads window.E2E_COVERAGE_DATA from the
|
|
// sibling matrix-data.js, mirroring the scenario-catalog viewer shape.
|
|
return `<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>elizaOS e2e coverage matrix</title>
|
|
<style>
|
|
:root { color-scheme: dark; }
|
|
body { margin: 0; font: 14px/1.5 ui-sans-serif, system-ui, sans-serif; background: #0b0b0d; color: #e9e9ec; }
|
|
header { padding: 20px 24px; border-bottom: 1px solid #222; position: sticky; top: 0; background: #0b0b0d; }
|
|
h1 { font-size: 18px; margin: 0 0 6px; }
|
|
.cards { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 12px; }
|
|
.card { border: 1px solid #222; border-radius: 8px; padding: 10px 14px; min-width: 120px; }
|
|
.card .n { font-size: 22px; font-weight: 700; }
|
|
.card .l { font-size: 12px; color: #9a9aa2; }
|
|
main { padding: 16px 24px 64px; }
|
|
.controls { display: flex; gap: 12px; align-items: center; margin-bottom: 12px; flex-wrap: wrap; }
|
|
input, select { background: #141417; color: inherit; border: 1px solid #2a2a2e; border-radius: 6px; padding: 6px 10px; }
|
|
table { width: 100%; border-collapse: collapse; }
|
|
th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid #1c1c20; vertical-align: top; }
|
|
th { color: #9a9aa2; font-weight: 600; position: sticky; top: 0; }
|
|
.pill { display: inline-block; padding: 1px 8px; border-radius: 999px; font-size: 12px; font-weight: 600; }
|
|
.covered { background: #11331f; color: #5fe39a; }
|
|
.exempt { background: #2c2a13; color: #e6d36a; }
|
|
.missing { background: #3a1414; color: #ff8585; }
|
|
.kind { color: #9a9aa2; font-size: 12px; }
|
|
code { background: #141417; padding: 1px 5px; border-radius: 4px; font-size: 12px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<header>
|
|
<h1>elizaOS e2e coverage matrix <span class="kind">— issue #8802</span></h1>
|
|
<div class="kind" id="meta"></div>
|
|
<div class="cards" id="cards"></div>
|
|
</header>
|
|
<main>
|
|
<div class="controls">
|
|
<input id="q" type="search" placeholder="filter by id / detail…" />
|
|
<select id="kind"><option value="">all kinds</option></select>
|
|
<select id="status">
|
|
<option value="">all statuses</option>
|
|
<option value="covered">covered</option>
|
|
<option value="exempt">exempt</option>
|
|
<option value="missing">missing</option>
|
|
</select>
|
|
</div>
|
|
<table>
|
|
<thead><tr><th>Surface</th><th>Kind</th><th>Status</th><th>Detail</th><th>Artifacts</th></tr></thead>
|
|
<tbody id="rows"></tbody>
|
|
</table>
|
|
</main>
|
|
<script src="./matrix-data.js"></script>
|
|
<script>
|
|
const data = window.E2E_COVERAGE_DATA || { items: [], summary: {} };
|
|
const s = data.summary || {};
|
|
document.getElementById('meta').textContent =
|
|
'generated ' + (data.generatedAt || '') + ' · schema ' + (data.schema || '');
|
|
const cardDefs = [
|
|
['commands', (s.commands||{}).covered + '/' + (s.commands||{}).total],
|
|
['plugin routes', (s.pluginRoutes||{}).covered + '/' + (s.pluginRoutes||{}).total + ' (+' + (s.pluginRoutes||{}).exempt + ' exempt)'],
|
|
['shortcuts', (s.shortcuts||{}).gated ? 'gated on #8791' : ((s.shortcuts||{}).covered + '/' + (s.shortcuts||{}).total)],
|
|
['view gates', (s.views||{}).gates],
|
|
['blocking gaps', s.blockingGaps],
|
|
['advisory gaps', s.advisoryGaps],
|
|
];
|
|
document.getElementById('cards').innerHTML = cardDefs
|
|
.map(([l, n]) => '<div class="card"><div class="n">' + n + '</div><div class="l">' + l + '</div></div>')
|
|
.join('');
|
|
const kinds = [...new Set((data.items||[]).map(i => i.kind))];
|
|
const ksel = document.getElementById('kind');
|
|
for (const k of kinds) { const o = document.createElement('option'); o.value = k; o.textContent = k; ksel.appendChild(o); }
|
|
function render() {
|
|
const q = document.getElementById('q').value.toLowerCase();
|
|
const kf = ksel.value, sf = document.getElementById('status').value;
|
|
const rows = (data.items||[]).filter(i =>
|
|
(!kf || i.kind === kf) && (!sf || i.status === sf) &&
|
|
(!q || (i.id + ' ' + i.detail).toLowerCase().includes(q)))
|
|
.map(i => '<tr><td><code>' + i.id + '</code></td><td class="kind">' + i.kind +
|
|
'</td><td><span class="pill ' + i.status + '">' + i.status + '</span></td><td>' +
|
|
i.detail + '</td><td class="kind">' + (i.artifacts||[]).join('<br>') + '</td></tr>').join('');
|
|
document.getElementById('rows').innerHTML = rows;
|
|
}
|
|
for (const id of ['q','kind','status']) document.getElementById(id).addEventListener('input', render);
|
|
render();
|
|
</script>
|
|
</body>
|
|
</html>
|
|
`;
|
|
}
|
|
|
|
function renderMarkdown(matrix: CoverageMatrix): string {
|
|
const s = matrix.summary;
|
|
const lines: string[] = [];
|
|
lines.push("# e2e coverage matrix (issue #8802)");
|
|
lines.push("");
|
|
lines.push(`Generated: ${matrix.generatedAt}`);
|
|
lines.push("");
|
|
lines.push(`- Commands: ${s.commands.covered}/${s.commands.total} covered`);
|
|
lines.push(
|
|
`- Plugin routes: ${s.pluginRoutes.covered}/${s.pluginRoutes.total} covered (+${s.pluginRoutes.exempt} exempt)`,
|
|
);
|
|
lines.push(
|
|
`- Shortcuts: ${s.shortcuts.gated ? "gated on #8791 (advisory)" : `${s.shortcuts.covered}/${s.shortcuts.total}`}`,
|
|
);
|
|
lines.push(`- View gates: ${s.views.gates}`);
|
|
lines.push(
|
|
`- Blocking gaps: ${s.blockingGaps} · Advisory gaps: ${s.advisoryGaps}`,
|
|
);
|
|
lines.push("");
|
|
if (matrix.blockingGaps.length > 0) {
|
|
lines.push("## Blocking gaps");
|
|
for (const gap of matrix.blockingGaps) {
|
|
lines.push(`- \`${gap.id}\` — ${gap.detail}`);
|
|
}
|
|
lines.push("");
|
|
}
|
|
if (matrix.advisoryGaps.length > 0) {
|
|
lines.push("## Advisory gaps");
|
|
for (const gap of matrix.advisoryGaps) {
|
|
lines.push(`- \`${gap.id}\` — ${gap.detail}`);
|
|
}
|
|
lines.push("");
|
|
}
|
|
return lines.join("\n");
|
|
}
|
|
|
|
function main(): number {
|
|
const options = parseArgs(process.argv.slice(2));
|
|
const matrix = buildCoverageMatrix({
|
|
generatedAt: new Date().toISOString(),
|
|
});
|
|
|
|
mkdirSync(options.reportDir, { recursive: true });
|
|
writeFileSync(
|
|
path.join(options.reportDir, "e2e-matrix.json"),
|
|
`${JSON.stringify(matrix, null, 2)}\n`,
|
|
);
|
|
const viewerDir = path.join(options.reportDir, "viewer");
|
|
mkdirSync(viewerDir, { recursive: true });
|
|
writeFileSync(path.join(viewerDir, "index.html"), renderViewerHtml());
|
|
writeFileSync(
|
|
path.join(viewerDir, "matrix-data.js"),
|
|
`window.E2E_COVERAGE_DATA = ${JSON.stringify(matrix)};\n`,
|
|
);
|
|
writeFileSync(
|
|
path.join(options.reportDir, "README.md"),
|
|
renderMarkdown(matrix),
|
|
);
|
|
|
|
const s = matrix.summary;
|
|
if (options.json) {
|
|
process.stdout.write(`${JSON.stringify(matrix, null, 2)}\n`);
|
|
} else {
|
|
process.stdout.write(
|
|
`e2e coverage — commands ${s.commands.covered}/${s.commands.total}; ` +
|
|
`routes ${s.pluginRoutes.covered}/${s.pluginRoutes.total} (+${s.pluginRoutes.exempt} exempt); ` +
|
|
`blocking gaps ${s.blockingGaps}; advisory ${s.advisoryGaps}\n`,
|
|
);
|
|
for (const gap of matrix.blockingGaps) {
|
|
process.stdout.write(` BLOCKING ${gap.id}: ${escapeHtml(gap.detail)}\n`);
|
|
}
|
|
}
|
|
|
|
if (options.failOnMissing && matrix.blockingGaps.length > 0) {
|
|
process.stderr.write(
|
|
`\n${matrix.blockingGaps.length} blocking e2e coverage gap(s); see ${path.relative(REPO_ROOT, options.reportDir)}/README.md\n`,
|
|
);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
process.exit(main());
|