From f4c58eefdf58648081ce4f3400c229fc90601f8e Mon Sep 17 00:00:00 2001 From: aalej Date: Sat, 21 Feb 2026 18:51:40 +0800 Subject: [PATCH 1/6] add FIREBASE_TOOLS_*_EMULATOR_VERSION to allow pinning emulator version --- CHANGELOG.md | 1 + src/emulator/download.ts | 7 ++++ src/emulator/downloadableEmulators.ts | 51 +++++++++++++++++++++++---- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb2d..ab1843b3aee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Added `FIREBASE_TOOLS_*_EMULATOR_VERSION` env variables to allow overriding specific versions of downloadable emulators diff --git a/src/emulator/download.ts b/src/emulator/download.ts index 76bd0a26008..5bb12280546 100644 --- a/src/emulator/download.ts +++ b/src/emulator/download.ts @@ -22,6 +22,13 @@ export async function downloadEmulator(name: DownloadableEmulators): Promise s.split(oldVersion).join(overrideVersion); + + details.version = overrideVersion; + details.downloadPath = replaceVersion(details.downloadPath); + if (details.unzipDir) { + details.unzipDir = replaceVersion(details.unzipDir); + } + if (details.binaryPath) { + details.binaryPath = replaceVersion(details.binaryPath); + } + + details.opts.remoteUrl = replaceVersion(details.opts.remoteUrl); + details.opts.skipChecksumAndSize = true; + } + + return details; } const EmulatorDetails: { [s in DownloadableEmulators]: DownloadableEmulatorDetails } = { @@ -631,3 +666,7 @@ export function isIncomaptibleArchError(err: unknown): boolean { process.platform === "darwin" ); } + +export function isVersionOverride(name: DownloadableEmulators) { + return !!process.env[EMULATOR_VERSION_OVERRIDE_ENV_MAP[name]]; +} From 6288e4afb5ba9575c4b1d2caa2d730f40445abce Mon Sep 17 00:00:00 2001 From: aalej Date: Sat, 21 Feb 2026 19:22:16 +0800 Subject: [PATCH 2/6] add test --- src/emulator/downloadableEmulators.spec.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/emulator/downloadableEmulators.spec.ts b/src/emulator/downloadableEmulators.spec.ts index d01da60d34c..6092f02cef8 100644 --- a/src/emulator/downloadableEmulators.spec.ts +++ b/src/emulator/downloadableEmulators.spec.ts @@ -83,4 +83,22 @@ describe("downloadDetails", () => { emulatorUpdateDetails.dataconnect.darwin_arm64.remoteUrl, ); }); + + it("should override emulator version when FIREBASE_TOOLS_PUBSUB_EMULATOR_VERSION is set", () => { + const fakeVersion = "1.2.3"; + sandbox.stub(process, "env").value({ + ...process.env, + FIREBASE_TOOLS_PUBSUB_EMULATOR_VERSION: fakeVersion, + }); + + const pubsubEmulatorDetails = downloadableEmulators.getDownloadDetails(Emulators.PUBSUB); + expect(pubsubEmulatorDetails.version).to.equal(fakeVersion); + expect(pubsubEmulatorDetails.downloadPath).to.contain(fakeVersion); + expect(pubsubEmulatorDetails.opts.remoteUrl).to.contain(fakeVersion); + expect(pubsubEmulatorDetails.opts.skipChecksumAndSize).to.be.true; + + expect(downloadableEmulators.isVersionOverride(Emulators.FIRESTORE)).to.be.false; + expect(downloadableEmulators.isVersionOverride(Emulators.DATABASE)).to.be.false; + expect(downloadableEmulators.isVersionOverride(Emulators.PUBSUB)).to.be.true; + }); }); From db6cece4fbe3581fa64a3e93e211056f56cde365 Mon Sep 17 00:00:00 2001 From: aalej Date: Sat, 21 Feb 2026 19:49:38 +0800 Subject: [PATCH 3/6] fix lint issue --- src/emulator/downloadableEmulators.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emulator/downloadableEmulators.ts b/src/emulator/downloadableEmulators.ts index 1c9174bdf32..24edf1ff637 100755 --- a/src/emulator/downloadableEmulators.ts +++ b/src/emulator/downloadableEmulators.ts @@ -75,7 +75,7 @@ function generateDownloadDetails(emulator: DownloadableEmulators): EmulatorDownl : EMULATOR_UPDATE_DETAILS.dataconnect.linux; let details: EmulatorDownloadDetails; - let overrideVersion = process.env[EMULATOR_VERSION_OVERRIDE_ENV_MAP[emulator]]; + const overrideVersion = process.env[EMULATOR_VERSION_OVERRIDE_ENV_MAP[emulator]]; switch (emulator) { case "database": details = { From 4a1d7b3df394573a6e78299a5dade4a27b28234c Mon Sep 17 00:00:00 2001 From: aalej Date: Tue, 24 Feb 2026 18:29:53 +0800 Subject: [PATCH 4/6] change FIREBASE_TOOLS_*_EMULATOR_VERSION to *_EMULATOR_VERSION --- .mocharc.yml | 2 +- CHANGELOG.md | 2 +- src/emulator/download.ts | 5 +++-- src/emulator/downloadableEmulators.spec.ts | 10 +++++----- src/emulator/downloadableEmulators.ts | 14 +++----------- 5 files changed, 13 insertions(+), 20 deletions(-) diff --git a/.mocharc.yml b/.mocharc.yml index 0e19f7e877f..c51e774c9dc 100644 --- a/.mocharc.yml +++ b/.mocharc.yml @@ -4,7 +4,7 @@ require: - src/test/helpers/mocha-bootstrap.ts file: - src/test/helpers/global-mock-auth.ts -timeout: 1000 +timeout: 10000 recursive: true node-options: - no-experimental-strip-types diff --git a/CHANGELOG.md b/CHANGELOG.md index 60a55b3265a..73586690e97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,2 @@ -- Added `FIREBASE_TOOLS_*_EMULATOR_VERSION` env variables to allow overriding specific versions of downloadable emulators +- Added `*_EMULATOR_VERSION` env variables to allow overriding specific versions of downloadable emulators - Updated suite of MCP tools for Firestore to include many new tools. Firestore tools no longer support emulator mode. diff --git a/src/emulator/download.ts b/src/emulator/download.ts index 5bb12280546..a6fecad00e9 100644 --- a/src/emulator/download.ts +++ b/src/emulator/download.ts @@ -22,11 +22,12 @@ export async function downloadEmulator(name: DownloadableEmulators): Promise { ); }); - it("should override emulator version when FIREBASE_TOOLS_PUBSUB_EMULATOR_VERSION is set", () => { + it("should override emulator version when PUBSUB_EMULATOR_VERSION is set", () => { const fakeVersion = "1.2.3"; sandbox.stub(process, "env").value({ ...process.env, - FIREBASE_TOOLS_PUBSUB_EMULATOR_VERSION: fakeVersion, + PUBSUB_EMULATOR_VERSION: fakeVersion, }); const pubsubEmulatorDetails = downloadableEmulators.getDownloadDetails(Emulators.PUBSUB); @@ -97,8 +97,8 @@ describe("downloadDetails", () => { expect(pubsubEmulatorDetails.opts.remoteUrl).to.contain(fakeVersion); expect(pubsubEmulatorDetails.opts.skipChecksumAndSize).to.be.true; - expect(downloadableEmulators.isVersionOverride(Emulators.FIRESTORE)).to.be.false; - expect(downloadableEmulators.isVersionOverride(Emulators.DATABASE)).to.be.false; - expect(downloadableEmulators.isVersionOverride(Emulators.PUBSUB)).to.be.true; + expect(downloadableEmulators.emulatorVersionOverride(Emulators.FIRESTORE)).to.be.undefined; + expect(downloadableEmulators.emulatorVersionOverride(Emulators.DATABASE)).to.be.undefined; + expect(downloadableEmulators.emulatorVersionOverride(Emulators.PUBSUB)).to.equal(fakeVersion); }); }); diff --git a/src/emulator/downloadableEmulators.ts b/src/emulator/downloadableEmulators.ts index 24edf1ff637..d459e4b8b8d 100755 --- a/src/emulator/downloadableEmulators.ts +++ b/src/emulator/downloadableEmulators.ts @@ -26,14 +26,6 @@ import * as emulatorUpdateDetails from "./downloadableEmulatorInfo.json"; const EMULATOR_INSTANCE_KILL_TIMEOUT = 4000; /* ms */ -const EMULATOR_VERSION_OVERRIDE_ENV_MAP: { [key: string]: string } = { - database: "FIREBASE_TOOLS_DATABASE_EMULATOR_VERSION", - firestore: "FIREBASE_TOOLS_FIRESTORE_EMULATOR_VERSION", - storage: "FIREBASE_TOOLS_STORAGE_EMULATOR_VERSION", - pubsub: "FIREBASE_TOOLS_PUBSUB_EMULATOR_VERSION", - dataconnect: "FIREBASE_TOOLS_DATACONNECT_EMULATOR_VERSION", -}; - const CACHE_DIR = process.env.FIREBASE_EMULATORS_PATH || path.join(os.homedir(), ".cache", "firebase", "emulators"); @@ -75,7 +67,7 @@ function generateDownloadDetails(emulator: DownloadableEmulators): EmulatorDownl : EMULATOR_UPDATE_DETAILS.dataconnect.linux; let details: EmulatorDownloadDetails; - const overrideVersion = process.env[EMULATOR_VERSION_OVERRIDE_ENV_MAP[emulator]]; + const overrideVersion = emulatorVersionOverride(emulator); switch (emulator) { case "database": details = { @@ -667,6 +659,6 @@ export function isIncomaptibleArchError(err: unknown): boolean { ); } -export function isVersionOverride(name: DownloadableEmulators) { - return !!process.env[EMULATOR_VERSION_OVERRIDE_ENV_MAP[name]]; +export function emulatorVersionOverride(emulator: DownloadableEmulators) { + return process.env[`${emulator.toUpperCase()}_EMULATOR_VERSION`]; } From 17ea141a3638bfce8e69f3957c2791d38722c54d Mon Sep 17 00:00:00 2001 From: aalej Date: Tue, 24 Feb 2026 18:30:13 +0800 Subject: [PATCH 5/6] revert .mocharc.yml change --- .mocharc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mocharc.yml b/.mocharc.yml index c51e774c9dc..0e19f7e877f 100644 --- a/.mocharc.yml +++ b/.mocharc.yml @@ -4,7 +4,7 @@ require: - src/test/helpers/mocha-bootstrap.ts file: - src/test/helpers/global-mock-auth.ts -timeout: 10000 +timeout: 1000 recursive: true node-options: - no-experimental-strip-types From 9bbca9f2a97c0f6bd0c35b6f4e36951101c1a568 Mon Sep 17 00:00:00 2001 From: aalej Date: Tue, 24 Feb 2026 20:02:52 +0800 Subject: [PATCH 6/6] better error message for invalid versions --- src/downloadUtils.ts | 4 +++- src/emulator/download.ts | 13 ++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/downloadUtils.ts b/src/downloadUtils.ts index 99e63eded9d..9e311108785 100644 --- a/src/downloadUtils.ts +++ b/src/downloadUtils.ts @@ -27,7 +27,9 @@ export async function downloadToTmp(remoteUrl: string, auth: boolean = false): P resolveOnHTTPError: true, }); if (res.status !== 200) { - throw new FirebaseError(`download failed, status ${res.status}: ${await res.response.text()}`); + throw new FirebaseError(`download failed, status ${res.status}: ${await res.response.text()}`, { + status: res.status, + }); } const total = parseInt(res.response.headers.get("content-length") || "0", 10); diff --git a/src/emulator/download.ts b/src/emulator/download.ts index a6fecad00e9..062f827a930 100644 --- a/src/emulator/download.ts +++ b/src/emulator/download.ts @@ -37,7 +37,18 @@ export async function downloadEmulator(name: DownloadableEmulators): Promise