diff --git a/package-lock.json b/package-lock.json index 3d2c864..cd88ba4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "firstline": "^2.0.2", "glob": "^10.3.10", "macho-uuid": "^1.3.2", - "pdb-guid": "^1.0.7", + "pdb-guid": "^2.1.1", "pretty-bytes": "^5.6.0", "promise-retry": "^2.0.1", "rxjs": "^7.8.1", @@ -1082,7 +1082,6 @@ "integrity": "sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.19.2" } @@ -1221,7 +1220,6 @@ "integrity": "sha512-aIFPci9xoTmVkxpqsSKcRG/Hn0lTy421jsCehHydYeIMd+getn0Pue0JqY5cW8yZglZjMeX0YfIy5wDtQDHEcA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/utils": "4.0.7", "fflate": "^0.8.2", @@ -4104,9 +4102,9 @@ "license": "MIT" }, "node_modules/pdb-guid": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/pdb-guid/-/pdb-guid-1.0.7.tgz", - "integrity": "sha512-aDF2UpdiTVUmoB2NAmCNtt7yIi4EMT0A5+oYzLKVslU2OpDyC/X6y4MFqEdeGJ4nLt81/GANLNl0UpJCW7W9wQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pdb-guid/-/pdb-guid-2.1.1.tgz", + "integrity": "sha512-wp3FW9AfL1+doMJpQkWH/Lv/hNIYDwnJW44zwM9ccDJfpQRL7yB/xslN3RUQgS9x/fkMZu4TdcLB2K01StAt8g==", "license": "MIT", "dependencies": { "command-line-args": "^5.2.1", @@ -4114,27 +4112,30 @@ }, "bin": { "pdb-guid": "dist/bin/index.js" + }, + "engines": { + "node": ">=20" } }, "node_modules/pdb-guid/node_modules/array-back": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", - "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.3.tgz", + "integrity": "sha512-SGDvmg6QTYiTxCBkYVmThcoa67uLl35pyzRHdpCGBOcqFy6BtwnphoFPk7LhJshD+Yk1Kt35WGWeZPTgwR4Fhw==", "license": "MIT", "engines": { "node": ">=12.17" } }, "node_modules/pdb-guid/node_modules/command-line-usage": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.3.tgz", - "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.4.tgz", + "integrity": "sha512-85UdvzTNx/+s5CkSgBm/0hzP80RFHAa7PsfeADE5ezZF3uHz3/Tqj9gIKGT9PTtpycc3Ua64T0oVulGfKxzfqg==", "license": "MIT", "dependencies": { "array-back": "^6.2.2", "chalk-template": "^0.4.0", - "table-layout": "^4.1.0", - "typical": "^7.1.1" + "table-layout": "^4.1.1", + "typical": "^7.3.0" }, "engines": { "node": ">=12.20.0" @@ -4154,18 +4155,18 @@ } }, "node_modules/pdb-guid/node_modules/typical": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/typical/-/typical-7.1.1.tgz", - "integrity": "sha512-T+tKVNs6Wu7IWiAce5BgMd7OZfNYUndHwc5MknN+UHOudi7sGZzuHdCadllRuqJ3fPtgFtIH9+lt9qRv6lmpfA==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz", + "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", "license": "MIT", "engines": { "node": ">=12.17" } }, "node_modules/pdb-guid/node_modules/wordwrapjs": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.0.tgz", - "integrity": "sha512-JNjcULU2e4KJwUNv6CHgI46UvDGitb6dGryHajXTDiLgg1/RiGoPSDw4kZfYnwGtEXf2ZMeIewDQgFGzkCB2Sg==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.1.tgz", + "integrity": "sha512-0yweIbkINJodk27gX9LBGMzyQdBDan3s/dEAiwBOj+Mf0PPyWL6/rikalkv8EeD0E8jm4o5RXEOrFTP3NXbhJg==", "license": "MIT", "engines": { "node": ">=12.17" @@ -4191,7 +4192,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -5354,7 +5354,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5435,7 +5434,6 @@ "integrity": "sha512-xQroKAadK503CrmbzCISvQUjeuvEZzv6U0wlnlVFOi5i3gnzfH4onyQ29f3lzpe0FresAiTAd3aqK0Bi/jLI8w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/expect": "4.0.7", "@vitest/mocker": "4.0.7", @@ -5541,7 +5539,6 @@ "integrity": "sha512-qTl3VF7BvOupTR85Zc561sPEgxyUSNSvTQ9fit7DEMP7yPgvvIGm5Zfa1dOM+kOwWGNviK9uFM9ra77+OjK7lQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", diff --git a/package.json b/package.json index 8b8c443..cc0e503 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "firstline": "^2.0.2", "glob": "^10.3.10", "macho-uuid": "^1.3.2", - "pdb-guid": "^1.0.7", + "pdb-guid": "^2.1.1", "pretty-bytes": "^5.6.0", "promise-retry": "^2.0.1", "rxjs": "^7.8.1", diff --git a/spec/guid.spec.ts b/spec/guid.spec.ts new file mode 100644 index 0000000..af1ad1f --- /dev/null +++ b/spec/guid.spec.ts @@ -0,0 +1,19 @@ +import { tryGetGuid } from '../src/guid'; + +describe('tryGetGuid', () => { + it('should return guid for c++ pdb', async () => { + await expect(tryGetGuid('spec/support/bugsplat.pdb')).resolves.toBe('E546B55B6D214E86871B40AC35CD0D461'); + }); + + it('should return guid for portable pdb', async () => { + await expect(tryGetGuid('spec/support/portable.pdb')).resolves.toBe('153A24FA52FF4C03813A890A535486B81'); + }); + + it('should return guid for c++ exe', async () => { + await expect(tryGetGuid('spec/support/bssndrpt.exe')).resolves.toBe('64FB82D565000'); + }); + + it('should return empty guid for unrecognized file', async () => { + await expect(tryGetGuid('spec/support/corrupt.exe')).resolves.toBe(''); + }); +}); diff --git a/spec/pdb.spec.ts b/spec/pdb.spec.ts deleted file mode 100644 index 7ef2b92..0000000 --- a/spec/pdb.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { tryGetPdbGuid, tryGetPeGuid } from '../src/pdb'; - -describe('pdb', () => { - describe('tryGetPdbGuid', () => { - it('should return guid for c++ pdb', async () => { - await expect(tryGetPdbGuid('spec/support/bugsplat.pdb')).resolves.toBe('E546B55B6D214E86871B40AC35CD0D461'); - }); - - it('should return empty guid for unrecognized pdb', async () => { - await expect(tryGetPdbGuid('spec/support/portable.pdb')).resolves.toBe(''); - }); - }); - - describe('tryGetPeGuid', () => { - it('should return guid for c++ exe', async () => { - await expect(tryGetPeGuid('spec/support/bssndrpt.exe')).resolves.toBe('64FB82D565000'); - }); - - it('should return empty guid for unrecognized pe file', async () => { - await expect(tryGetPeGuid('spec/support/corrupt.exe')).resolves.toBe(''); - }); - }); -}); \ No newline at end of file diff --git a/spec/worker.spec.ts b/spec/worker.spec.ts index dcfa0bf..2f09df4 100644 --- a/spec/worker.spec.ts +++ b/spec/worker.spec.ts @@ -60,31 +60,28 @@ describe('worker', () => { const application = 'application'; const version = 'version'; - describe('legacy', () => { - let symbolFileInfos; - - beforeEach(async () => { - symbolFileInfos = createFakeSymbolFileInfos(2).map((info) => ({ ...info, dbgId: undefined })); + describe('skip', () => { + it('should skip files with no dbgId', async () => { + const symbolFileInfos = createFakeSymbolFileInfos(2).map((info) => ({ ...info, dbgId: '' })); const worker = createUploadWorkerWithFakeReadStream(1, symbolFileInfos, clients); - await worker.upload(database, application, version); - }); + const results = await worker.upload(database, application, version); - it('should call versionsClient.postSymbols with database, application, version, and symbol files', () => { - const symbolFiles = symbolFileInfos.map(symbolFile => ({ - name: `${symbolFile.path}.zip`, - dbgId: symbolFile.dbgId, - moduleName: symbolFile.moduleName, - size: 0, - uncompressedSize: 0, - lastModified: 0, - file: expect.stringContaining('.zip'), - })); - expect(versionsClient.postSymbols).toHaveBeenCalledWith(database, application, version, expect.arrayContaining([symbolFiles[0]])); - expect(versionsClient.postSymbols).toHaveBeenCalledWith(database, application, version, expect.arrayContaining([symbolFiles[1]])); + expect(symbolsClient.postSymbols).not.toHaveBeenCalled(); + expect(versionsClient.postSymbols).not.toHaveBeenCalled(); + expect(results.every(r => r.size === 0)).toBe(true); }); + }); + + describe('legacy', () => { + it('should use versionsClient for source maps without dbgId', async () => { + const symbolFileInfos = [ + createFakeSymbolFileInfo({ path: 'app.js.map', moduleName: 'app.js.map', dbgId: '' }), + ]; + const worker = createUploadWorkerWithFakeReadStream(1, symbolFileInfos, clients); + await worker.upload(database, application, version); - it('should call versionsClient.postSymbols for each symbol file', () => { - expect(versionsClient.postSymbols).toHaveBeenCalledTimes(symbolFileInfos.length); + expect(versionsClient.postSymbols).toHaveBeenCalledTimes(1); + expect(symbolsClient.postSymbols).not.toHaveBeenCalled(); }); }); diff --git a/src/guid.ts b/src/guid.ts new file mode 100644 index 0000000..3090cdb --- /dev/null +++ b/src/guid.ts @@ -0,0 +1,12 @@ +import { createFromFile } from 'pdb-guid'; + +export async function tryGetGuid(filePath: string): Promise { + try { + const file = await createFromFile(filePath); + return `${file.guid}`; + } catch (error) { + console.log(`Could not get UUID for ${filePath}...`); + } + + return ''; +} diff --git a/src/info.ts b/src/info.ts index 63ab69d..e7bba1c 100644 --- a/src/info.ts +++ b/src/info.ts @@ -2,7 +2,7 @@ import { stat } from "node:fs/promises"; import { basename, extname } from "node:path"; import { getDSymFileInfos } from "./dsym"; import { tryGetElfUUID } from "./elf"; -import { tryGetPdbGuid, tryGetPeGuid } from "./pdb"; +import { tryGetGuid } from "./guid"; import { getSymFileInfo } from "./sym"; export type SymbolFileInfo = { @@ -16,23 +16,12 @@ export async function createSymbolFileInfos(symbolFilePath: string): Promise stats.isDirectory()); const extLowerCase = extname(path).toLowerCase(); const isSymFile = extLowerCase.includes('.sym') && !isFolder; - const isPdbFile = extLowerCase.includes('.pdb') && !isFolder; - const isPeFile = extLowerCase.includes('.exe') || extLowerCase.includes('.dll') && !isFolder; + const isPeOrPdbFile = (extLowerCase.includes('.pdb') || extLowerCase.includes('.exe') || extLowerCase.includes('.dll')) && !isFolder; const isDsymBundle = extLowerCase.includes('.dsym'); const isElfFile = elfExtensions.some((ext) => extLowerCase.includes(ext) && !isFolder); - if (isPdbFile) { - const dbgId = await tryGetPdbGuid(path); - const moduleName = basename(path); - return [{ - path, - dbgId, - moduleName, - } as SymbolFileInfo]; - } - - if (isPeFile) { - const dbgId = await tryGetPeGuid(path); + if (isPeOrPdbFile) { + const dbgId = await tryGetGuid(path); const moduleName = basename(path); return [{ path, diff --git a/src/pdb.ts b/src/pdb.ts deleted file mode 100644 index 7ca1b4f..0000000 --- a/src/pdb.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { PdbFile, PeFile } from 'pdb-guid'; - -export async function tryGetPdbGuid(pdbFilePath: string): Promise { - try { - const pdbFile = await PdbFile.createFromFile(pdbFilePath); - return `${pdbFile.guid}`; - } catch (error) { - console.log(`Could not get UUID for ${pdbFilePath}...`); - } - - return ''; -} - -export async function tryGetPeGuid(peFilePath: string): Promise { - try { - const pdbFile = await PeFile.createFromFile(peFilePath); - return `${pdbFile.guid}`; - } catch (error) { - console.log(`Could not get UUID for ${peFilePath}...`); - } - - return ''; -} \ No newline at end of file diff --git a/src/worker.ts b/src/worker.ts index 1652fdb..9dd4cec 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -58,16 +58,23 @@ export class UploadWorker { let name = basename(path); let tmpFileName = ''; + const isSourceMap = extname(path).toLowerCase() === '.map'; + if (dbgId && !isZip) { tmpFileName = join(tmpDir, `${fileName}-${dbgId}-${uuid}.gz`); client = this.symbolsClient; await this.pool.exec('createGzipFile', [path, tmpFileName]); - } else if (!isZip) { - name = `${name}.zip`; - tmpFileName = join(tmpDir, `${fileName}-${dbgId}-${uuid}.zip`); - await this.pool.exec('createZipFile', [path, tmpFileName]); + } else if (isSourceMap || isZip) { + if (isZip) { + tmpFileName = path; + } else { + name = `${name}.zip`; + tmpFileName = join(tmpDir, `${fileName}-${dbgId}-${uuid}.zip`); + await this.pool.exec('createZipFile', [path, tmpFileName]); + } } else { - tmpFileName = path; + console.warn(`Worker ${this.id} skipping ${name} (extension: ${extname(path)}), missing dbgId...`); + return { name, size: 0 }; } const { mtime: lastModified } = await this.stat(path);