78 lines · 2.4 KB · TypeScript
RawDownload
// Writes runs/manifest.json — the machine-readable inventory the matrix// renders (scenario × target + per-run status). Rebuilt after every scenario.import {  existsSync,  readFileSync,  readdirSync,  renameSync,  writeFileSync,  type Dirent,} from "node:fs";import { join } from "node:path"; export interface ManifestRun {  readonly scenario: string;  readonly target: string;  readonly slug: string;  readonly ok: boolean;  readonly durationMs?: number;  readonly endedAt?: number;} export interface ManifestSkip {  readonly scenario: string;  readonly target: string;  readonly missing: ReadonlyArray<string>;} export const buildManifest = (runsDir: string): void => {  const runs: ManifestRun[] = [];  const skips: ManifestSkip[] = [];   for (const target of readdirSync(runsDir, { withFileTypes: true })) {    if (!target.isDirectory() || target.name === "assets") continue;    // Both vitest projects build the manifest concurrently while runs are    // being (re)written — tolerate dirs vanishing mid-scan.    let slugs: Dirent[];    try {      slugs = readdirSync(join(runsDir, target.name), { withFileTypes: true });    } catch {      continue;    }    for (const slug of slugs) {      if (!slug.isDirectory()) continue;      const dir = join(runsDir, target.name, slug.name);      const resultPath = join(dir, "result.json");      if (existsSync(resultPath)) {        try {          const result = JSON.parse(readFileSync(resultPath, "utf8"));          runs.push({            scenario: result.scenario,            target: target.name,            slug: slug.name,            ok: result.ok,            durationMs: result.durationMs,            endedAt: result.endedAt,          });          continue;        } catch {          // unreadable result — fall through to the skip marker        }      }      const skipPath = join(dir, "skipped.json");      if (existsSync(skipPath)) {        try {          const skip = JSON.parse(readFileSync(skipPath, "utf8"));          skips.push({ scenario: skip.scenario, target: target.name, missing: skip.missing });        } catch {          // ignore        }      }    }  }   // Write-then-rename so a concurrent reader/writer never sees a torn file.  const tmp = join(runsDir, `.manifest-${process.pid}.tmp`);  writeFileSync(tmp, JSON.stringify({ generatedAt: Date.now(), runs, skips }, null, 1));  renameSync(tmp, join(runsDir, "manifest.json"));};