Skip to content

Commit 4534093

Browse files
authored
fix: Always delete files when sourcemaps.filesToDeleteAfterUpload is set (#547)
1 parent 6456536 commit 4534093

File tree

8 files changed

+130
-35
lines changed

8 files changed

+130
-35
lines changed

Diff for: packages/bundler-plugin-core/src/debug-id-upload.ts

+3-32
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ interface DebugIdUploadPluginOptions {
2424
handleRecoverableError: (error: unknown) => void;
2525
sentryHub: Hub;
2626
sentryClient: NodeClient;
27-
filesToDeleteAfterUpload?: string | string[];
27+
deleteFilesUpForDeletion: () => Promise<void>;
2828
sentryCliOptions: {
2929
url: string;
3030
authToken: string;
@@ -47,7 +47,7 @@ export function createDebugIdUploadFunction({
4747
sentryClient,
4848
sentryCliOptions,
4949
rewriteSourcesHook,
50-
filesToDeleteAfterUpload,
50+
deleteFilesUpForDeletion,
5151
}: DebugIdUploadPluginOptions) {
5252
return async (buildArtifactPaths: string[]) => {
5353
const artifactBundleUploadTransaction = sentryHub.startTransaction({
@@ -180,36 +180,7 @@ export function createDebugIdUploadFunction({
180180
logger.info("Successfully uploaded source maps to Sentry");
181181
}
182182

183-
if (filesToDeleteAfterUpload) {
184-
const deleteGlobSpan = artifactBundleUploadTransaction.startChild({
185-
description: "delete-glob",
186-
});
187-
const filePathsToDelete = await glob(filesToDeleteAfterUpload, {
188-
absolute: true,
189-
nodir: true,
190-
});
191-
deleteGlobSpan.finish();
192-
193-
filePathsToDelete.forEach((filePathToDelete) => {
194-
logger.debug(`Deleting asset after upload: ${filePathToDelete}`);
195-
});
196-
197-
const deleteSpan = artifactBundleUploadTransaction.startChild({
198-
description: "delete-files-after-upload",
199-
});
200-
await Promise.all(
201-
filePathsToDelete.map((filePathToDelete) =>
202-
fs.promises.rm(filePathToDelete, { force: true }).catch((e) => {
203-
// This is allowed to fail - we just don't do anything
204-
logger.debug(
205-
`An error occured while attempting to delete asset: ${filePathToDelete}`,
206-
e
207-
);
208-
})
209-
)
210-
);
211-
deleteSpan.finish();
212-
}
183+
await deleteFilesUpForDeletion();
213184
} catch (e) {
214185
sentryHub.withScope((scope) => {
215186
scope.setSpan(artifactBundleUploadTransaction);

Diff for: packages/bundler-plugin-core/src/index.ts

+40-3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
import * as dotenv from "dotenv";
2626
import { glob } from "glob";
2727
import { logger } from "@sentry/utils";
28+
import { fileDeletionPlugin } from "./plugins/sourcemap-deletion";
2829

2930
interface SentryUnpluginFactoryOptions {
3031
releaseInjectionPlugin: (injectionCode: string) => UnpluginOptions;
@@ -184,6 +185,34 @@ export function sentryUnpluginFactory({
184185
})
185186
);
186187

188+
async function deleteFilesUpForDeletion() {
189+
const filesToDeleteAfterUpload =
190+
options.sourcemaps?.filesToDeleteAfterUpload ?? options.sourcemaps?.deleteFilesAfterUpload;
191+
192+
if (filesToDeleteAfterUpload) {
193+
const filePathsToDelete = await glob(filesToDeleteAfterUpload, {
194+
absolute: true,
195+
nodir: true,
196+
});
197+
198+
filePathsToDelete.forEach((filePathToDelete) => {
199+
logger.debug(`Deleting asset after upload: ${filePathToDelete}`);
200+
});
201+
202+
await Promise.all(
203+
filePathsToDelete.map((filePathToDelete) =>
204+
fs.promises.rm(filePathToDelete, { force: true }).catch((e) => {
205+
// This is allowed to fail - we just don't do anything
206+
logger.debug(
207+
`An error occurred while attempting to delete asset: ${filePathToDelete}`,
208+
e
209+
);
210+
})
211+
)
212+
);
213+
}
214+
}
215+
187216
if (options.bundleSizeOptimizations) {
188217
const { bundleSizeOptimizations } = options;
189218
const replacementValues: SentrySDKBuildFlags = {};
@@ -297,12 +326,22 @@ export function sentryUnpluginFactory({
297326
vcsRemote: options.release.vcsRemote,
298327
headers: options.headers,
299328
},
329+
deleteFilesUpForDeletion,
300330
})
301331
);
302332
}
303333

304334
plugins.push(debugIdInjectionPlugin(logger));
305335

336+
plugins.push(
337+
fileDeletionPlugin({
338+
deleteFilesUpForDeletion,
339+
handleRecoverableError,
340+
sentryHub,
341+
sentryClient,
342+
})
343+
);
344+
306345
if (!options.authToken) {
307346
logger.warn(
308347
"No auth token provided. Will not upload source maps. Please set the `authToken` option. You can find information on how to generate a Sentry auth token here: https://docs.sentry.io/api/auth/"
@@ -321,9 +360,7 @@ export function sentryUnpluginFactory({
321360
createDebugIdUploadFunction({
322361
assets: options.sourcemaps?.assets,
323362
ignore: options.sourcemaps?.ignore,
324-
filesToDeleteAfterUpload:
325-
options.sourcemaps?.filesToDeleteAfterUpload ??
326-
options.sourcemaps?.deleteFilesAfterUpload,
363+
deleteFilesUpForDeletion,
327364
dist: options.release.dist,
328365
releaseName: options.release.name,
329366
logger: logger,

Diff for: packages/bundler-plugin-core/src/plugins/release-management.ts

+4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ interface ReleaseManagementPluginOptions {
2727
silent: boolean;
2828
headers?: Record<string, string>;
2929
};
30+
deleteFilesUpForDeletion: () => Promise<void>;
3031
}
3132

3233
export function releaseManagementPlugin({
@@ -41,6 +42,7 @@ export function releaseManagementPlugin({
4142
sentryHub,
4243
sentryClient,
4344
sentryCliOptions,
45+
deleteFilesUpForDeletion,
4446
}: ReleaseManagementPluginOptions): UnpluginOptions {
4547
return {
4648
name: "sentry-debug-id-upload-plugin",
@@ -83,6 +85,8 @@ export function releaseManagementPlugin({
8385
if (deployOptions) {
8486
await cliInstance.releases.newDeploy(releaseName, deployOptions);
8587
}
88+
89+
await deleteFilesUpForDeletion();
8690
} catch (e) {
8791
sentryHub.captureException('Error in "releaseManagementPlugin" writeBundle hook');
8892
await safeFlushTelemetry(sentryClient);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Hub, NodeClient } from "@sentry/node";
2+
import { UnpluginOptions } from "unplugin";
3+
import { safeFlushTelemetry } from "../sentry/telemetry";
4+
5+
interface FileDeletionPlugin {
6+
handleRecoverableError: (error: unknown) => void;
7+
deleteFilesUpForDeletion: () => Promise<void>;
8+
sentryHub: Hub;
9+
sentryClient: NodeClient;
10+
}
11+
12+
export function fileDeletionPlugin({
13+
handleRecoverableError,
14+
sentryHub,
15+
sentryClient,
16+
deleteFilesUpForDeletion,
17+
}: FileDeletionPlugin): UnpluginOptions {
18+
return {
19+
name: "sentry-file-deletion-plugin",
20+
async writeBundle() {
21+
try {
22+
await deleteFilesUpForDeletion();
23+
} catch (e) {
24+
sentryHub.captureException('Error in "sentry-file-deletion-plugin" buildEnd hook');
25+
await safeFlushTelemetry(sentryClient);
26+
handleRecoverableError(e);
27+
}
28+
},
29+
};
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/* eslint-disable jest/no-standalone-expect */
2+
/* eslint-disable jest/expect-expect */
3+
import path from "path";
4+
import fs from "fs";
5+
import { testIfNodeMajorVersionIsLessThan18 } from "../../utils/testIf";
6+
7+
describe("Deletes with `filesToDeleteAfterUpload` even without uploading anything", () => {
8+
testIfNodeMajorVersionIsLessThan18("webpack 4 bundle", () => {
9+
expect(fs.existsSync(path.join(__dirname, "out", "webpack4", "bundle.js.map"))).toBe(false);
10+
});
11+
12+
test("webpack 5 bundle", () => {
13+
expect(fs.existsSync(path.join(__dirname, "out", "webpack5", "bundle.js.map"))).toBe(false);
14+
});
15+
16+
test("esbuild bundle", () => {
17+
expect(fs.existsSync(path.join(__dirname, "out", "esbuild", "bundle.js.map"))).toBe(false);
18+
});
19+
20+
test("rollup bundle", () => {
21+
expect(fs.existsSync(path.join(__dirname, "out", "rollup", "bundle.js.map"))).toBe(false);
22+
});
23+
24+
test("vite bundle", () => {
25+
expect(fs.existsSync(path.join(__dirname, "out", "vite", "bundle.js.map"))).toBe(false);
26+
});
27+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// eslint-disable-next-line no-console
2+
console.log("whatever");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import * as path from "path";
2+
import { createCjsBundles } from "../../utils/create-cjs-bundles";
3+
4+
const outputDir = path.resolve(__dirname, "out");
5+
6+
["webpack4", "webpack5", "esbuild", "rollup", "vite"].forEach((bundler) => {
7+
createCjsBundles(
8+
{
9+
bundle: path.resolve(__dirname, "input", "bundle.js"),
10+
},
11+
outputDir,
12+
{
13+
sourcemaps: {
14+
filesToDeleteAfterUpload: [path.join(__dirname, "out", bundler, "bundle.js.map")],
15+
},
16+
},
17+
[bundler]
18+
);
19+
});

Diff for: packages/integration-tests/utils/create-cjs-bundles.ts

+5
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export function createCjsBundles(
2323
void vite.build({
2424
clearScreen: false,
2525
build: {
26+
sourcemap: true,
2627
outDir: path.join(outFolder, "vite"),
2728
rollupOptions: {
2829
input: entrypoints,
@@ -43,6 +44,7 @@ export function createCjsBundles(
4344
})
4445
.then((bundle) =>
4546
bundle.write({
47+
sourcemap: true,
4648
dir: path.join(outFolder, "rollup"),
4749
format: "cjs",
4850
exports: "named",
@@ -52,6 +54,7 @@ export function createCjsBundles(
5254

5355
if (plugins.length === 0 || plugins.includes("esbuild")) {
5456
void esbuild.build({
57+
sourcemap: true,
5558
entryPoints: entrypoints,
5659
outdir: path.join(outFolder, "esbuild"),
5760
plugins: [sentryEsbuildPlugin(sentryUnpluginOptions)],
@@ -65,6 +68,7 @@ export function createCjsBundles(
6568
if (parseInt(nodejsMajorversion) < 18 && (plugins.length === 0 || plugins.includes("webpack4"))) {
6669
webpack4(
6770
{
71+
devtool: "source-map",
6872
mode: "production",
6973
entry: entrypoints,
7074
cache: false,
@@ -86,6 +90,7 @@ export function createCjsBundles(
8690
if (plugins.length === 0 || plugins.includes("webpack5")) {
8791
webpack5(
8892
{
93+
devtool: "source-map",
8994
cache: false,
9095
entry: entrypoints,
9196
output: {

0 commit comments

Comments
 (0)