50d4e457800f

add scripts/ci.ts — bun CI runner for typecheck, tests, chrome and firefox builds

time
2026-05-04 23:50 UTC · 5 months ago
machine
koh2
parent
cc7196b4e72a
2 files package.json+2 −1scripts/ci.ts+121 −0
package.json+2 −1
@@ -13,7 +13,8 @@1313     "typecheck": "tsc --noEmit",1414     "test": "vitest run",1515     "test:watch": "vitest",16-    "test:passkeys": "npm run build:e2e && playwright test tests/passkeys.spec.ts"16+    "test:passkeys": "npm run build:e2e && playwright test tests/passkeys.spec.ts",17+    "ci": "bun run scripts/ci.ts"1718   },1819   "dependencies": {1920     "@bokuweb/zstd-wasm": "^0.0.27",

view file

scripts/ci.ts+121 −0
@@ -0,0 +1,121 @@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.ts5+6+import { spawnSync } from 'bun';7+import { existsSync } from 'fs';8+9+const RESET  = '\x1b[0m';10+const GREEN  = '\x1b[32m';11+const RED    = '\x1b[31m';12+const YELLOW = '\x1b[33m';13+const BOLD   = '\x1b[1m';14+const DIM    = '\x1b[2m';15+16+interface Step {17+  name: string;18+  cmd: string[];19+  cwd?: string;20+}21+22+const 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+41+function run(step: Step): { ok: boolean; output: string; duration: number } {42+  const start = Date.now();43+  const result = spawnSync(step.cmd, {44+    cwd: step.cwd ?? process.cwd(),45+    env: process.env,46+    stdout: 'pipe',47+    stderr: 'pipe',48+  });49+  const duration = Date.now() - start;50+  const ok = result.exitCode === 0;51+  const output = [52+    result.stdout?.toString().trim(),53+    result.stderr?.toString().trim(),54+  ].filter(Boolean).join('\n');55+56+  return { ok, output, duration };57+}58+59+function formatDuration(ms: number): string {60+  if (ms < 1000) return `${ms}ms`;61+  return `${(ms / 1000).toFixed(1)}s`;62+}63+64+function printStep(name: string, ok: boolean, duration: number) {65+  const icon = ok ? `${GREEN}✓${RESET}` : `${RED}✗${RESET}`;66+  const time = `${DIM}${formatDuration(duration)}${RESET}`;67+  console.log(`  ${icon}  ${name.padEnd(20)} ${time}`);68+}69+70+async function main() {71+  console.log();72+  console.log(`${BOLD}peach-browser CI${RESET}`);73+  console.log(`${DIM}${new Date().toLocaleTimeString()}${RESET}`);74+  console.log();75+76+  let failed = false;77+  const failures: { name: string; output: string }[] = [];78+79+  for (const step of steps) {80+    process.stdout.write(`  ${DIM}…${RESET}  ${step.name.padEnd(20)}\r`);81+    const result = run(step);82+    printStep(step.name, result.ok, result.duration);83+84+    if (!result.ok) {85+      failed = true;86+      failures.push({ name: step.name, output: result.output });87+      break; // fail fast88+    }89+  }90+91+  console.log();92+93+  if (!failed) {94+    console.log(`${GREEN}${BOLD}All checks passed.${RESET}`);95+    console.log();96+97+    // Show build artifact sizes98+    const chromeManifest = 'dist-chrome/manifest.json';99+    if (existsSync(chromeManifest)) {100+      const manifest = JSON.parse(await Bun.file(chromeManifest).text());101+      console.log(`${DIM}Version: ${manifest.version}${RESET}`);102+    }103+  } else {104+    console.log(`${RED}${BOLD}Failed: ${failures[0].name}${RESET}`);105+    console.log();106+107+    // Print the relevant failure output108+    const output = failures[0].output;109+    if (output) {110+      // Show last 30 lines of output — most errors are at the end111+      const lines = output.split('\n');112+      const relevant = lines.slice(-30).join('\n');113+      console.log(`${DIM}${relevant}${RESET}`);114+    }115+116+    console.log();117+    process.exit(1);118+  }119+}120+121+main();

view file