123456789101112import { execSync } from "child_process";13import { readFileSync, existsSync, mkdirSync } from "fs";14import { join } from "path";1516const ROOT = process.cwd();17const CHROME_ONLY = process.argv.includes("--chrome-only");1819const BOLD = "\x1b[1m";20const DIM = "\x1b[2m";21const ACCENT = "\x1b[38;5;208m";22const GREEN = "\x1b[32m";23const RED = "\x1b[31m";24const RESET = "\x1b[0m";2526function run(cmd: string, label?: string, env?: Record<string, string>) {27 console.log(`\n${ACCENT}▶${RESET} ${label ?? cmd}`);28 execSync(cmd, { stdio: "inherit", cwd: ROOT, env: { ...process.env, ...env } });29}3031function success(msg: string) {32 console.log(`${GREEN}✓ ${msg}${RESET}`);33}3435function fail(msg: string): never {36 console.error(`${RED}✗ ${msg}${RESET}`);37 process.exit(1);38}3940function check(name: string): string {41 const val = process.env[name];42 if (!val) fail(`${name} not set. Export it from your secret store and reload your shell.`);43 return val!;44}45464748console.clear();49console.log("");50console.log(`${ACCENT}${BOLD} ░░ peach release ░░${RESET}`);51console.log("");52535455const manifest = JSON.parse(readFileSync(join(ROOT, "manifest.json"), "utf8"));56const version = manifest.version;57if (!version) fail("Could not read version from manifest.json");58const targets = CHROME_ONLY ? "Chrome" : "Chrome + Firefox";59console.log(`${BOLD}Releasing Peach v${version} to ${targets}${RESET}`);60console.log(`${DIM}Stores update automatically for existing users.${RESET}`);61626364console.log(`\n${BOLD}Checking credentials…${RESET}`);6566const CWS_CLIENT_ID = check("CWS_CLIENT_ID");67const CWS_CLIENT_SECRET = check("CWS_CLIENT_SECRET");68const CWS_REFRESH_TOKEN = check("CWS_REFRESH_TOKEN");69const CWS_EXTENSION_ID = check("CWS_EXTENSION_ID");70const CWS_PUBLISHER_ID = check("PUBLISHER_ID");7172let AMO_JWT_ISSUER = "";73let AMO_JWT_SECRET = "";74if (!CHROME_ONLY) {75 AMO_JWT_ISSUER = check("AMO_JWT_ISSUER");76 AMO_JWT_SECRET = check("AMO_JWT_SECRET");77}7879success("All credentials present");80818283run("npm run ci", "Running CI gate");84success("All tests pass");85868788if (CHROME_ONLY) {89 run("npm run build:chrome", "Building Chrome target");90} else {91 run("npm run build", "Building all targets (Chrome + Firefox)");92}93success("Build complete");94959697if (!existsSync(join(ROOT, "artifacts"))) {98 mkdirSync(join(ROOT, "artifacts"));99}100101102103console.log(`\n${ACCENT}${BOLD}Chrome Web Store${RESET}`);104105const chromeZip = join(ROOT, "artifacts", `peach-chrome-${version}.zip`);106if (existsSync(chromeZip)) run(`trash "${chromeZip}"`, "Moving existing Chrome archive to Trash");107108run(109 `cd dist-chrome && zip -r "${chromeZip}" . --exclude '*.map' --exclude '.DS_Store' --exclude '__MACOSX/*'`,110 `Packaging Chrome extension → peach-chrome-${version}.zip`,111);112113run(114 [115 "npx chrome-webstore-upload-cli upload",116 `--source "${chromeZip}"`,117 `--extension-id "${CWS_EXTENSION_ID}"`,118 "--auto-publish",119 ].join(" "),120 "Uploading and publishing to Chrome Web Store",121 122 123 {124 CLIENT_ID: CWS_CLIENT_ID,125 CLIENT_SECRET: CWS_CLIENT_SECRET,126 REFRESH_TOKEN: CWS_REFRESH_TOKEN,127 PUBLISHER_ID: CWS_PUBLISHER_ID,128 },129);130131success(`Chrome v${version} submitted — review typically takes minutes to hours`);132133134135if (!CHROME_ONLY) {136 console.log(`\n${ACCENT}${BOLD}Firefox Add-ons (AMO)${RESET}`);137138 const sourceZip = join(ROOT, "artifacts", `peach-source-${version}.zip`);139 run(140 [141 "zip -r",142 sourceZip,143 "src",144 "scripts",145 "public",146 147 148 149 "crypto-core",150 "manifest.json",151 "manifest.firefox.json",152 "package.json",153 "bun.lock",154 "tsconfig.json",155 "vite.config.ts",156 "--exclude 'node_modules/*'",157 "--exclude 'dist-*/*'",158 "--exclude 'crypto-core/target/*'",159 "--exclude 'crypto-core/generated/*'",160 "--exclude '*.zip'",161 "--exclude '.DS_Store'",162 "--exclude '__MACOSX/*'",163 ].join(" "),164 "Packaging source archive for AMO",165 );166167 run(168 [169 "npx web-ext sign",170 "--source-dir dist-firefox",171 "--artifacts-dir artifacts",172 "--channel=listed",173 "--upload-source-code",174 sourceZip,175 ].join(" "),176 "Signing and submitting to Firefox AMO",177 178 179 {180 WEB_EXT_API_KEY: AMO_JWT_ISSUER,181 WEB_EXT_API_SECRET: AMO_JWT_SECRET,182 },183 );184185 success(`Firefox v${version} submitted with source archive retained in artifacts/`);186}187188189190console.log("");191console.log(`${ACCENT}${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);192console.log(`${ACCENT}${BOLD} Peach v${version} released to ${targets}.${RESET}`);193console.log(`${ACCENT}${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);194console.log("");195196if (!CHROME_ONLY) {197 console.log(` Chrome: under review — existing users update automatically`);198 console.log(` Firefox: under review — existing users update automatically`);199} else {200 console.log(` Chrome: under review — existing users update automatically`);201}202203console.log("");204console.log(`${DIM}Check status:${RESET}`);205console.log(` Chrome: https://chrome.google.com/webstore/devconsole`);206if (!CHROME_ONLY) {207 console.log(` Firefox: https://addons.mozilla.org/developers/`);208}209console.log("");