#!/usr/bin/env bun // scripts/release-firefox.ts // One-command Firefox AMO release. // Usage: bun run scripts/release-firefox.ts import { execSync } from "child_process"; import { readFileSync, existsSync, mkdirSync } from "fs"; import { join } from "path"; const ROOT = process.cwd(); function run(cmd: string, label?: string, env?: Record) { const display = label ?? cmd; console.log(`\n▶ ${display}`); execSync(cmd, { stdio: "inherit", cwd: ROOT, env: { ...process.env, ...env } }); } function fail(msg: string): never { console.error(`\n✗ ${msg}`); process.exit(1); } // ── Verify environment ──────────────────────────────────────────────────────── if (!process.env.AMO_JWT_ISSUER) { fail("AMO_JWT_ISSUER not set. Add it to your shell profile and reload."); } if (!process.env.AMO_JWT_SECRET) { fail("AMO_JWT_SECRET not set. Add it to your shell profile and reload."); } // ── Read version ────────────────────────────────────────────────────────────── const manifest = JSON.parse( readFileSync(join(ROOT, "manifest.firefox.json"), "utf8") ); const version = manifest.version; if (!version) fail("Could not read version from manifest.firefox.json"); console.log(`\n🍑 Peach Firefox — releasing v${version}`); // ── CI gate ─────────────────────────────────────────────────────────────────── run("npm run ci", "Running CI gate"); // ── Build ───────────────────────────────────────────────────────────────────── run("npm run build:firefox", `Building Firefox extension v${version}`); // ── Package source archive ──────────────────────────────────────────────────── // AMO requires source for any extension with minified/transpiled code. // This zips everything a reviewer needs to build Peach from source. const sourceZip = `peach-source-${version}.zip`; run( [ "zip -r", sourceZip, "src", "scripts", "public", // PB-26-004: the shipped .wasm is transpiled from crypto-core/src/lib.rs — // AMO reviewers must be able to inspect and rebuild it (npm run // build:noise-core). Build cache and generated output are excluded. "crypto-core", "manifest.json", "manifest.firefox.json", "package.json", "bun.lock", "tsconfig.json", "vite.config.ts", "--exclude 'node_modules/*'", "--exclude 'dist-*/*'", "--exclude 'crypto-core/target/*'", "--exclude 'crypto-core/generated/*'", "--exclude '*.zip'", "--exclude '.DS_Store'", "--exclude '__MACOSX/*'", ].join(" "), "Packaging source archive for AMO" ); // ── Ensure artifacts dir ────────────────────────────────────────────────────── if (!existsSync(join(ROOT, "artifacts"))) { mkdirSync(join(ROOT, "artifacts")); } // ── Sign and submit ─────────────────────────────────────────────────────────── run( [ "npx web-ext sign", "--source-dir dist-firefox", "--artifacts-dir artifacts", "--channel=listed", ].join(" "), "Signing and submitting to AMO", // web-ext reads WEB_EXT_API_KEY/WEB_EXT_API_SECRET from env — pass via // child env so the JWT credentials are not visible in the process table. { WEB_EXT_API_KEY: process.env.AMO_JWT_ISSUER!, WEB_EXT_API_SECRET: process.env.AMO_JWT_SECRET!, } ); // ── Cleanup zip ─────────────────────────────────────────────────────────────── run(`trash ${sourceZip}`, "Cleaning up source zip"); // ── Done ────────────────────────────────────────────────────────────────────── console.log(`\n✓ Peach Firefox v${version} submitted to AMO.`); console.log(" Existing users will receive the update automatically."); console.log(" Check AMO developer hub for review status."); console.log("");