import { mkdir, readFile, readdir, writeFile } from "node:fs/promises"; import path from "node:path"; const repoRoot = process.cwd(); const badgeDir = path.join(repoRoot, "docs", "badges"); const manifestPath = path.join(badgeDir, "badges.json"); const resultStyles = { success: { message: "passing", color: "#2ea043" }, failure: { message: "failing", color: "#cf222e" }, cancelled: { message: "cancelled", color: "#bf8700" }, skipped: { message: "skipped", color: "#6e7781" }, unknown: { message: "unknown", color: "#6e7781" }, }; const badgeDefinitions = { "dev-sha": { label: "dev", message: "unknown", color: "#0969da" }, "code-check": { label: "code check", message: "unknown", color: "#6e7781" }, biome: { label: "biome", message: "unknown", color: "#6e7781" }, "userfront-e2e-fast": { label: "userfront e2e fast", message: "unknown", color: "#6e7781", }, "userfront-e2e-full": { label: "userfront e2e full", message: "unknown", color: "#6e7781", }, "adminfront-coverage": { label: "adminfront coverage", message: "38.89%", color: "#bf8700", }, "devfront-coverage": { label: "devfront coverage", message: "8.87%", color: "#cf222e", }, "orgfront-coverage": { label: "orgfront coverage", message: "37.50%", color: "#bf8700", }, }; function normalizeResult(result) { return resultStyles[result] ? result : "unknown"; } function styleForResult(result) { return resultStyles[normalizeResult(result)]; } function colorForCoverage(percent) { if (percent >= 80) return "#2ea043"; if (percent >= 35) return "#bf8700"; return "#cf222e"; } function escapeXml(value) { return String(value) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } function textWidth(text) { return Math.max(38, Math.ceil(String(text).length * 6.8 + 10)); } function renderBadge({ label, message, color }) { const labelWidth = textWidth(label); const messageWidth = textWidth(message); const width = labelWidth + messageWidth; const labelCenter = labelWidth / 2; const messageCenter = labelWidth + messageWidth / 2; return ` ${escapeXml(label)}: ${escapeXml(message)} ${escapeXml(label)} ${escapeXml(label)} ${escapeXml(message)} ${escapeXml(message)} `; } async function readJsonIfExists(filePath) { try { return JSON.parse(await readFile(filePath, "utf8")); } catch { return null; } } async function findCoverageSummary(directory) { const entries = await readdir(directory, { withFileTypes: true }).catch( () => [], ); for (const entry of entries) { const entryPath = path.join(directory, entry.name); if (entry.isFile() && entry.name === "vitest-coverage-summary.json") { return entryPath; } if (entry.isDirectory()) { const found = await findCoverageSummary(entryPath); if (found) return found; } } return null; } function updateResultBadge(manifest, key, result) { const style = styleForResult(result); manifest.badges[key] = { ...(manifest.badges[key] ?? badgeDefinitions[key]), message: style.message, color: style.color, }; } function updateCoverageBadges(manifest, coverageSummary) { for (const row of coverageSummary.packages ?? []) { const key = `${row.package}-coverage`; if (!badgeDefinitions[key]) continue; const statements = Number(row.statements); manifest.badges[key] = { ...(manifest.badges[key] ?? badgeDefinitions[key]), message: `${statements.toFixed(2)}%`, color: colorForCoverage(statements), }; } } function shortSha(value) { return String(value ?? "").trim().slice(0, 12); } const existingManifest = process.env.RESET_BADGES === "true" ? null : await readJsonIfExists(manifestPath); const sourceSha = shortSha(process.env.BADGE_SOURCE_SHA || process.env.GITHUB_SHA); const manifest = { schemaVersion: 1, generatedBy: "scripts/update_code_check_badges.mjs", updatedAt: new Date().toISOString(), source: { branch: process.env.BADGE_SOURCE_BRANCH || "dev", sha: process.env.BADGE_SOURCE_SHA || process.env.GITHUB_SHA || null, shortSha: sourceSha || null, runId: process.env.GITHUB_RUN_ID || null, runNumber: process.env.GITHUB_RUN_NUMBER || null, }, badges: { ...badgeDefinitions, ...(existingManifest?.badges ?? {}), }, }; manifest.badges["dev-sha"] = { ...badgeDefinitions["dev-sha"], message: sourceSha || "unknown", }; const jobResults = { lint: process.env.LINT_RESULT, biome: process.env.BIOME_RESULT, backend: process.env.BACKEND_RESULT, userfront: process.env.USERFRONT_RESULT, userfrontE2e: process.env.USERFRONT_E2E_RESULT, coverage: process.env.COVERAGE_RESULT, adminfront: process.env.ADMINFRONT_RESULT, devfront: process.env.DEVFRONT_RESULT, orgfront: process.env.ORGFRONT_RESULT, }; const overallResults = Object.values(jobResults).filter(Boolean); const hasFailure = overallResults.some((result) => ["failure", "cancelled"].includes(result), ); const allSkipped = overallResults.length > 0 && overallResults.every((result) => result === "skipped"); if (process.env.BADGE_UPDATE_CODE_CHECK !== "false") { updateResultBadge( manifest, "code-check", overallResults.length === 0 ? "unknown" : hasFailure ? "failure" : allSkipped ? "skipped" : "success", ); } updateResultBadge(manifest, "biome", jobResults.biome); const e2eWasFull = process.env.USERFRONT_E2E_FULL === "true"; if (jobResults.userfrontE2e && jobResults.userfrontE2e !== "skipped") { updateResultBadge( manifest, e2eWasFull ? "userfront-e2e-full" : "userfront-e2e-fast", jobResults.userfrontE2e, ); } if (jobResults.coverage === "failure" || jobResults.coverage === "cancelled") { for (const key of [ "adminfront-coverage", "devfront-coverage", "orgfront-coverage", ]) { updateResultBadge(manifest, key, "failure"); } } else { const coverageSummaryPath = process.env.COVERAGE_SUMMARY_PATH || (await findCoverageSummary(path.join(repoRoot, "badge-artifacts"))); const coverageSummary = coverageSummaryPath ? await readJsonIfExists(coverageSummaryPath) : null; if (coverageSummary) { updateCoverageBadges(manifest, coverageSummary); } } await mkdir(badgeDir, { recursive: true }); await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`); for (const [key, badge] of Object.entries(manifest.badges)) { await writeFile(path.join(badgeDir, `${key}.svg`), renderBadge(badge)); } console.log(`Updated ${Object.keys(manifest.badges).length} badge files.`);