From adda7c89c8c77ed43bd732169583e6d1ef65eaac Mon Sep 17 00:00:00 2001 From: Anthony Barone Date: Mon, 2 Mar 2026 15:41:31 +0000 Subject: [PATCH 1/2] Update source map upload command options - Remove `telemetryServerUrl` option - Change mapping file positional argument from required to optional (defaults to root dir) --- .../crashlytics-sourcemap-upload.spec.ts | 34 +------------- src/commands/crashlytics-sourcemap-upload.ts | 44 ++++++------------- 2 files changed, 16 insertions(+), 62 deletions(-) diff --git a/src/commands/crashlytics-sourcemap-upload.spec.ts b/src/commands/crashlytics-sourcemap-upload.spec.ts index bf4bb186a59..fd322bb054f 100644 --- a/src/commands/crashlytics-sourcemap-upload.spec.ts +++ b/src/commands/crashlytics-sourcemap-upload.spec.ts @@ -17,7 +17,6 @@ const PROJECT_NUMBER = "12345"; const BUCKET_NAME = "test-bucket"; const DIR_PATH = "src/test/fixtures/mapping-files"; const FILE_PATH = "src/test/fixtures/mapping-files/mock_mapping.js.map"; -const TELEMETRY_SERVER_URL = "https://telemetry-api.com"; describe("crashlytics:sourcemap:upload", () => { let sandbox: sinon.SinonSandbox; @@ -69,17 +68,9 @@ describe("crashlytics:sourcemap:upload", () => { ); }); - it("should throw an error if no telemetry server URL is provided", async () => { - await expect(command.runner()("filename", { app: "test-app" })).to.be.rejectedWith( - FirebaseError, - "set --telemetry-server-url to a valid Firebase Telemetry server URL", - ); - }); - it("should create the default cloud storage bucket", async () => { await command.runner()(FILE_PATH, { app: "test-app", - telemetryServerUrl: TELEMETRY_SERVER_URL, }); expect(gcsMock.upsertBucket).to.be.calledOnce; const args = gcsMock.upsertBucket.firstCall.args; @@ -91,7 +82,6 @@ describe("crashlytics:sourcemap:upload", () => { const options = { app: "test-app", bucketLocation: "a-different-LoCaTiOn", - telemetryServerUrl: TELEMETRY_SERVER_URL, }; await command.runner()(FILE_PATH, options); expect(gcsMock.upsertBucket).to.be.calledOnce; @@ -106,7 +96,6 @@ describe("crashlytics:sourcemap:upload", () => { expect( command.runner()("invalid/path", { app: "test-app", - telemetryServerUrl: TELEMETRY_SERVER_URL, }), ).to.be.rejectedWith(FirebaseError, "provide a valid file path or directory"); }); @@ -114,7 +103,6 @@ describe("crashlytics:sourcemap:upload", () => { it("should upload a single mapping file", async () => { await command.runner()(FILE_PATH, { app: "test-app", - telemetryServerUrl: TELEMETRY_SERVER_URL, }); expect(gcsMock.uploadObject).to.be.calledOnce; expect(gcsMock.uploadObject).to.be.calledWith(sinon.match.any, BUCKET_NAME); @@ -124,7 +112,7 @@ describe("crashlytics:sourcemap:upload", () => { }); it("should find and upload mapping files in a directory", async () => { - await command.runner()(DIR_PATH, { app: "test-app", telemetryServerUrl: TELEMETRY_SERVER_URL }); + await command.runner()(DIR_PATH, { app: "test-app" }); expect(gcsMock.uploadObject).to.be.calledTwice; const uploadedFiles = gcsMock.uploadObject .getCalls() @@ -142,7 +130,6 @@ describe("crashlytics:sourcemap:upload", () => { await command.runner()(FILE_PATH, { app: "test-app", appVersion: "1.0.0", - telemetryServerUrl: TELEMETRY_SERVER_URL, }); expect(gcsMock.uploadObject.firstCall.args[0].file).to.eq( "test-app-1.0.0-src-test-fixtures-mapping-files-mock_mapping.js.map.zip", @@ -152,7 +139,6 @@ describe("crashlytics:sourcemap:upload", () => { it("should fall back to the git commit for app version", async () => { await command.runner()(FILE_PATH, { app: "test-app", - telemetryServerUrl: TELEMETRY_SERVER_URL, }); expect(gcsMock.uploadObject.firstCall.args[0].file).to.match( /test-app-a{40}-src-test-fixtures-mapping-files-mock_mapping.js.map.zip/, @@ -167,7 +153,6 @@ describe("crashlytics:sourcemap:upload", () => { await command.runner()(FILE_PATH, { app: "test-app", - telemetryServerUrl: TELEMETRY_SERVER_URL, }); expect(gcsMock.uploadObject.firstCall.args[0].file).to.eq( "test-app-1.2.3-src-test-fixtures-mapping-files-mock_mapping.js.map.zip", @@ -180,7 +165,6 @@ describe("crashlytics:sourcemap:upload", () => { await command.runner()(FILE_PATH, { app: "test-app", - telemetryServerUrl: TELEMETRY_SERVER_URL, }); expect(gcsMock.uploadObject.firstCall.args[0].file).to.eq( "test-app-unset-src-test-fixtures-mapping-files-mock_mapping.js.map.zip", @@ -190,7 +174,6 @@ describe("crashlytics:sourcemap:upload", () => { it("should register the source map after upload", async () => { await command.runner()(FILE_PATH, { app: "test-app", - telemetryServerUrl: TELEMETRY_SERVER_URL, }); expect(clientPostStub).to.be.calledOnce; const args = clientPostStub.firstCall.args; @@ -210,7 +193,6 @@ describe("crashlytics:sourcemap:upload", () => { clientPostStub.rejects(new Error("Registration failed")); await command.runner()(FILE_PATH, { app: "test-app", - telemetryServerUrl: TELEMETRY_SERVER_URL, }); expect(clientPostStub).to.be.calledOnce; expect(logLabeledWarningStub).to.be.calledOnceWith( @@ -219,21 +201,9 @@ describe("crashlytics:sourcemap:upload", () => { ); }); - it("should use the provided telemetry server URL", async () => { - const customServerUrl = "https://custom-server.com"; - clientPostStub.restore(); - clientPostStub = sandbox.stub(Client.prototype, "post").callsFake(function (this: any) { - expect(this["opts"].urlPrefix).to.equal(customServerUrl); - return Promise.resolve({ status: 200, response: {} as any, body: {} }); - }); - - await command.runner()(FILE_PATH, { app: "test-app", telemetryServerUrl: customServerUrl }); - expect(clientPostStub).to.be.calledOnce; - }); - it("should log failed files", async () => { clientPostStub.rejects(new Error("Registration failed")); - await command.runner()(DIR_PATH, { app: "test-app", telemetryServerUrl: TELEMETRY_SERVER_URL }); + await command.runner()(DIR_PATH, { app: "test-app" }); // Should verify that logLabeledBullet is called with the specific failed files expect(logLabeledBulletStub).to.be.calledWith( diff --git a/src/commands/crashlytics-sourcemap-upload.ts b/src/commands/crashlytics-sourcemap-upload.ts index 84b00ad980f..edb96c0fe16 100644 --- a/src/commands/crashlytics-sourcemap-upload.ts +++ b/src/commands/crashlytics-sourcemap-upload.ts @@ -19,7 +19,6 @@ interface CommandOptions extends Options { app?: string; bucketLocation?: string; appVersion?: string; - telemetryServerUrl?: string; } interface SourceMap { @@ -29,7 +28,7 @@ interface SourceMap { fileUri: string; } -export const command = new Command("crashlytics:sourcemap:upload ") +export const command = new Command("crashlytics:sourcemap:upload [mappingFiles]") .description("upload javascript source maps to de-minify stack traces") .option("--app ", "the app id of your Firebase app") .option( @@ -40,10 +39,8 @@ export const command = new Command("crashlytics:sourcemap:upload " "--app-version ", "the version of your Firebase app (defaults to Git commit hash, if available)", ) - .option("--telemetry-server-url ", "the url of the telemetry API") - .action(async (mappingFiles: string, options: CommandOptions) => { + .action(async (mappingFiles: string | undefined, options: CommandOptions) => { checkGoogleAppID(options); - checkTelemetryServerUrl(options); // App version const appVersion = getAppVersion(options); @@ -57,7 +54,7 @@ export const command = new Command("crashlytics:sourcemap:upload " // Find and upload mapping files const rootDir = options.projectRoot ?? process.cwd(); - const filePath = path.relative(rootDir, mappingFiles); + const filePath = path.relative(rootDir, mappingFiles || ""); let fstat: fs.Stats; try { fstat = statSync(filePath); @@ -69,11 +66,11 @@ export const command = new Command("crashlytics:sourcemap:upload " let successCount = 0; const failedFiles: string[] = []; if (fstat.isFile()) { - const success = await uploadMap(projectId, mappingFiles, bucketName, appVersion, options); + const success = await uploadMap(projectId, filePath, bucketName, appVersion, options); if (success) { successCount++; } else { - failedFiles.push(mappingFiles); + failedFiles.push(filePath); } } else if (fstat.isDirectory()) { logLabeledBullet("crashlytics", "Looking for mapping files in your directory..."); @@ -116,12 +113,6 @@ function checkGoogleAppID(options: CommandOptions): void { } } -function checkTelemetryServerUrl(options: CommandOptions): void { - if (!options.telemetryServerUrl) { - throw new FirebaseError("set --telemetry-server-url to a valid Firebase Telemetry server URL"); - } -} - function getAppVersion(options: CommandOptions): string { if (options.appVersion) { return options.appVersion; @@ -204,16 +195,12 @@ async function uploadMap( const fileUri = `gs://${bucket}/${object}`; logger.debug(`Uploaded mapping file ${mappingFile} to ${fileUri}`); - await registerSourceMap( - parent, - { - name, - version: appVersion, - obfuscatedFilePath: filePath, - fileUri, - }, - options.telemetryServerUrl!, - ); + await registerSourceMap(parent, { + name, + version: appVersion, + obfuscatedFilePath: filePath, + fileUri, + }); return true; } catch (e) { @@ -226,13 +213,10 @@ function normalizeFileName(fileName: string): string { return fileName.replaceAll(/\//g, "-"); } -async function registerSourceMap( - parent: string, - sourceMap: SourceMap, - telemetryServerUrl: string, -): Promise { +async function registerSourceMap(parent: string, sourceMap: SourceMap): Promise { const client = new Client({ - urlPrefix: telemetryServerUrl, + // TODO(tonybaroneee): use the real telemetry server url when ready + urlPrefix: "http://localhost", auth: true, apiVersion: "v1", }); From da5a568d67abf8b99ae96f238db233afb688b6be Mon Sep 17 00:00:00 2001 From: Anthony Barone Date: Mon, 2 Mar 2026 19:42:07 +0000 Subject: [PATCH 2/2] Fix mapping path fallback and add test --- src/commands/crashlytics-sourcemap-upload.spec.ts | 15 +++++++++++++++ src/commands/crashlytics-sourcemap-upload.ts | 9 ++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/commands/crashlytics-sourcemap-upload.spec.ts b/src/commands/crashlytics-sourcemap-upload.spec.ts index fd322bb054f..40e338f446d 100644 --- a/src/commands/crashlytics-sourcemap-upload.spec.ts +++ b/src/commands/crashlytics-sourcemap-upload.spec.ts @@ -126,6 +126,21 @@ describe("crashlytics:sourcemap:upload", () => { ); }); + it("should find and upload mapping files in the current directory if no path is provided", async () => { + await command.runner()(undefined, { app: "test-app" }); + expect(gcsMock.uploadObject).to.be.calledTwice; + const uploadedFiles = gcsMock.uploadObject + .getCalls() + .map((call) => call.args[0].file) + .sort(); + expect(uploadedFiles[0]).to.match( + /test-app-.*-src-test-fixtures-mapping-files-mock_mapping\.js\.map\.zip/, + ); + expect(uploadedFiles[1]).to.match( + /test-app-.*-src-test-fixtures-mapping-files-subdir-subdir_mock_mapping\.js\.map\.zip/, + ); + }); + it("should use the provided app version", async () => { await command.runner()(FILE_PATH, { app: "test-app", diff --git a/src/commands/crashlytics-sourcemap-upload.ts b/src/commands/crashlytics-sourcemap-upload.ts index edb96c0fe16..5fcddb2f2e5 100644 --- a/src/commands/crashlytics-sourcemap-upload.ts +++ b/src/commands/crashlytics-sourcemap-upload.ts @@ -54,7 +54,7 @@ export const command = new Command("crashlytics:sourcemap:upload [mappingFiles]" // Find and upload mapping files const rootDir = options.projectRoot ?? process.cwd(); - const filePath = path.relative(rootDir, mappingFiles || ""); + const filePath = path.relative(rootDir, mappingFiles || ".") || "."; let fstat: fs.Stats; try { fstat = statSync(filePath); @@ -172,13 +172,12 @@ async function upsertBucket( async function uploadMap( projectId: string, - mappingFile: string, + filePath: string, bucketName: string, appVersion: string, options: CommandOptions, ): Promise { try { - const filePath = path.relative(options.projectRoot ?? process.cwd(), mappingFile); const tmpArchive = await archiveFile(filePath, { archivedFileName: "mapping.js.map" }); const gcsFile = `${options.app}-${appVersion}-${normalizeFileName(filePath)}.zip`; const uid = murmurHashV3(`${appVersion}-${filePath}`); @@ -193,7 +192,7 @@ async function uploadMap( bucketName, ); const fileUri = `gs://${bucket}/${object}`; - logger.debug(`Uploaded mapping file ${mappingFile} to ${fileUri}`); + logger.debug(`Uploaded mapping file ${filePath} to ${fileUri}`); await registerSourceMap(parent, { name, @@ -204,7 +203,7 @@ async function uploadMap( return true; } catch (e) { - logLabeledWarning("crashlytics", `Failed to upload mapping file ${mappingFile}:\n${e}`); + logLabeledWarning("crashlytics", `Failed to upload mapping file ${filePath}:\n${e}`); return false; } }