Track HiveGrid Cloud planning reference
This commit is contained in:
1 parent
b3d5ea6764
commit
e341c5938a
5784 files changed
+1104929
-3
No files matched your search
@@ -0,0 +1,213 @@
|
||||
#!/usr/bin/env node
|
||||
/* eslint-disable no-console */
|
||||
import { createHash } from "node:crypto";
|
||||
import { mkdir, writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { markFailedBaselineGates } from "./baseline-gate-exit.mjs";
|
||||
import { spawn } from "node:child_process";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const repoRoot = path.resolve(__dirname, "..");
|
||||
export const reportPath = path.join(repoRoot, ".tc/baselines/observability-console/baseline-health.md");
|
||||
|
||||
export const gates = [
|
||||
{ id: "typecheck", command: "pnpm", args: ["run", "typecheck"] },
|
||||
{ id: "lint", command: "pnpm", args: ["run", "lint"] },
|
||||
{
|
||||
id: "vitest-observability-boundary",
|
||||
command: "pnpm",
|
||||
args: [
|
||||
"exec",
|
||||
"vitest",
|
||||
"run",
|
||||
"apps/api/src/middleware/request-id.test.ts",
|
||||
"apps/api/src/middleware/error-envelope.test.ts",
|
||||
"apps/worker/src/runtime.test.ts",
|
||||
"apps/worker/test/forced-failure.test.ts",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "console-inventory",
|
||||
command: "pnpm",
|
||||
args: ["exec", "tsx", "scripts/observability/scan-console.ts", "--check"],
|
||||
},
|
||||
];
|
||||
|
||||
function shellCommand({ command, args }) {
|
||||
return [command, ...args].join(" ");
|
||||
}
|
||||
|
||||
function outputFingerprint(output) {
|
||||
const normalized = output
|
||||
.split(/\r?\n/)
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean)
|
||||
.slice(-40)
|
||||
.join("\n");
|
||||
const hash = createHash("sha256").update(output).digest("hex").slice(0, 12);
|
||||
return {
|
||||
hash,
|
||||
excerpt: normalized.slice(0, 900) || "(no output)",
|
||||
};
|
||||
}
|
||||
|
||||
function parseNumber(value) {
|
||||
const parsed = Number.parseInt(value, 10);
|
||||
return Number.isFinite(parsed) ? parsed : 0;
|
||||
}
|
||||
|
||||
function countTypeScriptFailures(output) {
|
||||
return (output.match(/error TS\d+/g) ?? []).length;
|
||||
}
|
||||
|
||||
function countEslintFailures(output) {
|
||||
const problemsMatch = output.match(/✖\s+(\d+)\s+problems?\s+\((\d+)\s+errors?/i);
|
||||
if (problemsMatch) return parseNumber(problemsMatch[2]);
|
||||
|
||||
const compactMatch = output.match(/(\d+)\s+errors?/i);
|
||||
if (compactMatch) return parseNumber(compactMatch[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function countVitestFailures(output) {
|
||||
const failedTestMatch = output.match(/Tests\s+(\d+)\s+failed/i);
|
||||
if (failedTestMatch) return parseNumber(failedTestMatch[1]);
|
||||
|
||||
const failedFilesMatch = output.match(/Test Files\s+(\d+)\s+failed/i);
|
||||
if (failedFilesMatch) return parseNumber(failedFilesMatch[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function countConsoleInventoryFailures(output) {
|
||||
const inventoryMatch = output.match(/Console inventory matches:\s+(\d+)/i);
|
||||
if (inventoryMatch) return parseNumber(inventoryMatch[1]);
|
||||
|
||||
const scanConsoleMatch = output.match(/scan-console:\s+(\d+)\s+hit/i);
|
||||
if (scanConsoleMatch) return parseNumber(scanConsoleMatch[1]);
|
||||
|
||||
return output.trim() ? 1 : 0;
|
||||
}
|
||||
|
||||
export function failureCount(gateId, stdout, stderr) {
|
||||
const output = `${stdout}\n${stderr}`;
|
||||
if (gateId === "typecheck") return countTypeScriptFailures(output);
|
||||
if (gateId === "lint") return countEslintFailures(output);
|
||||
if (gateId === "vitest-observability-boundary") return countVitestFailures(output);
|
||||
if (gateId === "console-inventory") return countConsoleInventoryFailures(output);
|
||||
return output.trim() ? 1 : 0;
|
||||
}
|
||||
|
||||
export function gateFailureCount(gateId, stdout, stderr, exitCode) {
|
||||
const parsedFailureCount = failureCount(gateId, stdout, stderr);
|
||||
return exitCode === 0 ? parsedFailureCount : Math.max(parsedFailureCount, 1);
|
||||
}
|
||||
|
||||
function runShellGate(gate) {
|
||||
return new Promise((resolve) => {
|
||||
const startedAt = Date.now();
|
||||
const child = spawn(gate.command, gate.args, {
|
||||
cwd: repoRoot,
|
||||
env: { ...process.env, CI: "1" },
|
||||
shell: false,
|
||||
});
|
||||
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
child.stdout.on("data", (chunk) => {
|
||||
stdout += chunk;
|
||||
});
|
||||
child.stderr.on("data", (chunk) => {
|
||||
stderr += chunk;
|
||||
});
|
||||
child.on("error", (error) => {
|
||||
stderr += `\n${error.message}`;
|
||||
});
|
||||
child.on("close", (code) => {
|
||||
const output = `${stdout}\n${stderr}`;
|
||||
const exitCode = code ?? 1;
|
||||
resolve({
|
||||
id: gate.id,
|
||||
command: shellCommand(gate),
|
||||
exitCode,
|
||||
durationMs: Date.now() - startedAt,
|
||||
failureCount: gateFailureCount(gate.id, stdout, stderr, exitCode),
|
||||
fingerprint: outputFingerprint(output),
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function runGate(gate) {
|
||||
return runShellGate(gate);
|
||||
}
|
||||
|
||||
export function renderReport(results) {
|
||||
const lines = [
|
||||
"# Observability Console Baseline Health",
|
||||
"",
|
||||
`Generated: ${new Date().toISOString()}`,
|
||||
"",
|
||||
"This report captures non-E2E baseline gates for Observability Console baseline. The runner exits 0 when every gate was executed and this report was written, even if individual gates fail.",
|
||||
"",
|
||||
];
|
||||
|
||||
for (const result of results) {
|
||||
lines.push(
|
||||
`## ${result.id}`,
|
||||
"",
|
||||
`- Command: \`${result.command}\``,
|
||||
`- Exitcode: ${result.exitCode}`,
|
||||
`- Failure Count: ${result.failureCount}`,
|
||||
`- Duration: ${result.durationMs}ms`,
|
||||
`- Output Fingerprint: ${result.fingerprint.hash}`,
|
||||
"",
|
||||
"```text",
|
||||
result.fingerprint.excerpt,
|
||||
"```",
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
return `${lines.join("\n")}\n`;
|
||||
}
|
||||
|
||||
async function writeFailureReport(error) {
|
||||
await mkdir(path.dirname(reportPath), { recursive: true });
|
||||
const output = error?.stack ?? String(error);
|
||||
const result = {
|
||||
id: "runner-error",
|
||||
command: "node scripts/verify-observability-console-baseline.mjs",
|
||||
exitCode: 1,
|
||||
durationMs: 0,
|
||||
failureCount: 1,
|
||||
fingerprint: outputFingerprint(output),
|
||||
};
|
||||
await writeFile(reportPath, renderReport([result]), "utf8");
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const results = [];
|
||||
for (const gate of gates) {
|
||||
console.log(`Running ${gate.id}: ${shellCommand(gate)}`);
|
||||
results.push(await runGate(gate));
|
||||
}
|
||||
|
||||
await mkdir(path.dirname(reportPath), { recursive: true });
|
||||
await writeFile(reportPath, renderReport(results), "utf8");
|
||||
console.log(`Wrote ${path.relative(repoRoot, reportPath)}`);
|
||||
markFailedBaselineGates(results);
|
||||
}
|
||||
|
||||
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
||||
main().catch(async (error) => {
|
||||
console.error(error);
|
||||
try {
|
||||
await writeFailureReport(error);
|
||||
} finally {
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user