Skip to content

Commit 2fba173

Browse files
authored
fix(core): Don't crash on recoverable CLI command error (#682)
1 parent ee73414 commit 2fba173

File tree

14 files changed

+243
-11
lines changed

14 files changed

+243
-11
lines changed

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { stripQueryAndHashFromPath } from "./utils";
1212
import { setMeasurement, spanToTraceHeader, startSpan } from "@sentry/core";
1313
import { getDynamicSamplingContextFromSpan, Scope } from "@sentry/core";
1414
import { Client } from "@sentry/types";
15+
import { HandleRecoverableErrorFn } from "./types";
1516

1617
interface RewriteSourcesHook {
1718
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -25,7 +26,7 @@ interface DebugIdUploadPluginOptions {
2526
releaseName?: string;
2627
dist?: string;
2728
rewriteSourcesHook?: RewriteSourcesHook;
28-
handleRecoverableError: (error: unknown) => void;
29+
handleRecoverableError: HandleRecoverableErrorFn;
2930
sentryScope: Scope;
3031
sentryClient: Client;
3132
sentryCliOptions: {
@@ -167,7 +168,7 @@ export function createDebugIdUploadFunction({
167168
});
168169

169170
await cliInstance.releases.uploadSourceMaps(
170-
releaseName ?? "undefined", // unfortunetly this needs a value for now but it will not matter since debug IDs overpower releases anyhow
171+
releaseName ?? "undefined", // unfortunately this needs a value for now but it will not matter since debug IDs overpower releases anyhow
171172
{
172173
include: [
173174
{
@@ -187,7 +188,7 @@ export function createDebugIdUploadFunction({
187188
}
188189
} catch (e) {
189190
sentryScope.captureException('Error in "debugIdUploadPlugin" writeBundle hook');
190-
handleRecoverableError(e);
191+
handleRecoverableError(e, false);
191192
} finally {
192193
if (folderToCleanUp) {
193194
void startSpan({ name: "cleanup", scope: sentryScope }, async () => {

packages/bundler-plugin-core/src/index.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,16 @@ export function sentryUnpluginFactory({
155155
"SENTRY_PIPELINE"
156156
] = `${unpluginMetaContext.framework}-plugin/${__PACKAGE_VERSION__}`;
157157

158-
function handleRecoverableError(unknownError: unknown) {
158+
/**
159+
* Handles errors caught and emitted in various areas of the plugin.
160+
*
161+
* Also sets the sentry session status according to the error handling.
162+
*
163+
* If users specify their custom `errorHandler` we'll leave the decision to throw
164+
* or continue up to them. By default, @param throwByDefault controls if the plugin
165+
* should throw an error (which causes a build fail in most bundlers) or continue.
166+
*/
167+
function handleRecoverableError(unknownError: unknown, throwByDefault: boolean) {
159168
sentrySession.status = "abnormal";
160169
try {
161170
if (options.errorHandler) {
@@ -170,17 +179,24 @@ export function sentryUnpluginFactory({
170179
throw e;
171180
}
172181
} else {
182+
// setting the session to "crashed" b/c from a plugin perspective this run failed.
183+
// However, we're intentionally not rethrowing the error to avoid breaking the user build.
173184
sentrySession.status = "crashed";
174-
throw unknownError;
185+
if (throwByDefault) {
186+
throw unknownError;
187+
}
188+
logger.error("An error occurred. Couldn't finish all operations:", unknownError);
175189
}
176190
} finally {
177191
endSession();
178192
}
179193
}
180194

181195
if (!validateOptions(options, logger)) {
196+
// Throwing by default to avoid a misconfigured plugin going unnoticed.
182197
handleRecoverableError(
183-
new Error("Options were not set correctly. See output above for more details.")
198+
new Error("Options were not set correctly. See output above for more details."),
199+
true
184200
);
185201
}
186202

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Scope } from "@sentry/core";
33
import { UnpluginOptions } from "unplugin";
44
import { Logger } from "../sentry/logger";
55
import { safeFlushTelemetry } from "../sentry/telemetry";
6-
import { IncludeEntry } from "../types";
6+
import { HandleRecoverableErrorFn, IncludeEntry } from "../types";
77
import { arrayify } from "../utils";
88
import { Client } from "@sentry/types";
99

@@ -16,7 +16,7 @@ interface ReleaseManagementPluginOptions {
1616
setCommitsOption?: SentryCliCommitsOptions;
1717
deployOptions?: SentryCliNewDeployOptions;
1818
dist?: string;
19-
handleRecoverableError: (error: unknown) => void;
19+
handleRecoverableError: HandleRecoverableErrorFn;
2020
sentryScope: Scope;
2121
sentryClient: Client;
2222
sentryCliOptions: {
@@ -100,7 +100,7 @@ export function releaseManagementPlugin({
100100
} catch (e) {
101101
sentryScope.captureException('Error in "releaseManagementPlugin" writeBundle hook');
102102
await safeFlushTelemetry(sentryClient);
103-
handleRecoverableError(e);
103+
handleRecoverableError(e, false);
104104
} finally {
105105
freeGlobalDependencyOnSourcemapFiles();
106106
freeWriteBundleInvocationDependencyOnSourcemapFiles();

packages/bundler-plugin-core/src/plugins/sourcemap-deletion.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import { safeFlushTelemetry } from "../sentry/telemetry";
55
import fs from "fs";
66
import { Scope } from "@sentry/core";
77
import { Client } from "@sentry/types";
8+
import { HandleRecoverableErrorFn } from "../types";
89

910
interface FileDeletionPlugin {
10-
handleRecoverableError: (error: unknown) => void;
11+
handleRecoverableError: HandleRecoverableErrorFn;
1112
waitUntilSourcemapFileDependenciesAreFreed: () => Promise<void>;
1213
sentryScope: Scope;
1314
sentryClient: Client;
@@ -59,7 +60,9 @@ export function fileDeletionPlugin({
5960
} catch (e) {
6061
sentryScope.captureException('Error in "sentry-file-deletion-plugin" buildEnd hook');
6162
await safeFlushTelemetry(sentryClient);
62-
handleRecoverableError(e);
63+
// We throw by default if we get here b/c not being able to delete
64+
// source maps could leak them to production
65+
handleRecoverableError(e, true);
6366
}
6467
},
6568
};

packages/bundler-plugin-core/src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,3 +552,5 @@ type DeployOptions = {
552552
*/
553553
url?: string;
554554
};
555+
556+
export type HandleRecoverableErrorFn = (error: unknown, throwByDefault: boolean) => void;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { sentryEsbuildPlugin } from "@sentry/esbuild-plugin";
2+
import { build } from "esbuild";
3+
import pluginOptions from "./plugin-options";
4+
import path from "path";
5+
6+
build({
7+
entryPoints: [path.join(__dirname, "input", "bundle.js")],
8+
outdir: path.join(__dirname, "out", "esbuild"),
9+
plugins: [sentryEsbuildPlugin(pluginOptions)],
10+
minify: true,
11+
bundle: true,
12+
format: "cjs",
13+
sourcemap: true,
14+
}).catch((e) => {
15+
// eslint-disable-next-line no-console
16+
console.error(e);
17+
process.exit(1);
18+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { sentryRollupPlugin } from "@sentry/rollup-plugin";
2+
import * as path from "path";
3+
import * as rollup from "rollup";
4+
import pluginOptions from "./plugin-options";
5+
6+
rollup
7+
.rollup({
8+
input: {
9+
index: path.join(__dirname, "input", "bundle.js"),
10+
},
11+
plugins: [sentryRollupPlugin(pluginOptions)],
12+
})
13+
.then((bundle) =>
14+
bundle.write({
15+
dir: path.join(__dirname, "out", "rollup"),
16+
format: "cjs",
17+
exports: "named",
18+
})
19+
)
20+
.catch((e) => {
21+
// eslint-disable-next-line no-console
22+
console.error(e);
23+
process.exit(1);
24+
});
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { sentryVitePlugin } from "@sentry/vite-plugin";
2+
import * as path from "path";
3+
import * as vite from "vite";
4+
import pluginOptions from "./plugin-options";
5+
6+
vite
7+
.build({
8+
clearScreen: false,
9+
build: {
10+
outDir: path.join(__dirname, "out", "vite"),
11+
rollupOptions: {
12+
input: {
13+
index: path.join(__dirname, "input", "bundle.js"),
14+
},
15+
output: {
16+
format: "cjs",
17+
entryFileNames: "[name].js",
18+
},
19+
},
20+
},
21+
plugins: [sentryVitePlugin(pluginOptions)],
22+
})
23+
.catch((e) => {
24+
// eslint-disable-next-line no-console
25+
console.error(e);
26+
process.exit(1);
27+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { sentryWebpackPlugin } from "@sentry/webpack-plugin";
2+
import * as path from "path";
3+
import { default as webpack4 } from "webpack4";
4+
import pluginOptions from "./plugin-options";
5+
6+
webpack4(
7+
{
8+
devtool: "source-map",
9+
cache: false,
10+
entry: {
11+
index: path.join(__dirname, "input", "bundle.js"),
12+
},
13+
output: {
14+
path: path.join(__dirname, "out", "webpack4"),
15+
libraryTarget: "commonjs",
16+
},
17+
mode: "production",
18+
target: "node", // needed for webpack 4 so we can access node api
19+
plugins: [sentryWebpackPlugin(pluginOptions)],
20+
},
21+
(err) => {
22+
if (err) {
23+
process.exit(1);
24+
}
25+
}
26+
);
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { sentryWebpackPlugin } from "@sentry/webpack-plugin";
2+
import * as path from "path";
3+
import { webpack as webpack5 } from "webpack5";
4+
import pluginOptions from "./plugin-options";
5+
6+
webpack5(
7+
{
8+
devtool: "source-map",
9+
cache: false,
10+
entry: {
11+
index: path.join(__dirname, "input", "bundle.js"),
12+
},
13+
output: {
14+
path: path.join(__dirname, "out", "webpack5"),
15+
library: {
16+
type: "commonjs",
17+
},
18+
},
19+
mode: "production",
20+
plugins: [sentryWebpackPlugin(pluginOptions)],
21+
},
22+
(err) => {
23+
if (err) {
24+
process.exit(1);
25+
}
26+
}
27+
);

0 commit comments

Comments
 (0)