ci.ts

3.1 KB · raw

1#!/usr/bin/env bun2// peach-browser/scripts/ci.ts3// Run at the end of a work session to verify everything is solid.4// Usage: bun run scripts/ci.ts56import { 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';1516interface Step {17  name: string;18  cmd: string[];19  cwd?: string;20}2122const steps: Step[] = [23  {24    name: 'TypeScript',25    cmd: ['npx', 'tsc', '--noEmit'],26  },27  {28    name: 'Tests',29    cmd: ['npm', 'test', '--', '--reporter=verbose'],30  },31  {32    name: 'Chrome build',33    cmd: ['npm', 'run', 'build:chrome'],34  },35  {36    name: 'Firefox build',37    cmd: ['npm', 'run', 'build:firefox'],38  },39  {40    name: 'Mobile build',41    cmd: ['npm', 'run', 'build:mobile'],42  },43];4445function run(step: Step): { ok: boolean; output: string; duration: number } {46  const start = Date.now();47  const result = spawnSync(step.cmd, {48    cwd: step.cwd ?? process.cwd(),49    env: process.env,50    stdout: 'pipe',51    stderr: 'pipe',52  });53  const duration = Date.now() - start;54  const ok = result.exitCode === 0;55  const output = [56    result.stdout?.toString().trim(),57    result.stderr?.toString().trim(),58  ].filter(Boolean).join('\n');5960  return { ok, output, duration };61}6263function formatDuration(ms: number): string {64  if (ms < 1000) return `${ms}ms`;65  return `${(ms / 1000).toFixed(1)}s`;66}6768function printStep(name: string, ok: boolean, duration: number) {69  const icon = ok ? `${GREEN}✓${RESET}` : `${RED}✗${RESET}`;70  const time = `${DIM}${formatDuration(duration)}${RESET}`;71  console.log(`  ${icon}  ${name.padEnd(20)} ${time}`);72}7374async function main() {75  console.log();76  console.log(`${BOLD}peach-browser CI${RESET}`);77  console.log(`${DIM}${new Date().toLocaleTimeString()}${RESET}`);78  console.log();7980  let failed = false;81  const failures: { name: string; output: string }[] = [];8283  for (const step of steps) {84    process.stdout.write(`  ${DIM}…${RESET}  ${step.name.padEnd(20)}\r`);85    const result = run(step);86    printStep(step.name, result.ok, result.duration);8788    if (!result.ok) {89      failed = true;90      failures.push({ name: step.name, output: result.output });91      break; // fail fast92    }93  }9495  console.log();9697  if (!failed) {98    console.log(`${GREEN}${BOLD}All checks passed.${RESET}`);99    console.log();100101    // Show build artifact sizes102    const chromeManifest = 'dist-chrome/manifest.json';103    if (existsSync(chromeManifest)) {104      const manifest = JSON.parse(await Bun.file(chromeManifest).text());105      console.log(`${DIM}Version: ${manifest.version}${RESET}`);106    }107  } else {108    console.log(`${RED}${BOLD}Failed: ${failures[0].name}${RESET}`);109    console.log();110111    // Print the relevant failure output112    const output = failures[0].output;113    if (output) {114      // Show last 30 lines of output — most errors are at the end115      const lines = output.split('\n');116      const relevant = lines.slice(-30).join('\n');117      console.log(`${DIM}${relevant}${RESET}`);118    }119120    console.log();121    process.exit(1);122  }123}124125main();