123456import { spawnSync } from 'bun';7import { existsSync } from 'fs';89const RESET = '\x1b[0m';10const GREEN = '\x1b[32m';11const RED = '\x1b[31m';12const YELLOW = '\x1b[33m';13const BOLD = '\x1b[1m';14const DIM = '\x1b[2m';15const RUN_FIREFOX_BROWSER_CHECKS = process.env.CI === 'true' || process.env.PEACH_E2E_FIREFOX === '1';1617interface Step {18 name: string;19 cmd: string[];20 cwd?: string;21}2223const steps: Step[] = [24 {25 26 27 28 name: 'Lockfile freshness',29 cmd: ['bun', 'install', '--frozen-lockfile'],30 },31 {32 name: 'TypeScript',33 cmd: ['npx', 'tsc', '--noEmit'],34 },35 {36 name: 'Lint',37 cmd: ['npm', 'run', 'lint'],38 },39 {40 name: 'Tests',41 cmd: ['npm', 'test', '--', '--reporter=verbose'],42 },43 {44 45 46 name: 'Noise WASM provenance',47 cmd: ['node', 'scripts/check-noise-core.mjs'],48 },49 {50 name: 'Chrome build',51 cmd: ['npm', 'run', 'build:chrome'],52 },53 {54 name: 'Firefox build',55 cmd: ['npm', 'run', 'build:firefox'],56 },57 ...(RUN_FIREFOX_BROWSER_CHECKS58 ? [{59 name: 'Firefox sender context',60 cmd: ['npm', 'run', 'test:firefox:sender'],61 }]62 : []),63];6465function run(step: Step): { ok: boolean; output: string; duration: number } {66 const start = Date.now();67 const result = spawnSync(step.cmd, {68 cwd: step.cwd ?? process.cwd(),69 env: process.env,70 stdout: 'pipe',71 stderr: 'pipe',72 });73 const duration = Date.now() - start;74 const ok = result.exitCode === 0;75 const output = [76 result.stdout?.toString().trim(),77 result.stderr?.toString().trim(),78 ].filter(Boolean).join('\n');7980 return { ok, output, duration };81}8283function formatDuration(ms: number): string {84 if (ms < 1000) return `${ms}ms`;85 return `${(ms / 1000).toFixed(1)}s`;86}8788function printStep(name: string, ok: boolean, duration: number) {89 const icon = ok ? `${GREEN}✓${RESET}` : `${RED}✗${RESET}`;90 const time = `${DIM}${formatDuration(duration)}${RESET}`;91 console.log(` ${icon} ${name.padEnd(20)} ${time}`);92}9394async function main() {95 console.log();96 console.log(`${BOLD}peach-browser CI${RESET}`);97 console.log(`${DIM}${new Date().toLocaleTimeString()}${RESET}`);98 console.log();99100 let failed = false;101 const failures: { name: string; output: string }[] = [];102103 for (const step of steps) {104 process.stdout.write(` ${DIM}…${RESET} ${step.name.padEnd(20)}\r`);105 const result = run(step);106 printStep(step.name, result.ok, result.duration);107108 if (!result.ok) {109 failed = true;110 failures.push({ name: step.name, output: result.output });111 break; 112 }113 }114115 console.log();116117 if (!failed) {118 console.log(`${GREEN}${BOLD}All checks passed.${RESET}`);119 console.log();120121 122 const chromeManifest = 'dist-chrome/manifest.json';123 if (existsSync(chromeManifest)) {124 const manifest = JSON.parse(await Bun.file(chromeManifest).text());125 console.log(`${DIM}Version: ${manifest.version}${RESET}`);126 }127 } else {128 console.log(`${RED}${BOLD}Failed: ${failures[0].name}${RESET}`);129 console.log();130131 132 const output = failures[0].output;133 if (output) {134 135 const lines = output.split('\n');136 const relevant = lines.slice(-30).join('\n');137 console.log(`${DIM}${relevant}${RESET}`);138 }139140 console.log();141 process.exit(1);142 }143}144145main();