Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 12:43:05 +08:00

221 lines
7.2 KiB
JavaScript

#!/usr/bin/env node
/**
* Render the certification-verify gate result for humans. Consumes the
* `certify:verify --json` report plus the commit-drift outcome and emits
* either GitHub step-summary markdown (`--mode summary`) or `::error`
* workflow-command annotations (`--mode annotations`). Display only — every
* pass/fail decision was already made by `certify:verify` and
* check-commit-drift.mjs; this file must never turn a red input green.
*
* The point of the summary is the trust handoff: the human merging to main
* sees WHO certified WHAT (reviewer identity is part of the signed payload
* precisely because a signature proves custody, not diligence).
*/
import { createHash, createPublicKey } from "node:crypto";
import { readFileSync } from "node:fs";
import { pathToFileURL } from "node:url";
/**
* Display-only fingerprint (first 16 hex of sha256 over the SPKI DER) so the
* summary names which trusted key was used. The canonical implementation the
* verifier trusts lives in packages/evidence/src/certify/keys.ts.
*/
export function fingerprintPem(pem) {
const der = createPublicKey(pem).export({ type: "spki", format: "der" });
return createHash("sha256").update(der).digest("hex").slice(0, 16);
}
function readJsonIfPossible(filePath) {
if (filePath === undefined) return undefined;
try {
return JSON.parse(readFileSync(filePath, "utf8"));
} catch {
return undefined;
}
}
function formatAge(createdAt, now = Date.now()) {
const createdMs = Date.parse(createdAt);
if (Number.isNaN(createdMs)) return "unparseable createdAt";
const hours = (now - createdMs) / 3_600_000;
return `${hours.toFixed(1)}h old`;
}
function escapeCell(text) {
return String(text).replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
}
const VERDICT_ICONS = { pass: "✅", fail: "❌", waived: "⚠️" };
export function renderSummary({ report, drift, trustedFingerprint, certPath }) {
const lines = ["## certification-verify", ""];
const verifyOk = report?.ok === true;
// No drift outcome supplied (e.g. the selftest's direct render) means drift
// was not part of this evaluation; the workflow always passes one.
const driftOk =
drift === undefined ||
drift.result === "match" ||
drift.result === "allowed-drift";
lines.push(
`**Result:** ${verifyOk && driftOk ? "✅ certification verified" : "❌ certification rejected"}`,
"",
);
lines.push(`- Certification: \`${certPath}\``);
lines.push(
`- Trusted public key (from the base branch): fingerprint \`${trustedFingerprint}\``,
);
const payload = report?.payload;
if (payload !== undefined) {
const reviewer = payload.reviewer ?? {};
const model = reviewer.model !== undefined ? ` (${reviewer.model})` : "";
lines.push(
`- Reviewer: **${reviewer.kind}:${reviewer.id}**${model}`,
`- Tier: \`${payload.tier}\` · Commit: \`${payload.commit}\` (\`${payload.branch}\` → \`${payload.baseRef}\`)`,
`- Created: \`${payload.createdAt}\` (${formatAge(payload.createdAt)})`,
);
} else {
lines.push(
"- Reviewer/tier/commit: unavailable — the certification payload did not parse",
);
}
if (report !== undefined) {
lines.push(
report.bundle !== undefined
? `- Bundle: re-hashed ${report.bundle.artifactCount} artifact(s); rollup completeness re-derived`
: "- Bundle: not supplied to verifier — promotion gate requires `evidence/bundle` and should fail before this point",
);
}
if (drift !== undefined) {
lines.push("", "### Commit drift", "");
lines.push(
`- ${driftOk ? "✅" : "❌"} \`${drift.result}\`: ${drift.detail}`,
);
if ((drift.driftPaths ?? []).length > 0) {
lines.push("", "<details><summary>Drifted paths</summary>", "");
for (const entry of drift.driftPaths) {
const bad = (drift.disallowedPaths ?? []).includes(entry);
lines.push(`- ${bad ? "❌" : "✅"} \`${entry}\``);
}
lines.push("", "</details>");
}
}
if ((payload?.verdicts ?? []).length > 0) {
lines.push("", "### Signed verdicts", "");
lines.push("| Subject | Verdict | Notes |", "| --- | --- | --- |");
for (const verdict of payload.verdicts) {
lines.push(
`| \`${escapeCell(verdict.subject)}\` | ${VERDICT_ICONS[verdict.verdict] ?? ""} ${escapeCell(verdict.verdict)} | ${escapeCell(verdict.notes ?? "")} |`,
);
}
}
const failures = report?.failures ?? [];
if (failures.length > 0) {
lines.push("", "### Verification failures", "");
for (const failure of failures) {
lines.push(`- \`${failure.code}\`: ${failure.message}`);
}
}
if (report === undefined) {
lines.push(
"",
"### Verification failures",
"",
"- verify report missing — `certify:verify` did not produce output; see the job log",
);
}
lines.push("");
return lines.join("\n");
}
export function renderAnnotations({ report, drift }) {
const lines = [];
for (const failure of report?.failures ?? []) {
lines.push(
`::error title=certification-verify (${failure.code})::${failure.message}`,
);
}
if (report === undefined) {
lines.push(
"::error title=certification-verify::certify:verify produced no report; see the job log",
);
}
if (
drift !== undefined &&
drift.result !== "match" &&
drift.result !== "allowed-drift"
) {
lines.push(
`::error title=certification-verify (${drift.result})::${drift.detail}`,
);
}
return lines.join("\n");
}
function main() {
const argv = process.argv.slice(2);
const args = {};
for (let index = 0; index < argv.length; index += 1) {
const flag = argv[index];
const value = () => {
const next = argv[index + 1];
if (next === undefined) {
console.error(`${flag} requires a value`);
process.exit(2);
}
index += 1;
return next;
};
if (flag === "--report") args.reportPath = value();
else if (flag === "--drift") args.driftPath = value();
else if (flag === "--pubkey") args.pubkeyPath = value();
else if (flag === "--cert-path") args.certPath = value();
else if (flag === "--mode") args.mode = value();
else {
console.error(`unknown argument: ${flag}`);
process.exit(2);
}
}
if (args.mode !== "summary" && args.mode !== "annotations") {
console.error(
"Usage: render-check-summary.mjs --mode <summary|annotations> --report <verify.json> --drift <drift.json> --pubkey <pem> --cert-path <display path>",
);
process.exit(2);
}
const report = readJsonIfPossible(args.reportPath);
const drift = readJsonIfPossible(args.driftPath);
let trustedFingerprint = "unavailable";
if (args.pubkeyPath !== undefined) {
try {
trustedFingerprint = fingerprintPem(
readFileSync(args.pubkeyPath, "utf8"),
);
} catch {
trustedFingerprint = "unreadable public key";
}
}
const output =
args.mode === "summary"
? renderSummary({
report,
drift,
trustedFingerprint,
certPath: args.certPath ?? "certification.json",
})
: renderAnnotations({ report, drift });
if (output.length > 0) process.stdout.write(`${output}\n`);
}
if (
process.argv[1] &&
import.meta.url === pathToFileURL(process.argv[1]).href
) {
main();
}