#!/usr/bin/env node /** * Coverage ratchet (spec AC23): the enforced minimum is the highest coverage * achieved so far; it may rise, never fall. Reads the node test runner * coverage summary from stdin and compares against coverage-ratchet.json. * Run: node --test --experimental-test-coverage ... | node scripts/coverage-ratchet.mjs */ import { readFile, writeFile } from 'node:fs/promises' import { dirname, join } from 'node:path' import { fileURLToPath } from 'node:url' const root = join(dirname(fileURLToPath(import.meta.url)), '..') const ratchetPath = join(root, 'coverage-ratchet.json') let input = '' for await (const chunk of process.stdin) input += String(chunk) // Reporter line looks like: `ℹ all files | 89.34 | 71.54 | 83.44 |` const summaryLine = input .split('\n') .map((l) => l.replace(/^[^\w]*[\u2139]\s*/, '').trim()) .find((l) => /^all files\b/i.test(l)) if (!summaryLine) { console.error('coverage-ratchet: no "all files" summary found on stdin') process.exit(2) } const cells = summaryLine.split('|').map((c) => c.trim()) const linePct = Number.parseFloat(cells[1] ?? '') if (Number.isNaN(linePct)) { console.error(`coverage-ratchet: could not parse line coverage from "${summaryLine}"`) process.exit(2) } let ratchet = {} try { ratchet = JSON.parse(await readFile(ratchetPath, 'utf8')) } catch { ratchet = {} } const floor = typeof ratchet.all === 'number' ? ratchet.all : 0 const achieved = Math.floor(linePct * 10) / 10 if (achieved >= floor) { if (achieved > floor) { ratchet.all = achieved ratchet.updatedAt = new Date().toISOString() ratchet.note = 'ratchet only rises; commit the raise with the change that caused it' await writeFile(ratchetPath, `${JSON.stringify(ratchet, null, 2)}\n`, 'utf8') console.log(`coverage-ratchet: ${achieved}% >= floor ${floor}% (ratchet raised, commit it)`) } else { console.log(`coverage-ratchet: ${achieved}% >= floor ${floor}%`) } } else { console.error(`coverage-ratchet: coverage ${achieved}% is below the committed floor ${floor}%`) process.exit(1) }