release.ts

7.7 KB · raw

1#!/usr/bin/env bun2// scripts/release.ts3// One command to release Peach to both Chrome Web Store and Firefox AMO.4// Usage: npm run release5//        npm run release:chrome   (Chrome only — skips AMO)6//7// Required environment variables (set via setup-chrome-release.sh and shell profile):8//   CWS_CLIENT_ID, CWS_CLIENT_SECRET, CWS_REFRESH_TOKEN, CWS_EXTENSION_ID9//   AMO_JWT_ISSUER, AMO_JWT_SECRET  (not needed for --chrome-only)1011import { execSync } from "child_process";12import { readFileSync, existsSync, mkdirSync, rmSync } from "fs";13import { join } from "path";1415const ROOT = process.cwd();16const CHROME_ONLY = process.argv.includes("--chrome-only");1718const BOLD = "\x1b[1m";19const DIM = "\x1b[2m";20const ACCENT = "\x1b[38;5;208m";21const GREEN = "\x1b[32m";22const RED = "\x1b[31m";23const RESET = "\x1b[0m";2425function run(cmd: string, label?: string, env?: Record<string, string>) {26  console.log(`\n${ACCENT}▶${RESET} ${label ?? cmd}`);27  execSync(cmd, { stdio: "inherit", cwd: ROOT, env: { ...process.env, ...env } });28}2930function success(msg: string) {31  console.log(`${GREEN}✓ ${msg}${RESET}`);32}3334function fail(msg: string): never {35  console.error(`${RED}✗ ${msg}${RESET}`);36  process.exit(1);37}3839function check(name: string): string {40  const val = process.env[name];41  if (!val) fail(`${name} not set. Run setup-chrome-release.sh and reload your shell.`);42  return val!;43}4445// ─── Header ──────────────────────────────────────────────────────────────────4647console.clear();48console.log("");49console.log(`${ACCENT}${BOLD}  ░░  peach release  ░░${RESET}`);50console.log("");5152// ─── Read version ─────────────────────────────────────────────────────────────5354const manifest = JSON.parse(readFileSync(join(ROOT, "manifest.json"), "utf8"));55const version = manifest.version;56if (!version) fail("Could not read version from manifest.json");57const targets = CHROME_ONLY ? "Chrome" : "Chrome + Firefox";58console.log(`${BOLD}Releasing Peach v${version} to ${targets}${RESET}`);59console.log(`${DIM}Stores update automatically for existing users.${RESET}`);6061// ─── Verify environment ───────────────────────────────────────────────────────6263console.log(`\n${BOLD}Checking credentials…${RESET}`);6465const CWS_CLIENT_ID     = check("CWS_CLIENT_ID");66const CWS_CLIENT_SECRET = check("CWS_CLIENT_SECRET");67const CWS_REFRESH_TOKEN = check("CWS_REFRESH_TOKEN");68const CWS_EXTENSION_ID  = check("CWS_EXTENSION_ID");69const CWS_PUBLISHER_ID  = check("PUBLISHER_ID");7071let AMO_JWT_ISSUER = "";72let AMO_JWT_SECRET = "";73if (!CHROME_ONLY) {74  AMO_JWT_ISSUER = check("AMO_JWT_ISSUER");75  AMO_JWT_SECRET = check("AMO_JWT_SECRET");76}7778success("All credentials present");7980// ─── CI gate ──────────────────────────────────────────────────────────────────8182run("npm run ci", "Running CI gate");83success("All tests pass");8485// ─── Build ────────────────────────────────────────────────────────────────────8687if (CHROME_ONLY) {88  run("npm run build:chrome", "Building Chrome target");89} else {90  run("npm run build", "Building all targets (Chrome + Firefox + Mobile)");91}92success("Build complete");9394// ─── Ensure artifacts dir ─────────────────────────────────────────────────────9596if (!existsSync(join(ROOT, "artifacts"))) {97  mkdirSync(join(ROOT, "artifacts"));98}99100// ─── Chrome release ───────────────────────────────────────────────────────────101102console.log(`\n${ACCENT}${BOLD}Chrome Web Store${RESET}`);103104const chromeZip = join(ROOT, "artifacts", `peach-chrome-${version}.zip`);105if (existsSync(chromeZip)) rmSync(chromeZip);106107run(108  `cd dist-chrome && zip -r "${chromeZip}" . --exclude '*.map' --exclude '.DS_Store' --exclude '__MACOSX/*'`,109  `Packaging Chrome extension → peach-chrome-${version}.zip`,110);111112run(113  [114    "npx chrome-webstore-upload-cli upload",115    `--source "${chromeZip}"`,116    `--extension-id "${CWS_EXTENSION_ID}"`,117    "--auto-publish",118  ].join(" "),119  "Uploading and publishing to Chrome Web Store",120  // Credentials via child env, not inline command-line assignments, so they121  // are not visible in the process table. The CLI reads these from env.122  {123    CLIENT_ID: CWS_CLIENT_ID,124    CLIENT_SECRET: CWS_CLIENT_SECRET,125    REFRESH_TOKEN: CWS_REFRESH_TOKEN,126    PUBLISHER_ID: CWS_PUBLISHER_ID,127  },128);129130success(`Chrome v${version} submitted — review typically takes minutes to hours`);131132// ─── Firefox release ──────────────────────────────────────────────────────────133134if (!CHROME_ONLY) {135  console.log(`\n${ACCENT}${BOLD}Firefox Add-ons (AMO)${RESET}`);136137  const sourceZip = `peach-source-${version}.zip`;138  run(139    [140      "zip -r",141      sourceZip,142      "src",143      "scripts",144      "public",145      "manifest.json",146      "manifest.firefox.json",147      "package.json",148      "bun.lock",149      "tsconfig.json",150      "vite.config.ts",151      "vite.config.firefox.ts",152      "vite.config.chrome.ts",153      "vite.config.mobile.ts",154      "--exclude 'node_modules/*'",155      "--exclude 'dist-*/*'",156      "--exclude '*.zip'",157      "--exclude '.DS_Store'",158      "--exclude '__MACOSX/*'",159    ].join(" "),160    "Packaging source archive for AMO",161  );162163  run(164    [165      "npx web-ext sign",166      "--source-dir dist-firefox",167      "--artifacts-dir artifacts",168      "--channel=listed",169    ].join(" "),170    "Signing and submitting to Firefox AMO",171    // web-ext reads WEB_EXT_API_KEY/WEB_EXT_API_SECRET from env — pass via172    // child env so the JWT credentials are not visible in the process table.173    {174      WEB_EXT_API_KEY: AMO_JWT_ISSUER,175      WEB_EXT_API_SECRET: AMO_JWT_SECRET,176    },177  );178179  rmSync(join(ROOT, sourceZip), { force: true });180181  success(`Firefox v${version} submitted — review typically takes minutes`);182}183184// ─── Done ─────────────────────────────────────────────────────────────────────185186console.log("");187console.log(`${ACCENT}${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);188console.log(`${ACCENT}${BOLD}  Peach v${version} released to ${targets}.${RESET}`);189console.log(`${ACCENT}${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);190console.log("");191192if (!CHROME_ONLY) {193  console.log(`  Chrome:  under review — existing users update automatically`);194  console.log(`  Firefox: under review — existing users update automatically`);195} else {196  console.log(`  Chrome: under review — existing users update automatically`);197}198199console.log("");200console.log(`${DIM}Check status:${RESET}`);201console.log(`  Chrome:  https://chrome.google.com/webstore/devconsole`);202if (!CHROME_ONLY) {203  console.log(`  Firefox: https://addons.mozilla.org/developers/`);204}205console.log("");