From edb37558c87d25d1903474873ce417946feca245 Mon Sep 17 00:00:00 2001 From: Kevin Buffardi Date: Mon, 3 Aug 2026 12:27:33 -0700 Subject: [PATCH 1/4] fix: clarify Firefox support and omit unsigned ZIP --- scripts/e2e-firefox-compatibility.test.mjs | 6 ++++-- scripts/e2e-release-packaging.test.mjs | 22 ++++++++++------------ scripts/release-targets.js | 3 ++- src/ui/browser-capabilities.mjs | 6 ++++++ 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/scripts/e2e-firefox-compatibility.test.mjs b/scripts/e2e-firefox-compatibility.test.mjs index 2057491..6f45a19 100644 --- a/scripts/e2e-firefox-compatibility.test.mjs +++ b/scripts/e2e-firefox-compatibility.test.mjs @@ -59,8 +59,10 @@ test('e2e: Firefox compatibility report accepts supported fallback-based capabil assert.deepEqual(report.missing, []); assert.ok(report.limitations.some((item) => item.key === 'limitedInteractiveStdin')); assert.ok(report.limitations.some((item) => item.key === 'firefoxWorkspacePersistence')); - assert.ok(message.includes('pre-supplied buffered stdin')); - assert.ok(message.includes('Persistent folder write-back')); + assert.equal( + message, + 'Firefox support is still experimental. Brave, Chrome, Chromium, or Edge browsers are recommended.' + ); }); test('e2e: target manifest generation creates a Firefox manifest without Chromium-only background keys', () => { diff --git a/scripts/e2e-release-packaging.test.mjs b/scripts/e2e-release-packaging.test.mjs index f6482b7..1ba5959 100644 --- a/scripts/e2e-release-packaging.test.mjs +++ b/scripts/e2e-release-packaging.test.mjs @@ -207,7 +207,7 @@ test('e2e: clean release workspace removes stale dist and release outputs', () = assert.equal(fs.existsSync(path.join(repoRoot, 'release')), false); }); -test('e2e: release packaging creates all browser artifacts and metadata', () => { +test('e2e: release packaging creates Chromium-family browser artifacts and metadata', () => { const repoRoot = makeRepoFixture(); const result = createReleaseArtifacts({ repoRoot }); @@ -232,9 +232,9 @@ test('e2e: release packaging creates all browser artifacts and metadata', () => assert.equal(checksumLines.length, publishableTargets.length); const firefoxTarget = manifest.targets.find((target) => target.target === 'firefox'); - assert.equal(firefoxTarget.publishable, true); + assert.equal(firefoxTarget.publishable, false); assert.equal(firefoxTarget.packageStrategy, 'distinct'); - assert.equal(firefoxTarget.fileName, 'browser-cpp-firefox-v1.2.3.zip'); + assert.equal(firefoxTarget.fileName, null); assert.equal(firefoxTarget.signing.listed, 'manual-owner-submission'); assert.equal(firefoxTarget.signing.unlisted, 'required-release-artifact'); @@ -242,16 +242,14 @@ test('e2e: release packaging creates all browser artifacts and metadata', () => assert.equal(edgeArtifact.packageStrategy, 'shared-with:chrome'); assert.equal(edgeArtifact.payloadGroup, 'chromium-mv3'); - const firefoxArtifact = manifest.artifacts.find((artifact) => artifact.target === 'firefox'); - assert.equal(firefoxArtifact.packageStrategy, 'distinct'); - assert.equal(firefoxArtifact.payloadGroup, 'firefox-webext'); - assert.equal(firefoxArtifact.sharedPayload, false); - assert.equal(firefoxArtifact.format, 'zip'); - assert.equal(firefoxArtifact.sourceDir, 'dist-firefox'); + assert.equal( + manifest.artifacts.some((artifact) => artifact.target === 'firefox'), + false + ); for (const artifact of result.artifacts) { assert.equal(fs.existsSync(artifact.filePath), true); - assert.match(artifact.fileName, /^browser-cpp-(chrome|edge|firefox|brave|chromium)-v1\.2\.3\.zip$/); + assert.match(artifact.fileName, /^browser-cpp-(chrome|edge|brave|chromium)-v1\.2\.3\.zip$/); } }); @@ -277,13 +275,13 @@ test('e2e: release-target metadata includes Firefox and shared Chromium payloads const firefox = getReleaseTarget('firefox'); assert.equal(firefox.packageStrategy, 'distinct'); - assert.equal(firefox.publishable, true); + assert.equal(firefox.publishable, false); assert.equal(firefox.payloadGroup, 'firefox-webext'); const publishableTargets = getPublishableReleaseTargets(); assert.deepEqual( publishableTargets.map((target) => target.key), - ['chrome', 'edge', 'firefox', 'brave', 'chromium'] + ['chrome', 'edge', 'brave', 'chromium'] ); }); diff --git a/scripts/release-targets.js b/scripts/release-targets.js index 9b3e84d..c931518 100644 --- a/scripts/release-targets.js +++ b/scripts/release-targets.js @@ -25,7 +25,8 @@ const TARGETS = Object.freeze([ channel: 'Firefox Add-ons / signed XPI', packageStrategy: 'distinct', payloadGroup: 'firefox-webext', - publishable: true, + publishable: false, + blockReason: 'Firefox unsigned ZIP generation is disabled; use the signed unlisted XPI.', signing: { listed: 'manual-owner-submission', unlisted: 'required-release-artifact', diff --git a/src/ui/browser-capabilities.mjs b/src/ui/browser-capabilities.mjs index e874bdd..4fefa00 100644 --- a/src/ui/browser-capabilities.mjs +++ b/src/ui/browser-capabilities.mjs @@ -5,6 +5,8 @@ import { getExtensionAPI } from '../extension-api.mjs'; const MINIMUM_CHROMIUM_MAJOR = 105; const MINIMUM_FIREFOX_MAJOR = 140; export const FIREFOX_JSPI_STDIN_MINIMUM_MAJOR = 153; +export const FIREFOX_EXPERIMENTAL_WARNING = + 'Firefox support is still experimental. Brave, Chrome, Chromium, or Edge browsers are recommended.'; const BASE_REQUIRED_CAPABILITIES = [ { key: 'extensionRuntime', label: 'Extension runtime API' }, @@ -223,6 +225,10 @@ export function createBrowserCompatibilityReport(root = globalThis, workerCapabi } export function formatBrowserCompatibilityMessage(report) { + if (report.capabilities?.browserFamily === 'firefox') { + return FIREFOX_EXPERIMENTAL_WARNING; + } + const lines = []; if (report.missing.length) { lines.push('Browser compatibility warning: required capabilities are missing:'); From 3a49d645f4d19c749acacfb7320def0c596dcf34 Mon Sep 17 00:00:00 2001 From: Kevin Buffardi Date: Mon, 3 Aug 2026 12:29:05 -0700 Subject: [PATCH 2/4] chore: bump release version to 0.4.1 --- README.md | 10 ++++------ amo/metadata/listed.json | 2 +- docs/release-playbook.md | 6 +++--- manifest.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 12 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index f4f4b11..3d66b25 100644 --- a/README.md +++ b/README.md @@ -215,8 +215,7 @@ Full feature parity is supported for desktop Chrome, Edge, Brave, and Chromium when the browser is based on Chromium 105 or newer. Latest stable is recommended for release testing. -Firefox desktop is also a supported release target, but its support contract is -different: +Firefox desktop can load the extension, but support remains experimental: - compile/run, Monaco, and extension-runtime flows are supported - Firefox 153+ uses WebAssembly JSPI for live, line-buffered `std::cin`, @@ -348,7 +347,6 @@ This writes: - `release/browser-cpp-chrome-v.zip` for the Chrome Web Store listing - `release/browser-cpp-edge-v.zip` for Microsoft Edge Add-ons -- `release/browser-cpp-firefox-v.zip` for Firefox unsigned/manual submission packaging - `release/browser-cpp-brave-v.zip` for Brave validation/distribution - `release/browser-cpp-chromium-v.zip` for Chromium/GitHub distribution - `release/firefox-unlisted/*.xpi` after the protected release workflow signs the Firefox unlisted build @@ -359,7 +357,7 @@ The release manifest tracks the browser package matrix: - Chrome is the canonical Chromium-family payload - Edge, Brave, and Chromium currently reuse that payload under browser-labeled filenames -- Firefox has its own manifest, background entry, unsigned ZIP artifact, and signing metadata +- Firefox has its own manifest, background entry, smoke-tested temporary package, and signing metadata Chrome, Edge, Brave, and Chromium still share the same MV3 payload. Firefox is packaged from `dist-firefox/` as a separate payload because its manifest and @@ -385,7 +383,7 @@ Use `.github/workflows/release.yml` to publish one GitHub Release per 5. Fetches the Clang toolchain 6. Runs lint, build, release validation, and E2E checks 7. Runs Firefox packaging smoke validation -8. Produces the browser-labeled ZIPs plus checksums and release metadata +8. Produces the Chromium-family ZIPs plus checksums and release metadata 9. Signs the Firefox unlisted XPI with protected AMO credentials 10. Creates or updates GitHub Release `v` and uploads all files under `release/` @@ -458,7 +456,7 @@ manual/GitHub-distributed channel. 1. Run `npm run test:browser:firefox`. 2. Review `amo/metadata/listed.json` and update it if the release changes Firefox-facing product behavior or listing copy. -3. For public AMO publication, upload the Firefox package and metadata manually +3. For public AMO publication, build the Firefox package from `dist-firefox/`, then upload it with the metadata manually through the owner-managed listing workflow. 4. For self-distribution, verify that the protected release workflow produced a signed artifact under `release/firefox-unlisted/`. diff --git a/amo/metadata/listed.json b/amo/metadata/listed.json index f14e712..4991fc5 100644 --- a/amo/metadata/listed.json +++ b/amo/metadata/listed.json @@ -3,7 +3,7 @@ "ownerAction": "manual-submission", "summary": "Public AMO listing remains a manual owner-managed release step.", "artifacts": [ - "release/browser-cpp-firefox-v.zip" + "dist-firefox/ (package manually with web-ext for listed AMO submission)" ], "notes": [ "Review Firefox limitations around file and workspace persistence before submission.", diff --git a/docs/release-playbook.md b/docs/release-playbook.md index e6a4838..76c985a 100644 --- a/docs/release-playbook.md +++ b/docs/release-playbook.md @@ -22,7 +22,7 @@ ## Manual Release Flow - Review the uploaded artifacts and confirm the version bump is intentional. -- For Firefox, confirm the unsigned ZIP and the AMO/manual-submission metadata are present before owner handoff. +- For Firefox, confirm the Firefox build and AMO/manual-submission metadata are ready before owner handoff. - Use the existing tag/manual release workflow for final publication. - Publish browser store listings and verify installed updates as required by the target browser. - The protected release workflow signs the Firefox unlisted XPI with AMO credentials and uploads it with the other release assets. @@ -74,7 +74,7 @@ Passing these gates proves that: - Firefox-specific manifest generation succeeds - the Firefox extension package passes `web-ext` lint/build smoke -- release packaging emits the Firefox artifact and release manifest +- release packaging emits the Chromium-family artifacts and release manifest - manifest/package metadata stay version-synchronized ### Manual Firefox runtime QA @@ -97,7 +97,7 @@ real Firefox desktop build: persistence behavior matches reality. 8. Restart Firefox and verify session/workspace restore behavior matches the documented limitations. -9. Install the packaged Firefox ZIP/XPI and repeat the compile/run sanity check. +9. Install the signed Firefox XPI and repeat the compile/run sanity check. ### Release decision diff --git a/manifest.json b/manifest.json index 68946aa..a2e7871 100644 --- a/manifest.json +++ b/manifest.json @@ -3,7 +3,7 @@ "name": "browser.cpp", "short_name": "browser.cpp", "description": "In-browser C++20 IDE powered by Monaco Editor and WASM Clang", - "version": "0.4.0", + "version": "0.4.1", "minimum_chrome_version": "105", "icons": { "16": "icons/icon16.png", diff --git a/package-lock.json b/package-lock.json index dc9be4f..d0d023a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "browser.cpp", - "version": "0.4.0", + "version": "0.4.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "browser.cpp", - "version": "0.4.0", + "version": "0.4.1", "dependencies": { "@xterm/addon-fit": "^0.11.0", "@xterm/addon-web-links": "^0.12.0", diff --git a/package.json b/package.json index 8cfaf64..6fcea93 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "browser.cpp", - "version": "0.4.0", + "version": "0.4.1", "description": "In-browser C++20 IDE with WASM Clang toolchain", "private": true, "scripts": { From 4e8725548e60736f0414b919afb0117ace18a473 Mon Sep 17 00:00:00 2001 From: Kevin Buffardi Date: Mon, 3 Aug 2026 12:31:16 -0700 Subject: [PATCH 3/4] docs: clarify Firefox release distribution --- README.md | 4 ++-- scripts/e2e-release-packaging.test.mjs | 4 ++++ src/ui/browser-capabilities.mjs | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3d66b25..60cb0de 100644 --- a/README.md +++ b/README.md @@ -360,8 +360,8 @@ The release manifest tracks the browser package matrix: - Firefox has its own manifest, background entry, smoke-tested temporary package, and signing metadata Chrome, Edge, Brave, and Chromium still share the same MV3 payload. Firefox is -packaged from `dist-firefox/` as a separate payload because its manifest and -background model differ from Chromium. +built from `dist-firefox/` as a separate payload because its manifest and +background model differ from Chromium; release distribution uses the signed XPI. Store submission notes should state: diff --git a/scripts/e2e-release-packaging.test.mjs b/scripts/e2e-release-packaging.test.mjs index 1ba5959..69a45cf 100644 --- a/scripts/e2e-release-packaging.test.mjs +++ b/scripts/e2e-release-packaging.test.mjs @@ -235,6 +235,10 @@ test('e2e: release packaging creates Chromium-family browser artifacts and metad assert.equal(firefoxTarget.publishable, false); assert.equal(firefoxTarget.packageStrategy, 'distinct'); assert.equal(firefoxTarget.fileName, null); + assert.equal( + firefoxTarget.blockReason, + 'Firefox unsigned ZIP generation is disabled; use the signed unlisted XPI.' + ); assert.equal(firefoxTarget.signing.listed, 'manual-owner-submission'); assert.equal(firefoxTarget.signing.unlisted, 'required-release-artifact'); diff --git a/src/ui/browser-capabilities.mjs b/src/ui/browser-capabilities.mjs index 4fefa00..5ae2a26 100644 --- a/src/ui/browser-capabilities.mjs +++ b/src/ui/browser-capabilities.mjs @@ -5,7 +5,7 @@ import { getExtensionAPI } from '../extension-api.mjs'; const MINIMUM_CHROMIUM_MAJOR = 105; const MINIMUM_FIREFOX_MAJOR = 140; export const FIREFOX_JSPI_STDIN_MINIMUM_MAJOR = 153; -export const FIREFOX_EXPERIMENTAL_WARNING = +const FIREFOX_EXPERIMENTAL_WARNING = 'Firefox support is still experimental. Brave, Chrome, Chromium, or Edge browsers are recommended.'; const BASE_REQUIRED_CAPABILITIES = [ From 50b045a42a5931151ddb338b1c9ec8742541ed8f Mon Sep 17 00:00:00 2001 From: Kevin Buffardi Date: Mon, 3 Aug 2026 12:33:00 -0700 Subject: [PATCH 4/4] fix: remove stale Firefox release ZIPs --- scripts/e2e-release-packaging.test.mjs | 17 +++++++++++++++++ scripts/package-extension-release.js | 11 +++++++++++ 2 files changed, 28 insertions(+) diff --git a/scripts/e2e-release-packaging.test.mjs b/scripts/e2e-release-packaging.test.mjs index 69a45cf..0995950 100644 --- a/scripts/e2e-release-packaging.test.mjs +++ b/scripts/e2e-release-packaging.test.mjs @@ -207,6 +207,23 @@ test('e2e: clean release workspace removes stale dist and release outputs', () = assert.equal(fs.existsSync(path.join(repoRoot, 'release')), false); }); +test('e2e: release packaging removes stale Firefox ZIP artifacts', () => { + const repoRoot = makeRepoFixture(); + fs.mkdirSync(path.join(repoRoot, 'release'), { recursive: true }); + fs.writeFileSync( + path.join(repoRoot, 'release', 'browser-cpp-firefox-v0.3.0.zip'), + 'stale Firefox artifact\n', + 'utf8' + ); + + createReleaseArtifacts({ repoRoot }); + + assert.equal( + fs.existsSync(path.join(repoRoot, 'release', 'browser-cpp-firefox-v0.3.0.zip')), + false + ); +}); + test('e2e: release packaging creates Chromium-family browser artifacts and metadata', () => { const repoRoot = makeRepoFixture(); const result = createReleaseArtifacts({ repoRoot }); diff --git a/scripts/package-extension-release.js b/scripts/package-extension-release.js index 56c0a46..296d01c 100644 --- a/scripts/package-extension-release.js +++ b/scripts/package-extension-release.js @@ -162,6 +162,16 @@ function getCommitSha(repoRoot) { return null; } +function removeStaleFirefoxZipArtifacts(releaseDir) { + if (!fs.existsSync(releaseDir)) return; + + for (const name of fs.readdirSync(releaseDir)) { + if (!/^browser-cpp-firefox-v.+\.zip$/.test(name)) continue; + const filePath = path.join(releaseDir, name); + if (fs.statSync(filePath).isFile()) fs.unlinkSync(filePath); + } +} + function createReleaseArtifacts(options = {}) { const repoRoot = options.repoRoot || path.resolve(__dirname, '..'); const distDir = options.distDir || path.join(repoRoot, 'dist'); @@ -180,6 +190,7 @@ function createReleaseArtifacts(options = {}) { ]); fs.mkdirSync(releaseDir, { recursive: true }); + removeStaleFirefoxZipArtifacts(releaseDir); const artifacts = publishableTargets.map((target) => { const payloadGroup = target.payloadGroup;