Skip to content

Commit 18ba045

Browse files
author
Luca Forstner
authored
ref(core): Extract release management into a separate plugin (#232)
1 parent cc7ad7d commit 18ba045

File tree

4 files changed

+109
-498
lines changed

4 files changed

+109
-498
lines changed

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

Lines changed: 43 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,19 @@
11
import SentryCli from "@sentry/cli";
22
import { makeMain } from "@sentry/node";
3-
import { Span, Transaction } from "@sentry/types";
43
import fs from "fs";
54
import MagicString from "magic-string";
65
import { createUnplugin, UnpluginOptions } from "unplugin";
7-
import { NormalizedOptions, normalizeUserOptions, validateOptions } from "./options-mapping";
6+
import { normalizeUserOptions, validateOptions } from "./options-mapping";
87
import { debugIdUploadPlugin } from "./plugins/debug-id-upload";
8+
import { releaseManagementPlugin } from "./plugins/release-management";
99
import { getSentryCli } from "./sentry/cli";
10-
import { createLogger, Logger } from "./sentry/logger";
11-
import {
12-
addDeploy,
13-
cleanArtifacts,
14-
createNewRelease,
15-
finalizeRelease,
16-
setCommits,
17-
uploadSourceMaps,
18-
} from "./sentry/releasePipeline";
10+
import { createLogger } from "./sentry/logger";
1911
import {
2012
addPluginOptionInformationToHub,
21-
addSpanToTransaction,
2213
makeSentryClient,
2314
shouldSendTelemetry,
2415
} from "./sentry/telemetry";
25-
import { BuildContext, Options } from "./types";
16+
import { Options } from "./types";
2617
import {
2718
determineReleaseName,
2819
generateGlobalInjectorCode,
@@ -72,7 +63,7 @@ export function sentryUnpluginFactory({
7263

7364
const allowedToSendTelemetryPromise = shouldSendTelemetry(options);
7465

75-
const { sentryHub, sentryClient } = makeSentryClient(
66+
const { sentryHub } = makeSentryClient(
7667
"https://[email protected]/6690737",
7768
allowedToSendTelemetryPromise,
7869
options.project
@@ -93,25 +84,34 @@ export function sentryUnpluginFactory({
9384
debug: options.debug,
9485
});
9586

87+
function handleError(unknownError: unknown) {
88+
if (unknownError instanceof Error) {
89+
logger.error(unknownError.message);
90+
} else {
91+
logger.error(String(unknownError));
92+
}
93+
94+
if (options.errorHandler) {
95+
if (unknownError instanceof Error) {
96+
options.errorHandler(unknownError);
97+
} else {
98+
options.errorHandler(new Error("An unknown error occured"));
99+
}
100+
} else {
101+
throw unknownError;
102+
}
103+
}
104+
96105
if (!validateOptions(options, logger)) {
97-
handleError(
98-
new Error("Options were not set correctly. See output above for more details."),
99-
logger,
100-
options.errorHandler
101-
);
106+
handleError(new Error("Options were not set correctly. See output above for more details."));
102107
}
103108

104109
const cli = getSentryCli(options, logger);
105110

106-
let transaction: Transaction | undefined;
107-
let releaseInjectionSpan: Span | undefined;
108-
109111
const releaseName = options.release ?? determineReleaseName();
110112
if (!releaseName) {
111113
handleError(
112-
new Error("Unable to determine a release name. Please set the `release` option."),
113-
logger,
114-
options.errorHandler
114+
new Error("Unable to determine a release name. Please set the `release` option.")
115115
);
116116
}
117117

@@ -144,79 +144,27 @@ export function sentryUnpluginFactory({
144144
"Running Sentry plugin from within a `node_modules` folder. Some features may not work."
145145
);
146146
}
147-
148-
transaction = sentryHub.startTransaction({
149-
op: "function.plugin",
150-
name: "Sentry Bundler Plugin execution",
151-
});
152-
},
153-
154-
/**
155-
* Responsible for executing the sentry release creation pipeline (i.e. creating a release on
156-
* Sentry.io, uploading sourcemaps, associating commits and deploys and finalizing the release)
157-
*/
158-
async writeBundle() {
159-
logger.debug('Called "writeBundle"');
160-
161-
releaseInjectionSpan?.finish();
162-
const releasePipelineSpan =
163-
transaction &&
164-
addSpanToTransaction(
165-
{ hub: sentryHub, parentSpan: transaction, logger, cli },
166-
"function.plugin.release",
167-
"Release pipeline"
168-
);
169-
170-
sentryHub.addBreadcrumb({
171-
category: "writeBundle:start",
172-
level: "info",
173-
});
174-
175-
const ctx: BuildContext = { hub: sentryHub, parentSpan: releasePipelineSpan, logger, cli };
176-
177-
let tmpUploadFolder: string | undefined;
178-
179-
try {
180-
if (!unpluginMetaContext.watchMode) {
181-
if (releaseName) {
182-
await createNewRelease(options, ctx, releaseName);
183-
await cleanArtifacts(options, ctx, releaseName);
184-
await uploadSourceMaps(options, ctx, releaseName);
185-
await setCommits(options, ctx, releaseName);
186-
await finalizeRelease(options, ctx, releaseName);
187-
await addDeploy(options, ctx, releaseName);
188-
} else {
189-
logger.warn("No release value provided. Will not upload source maps.");
190-
}
191-
}
192-
transaction?.setStatus("ok");
193-
} catch (e: unknown) {
194-
transaction?.setStatus("cancelled");
195-
sentryHub.addBreadcrumb({
196-
level: "error",
197-
message: "Error during writeBundle",
198-
});
199-
handleError(e, logger, options.errorHandler);
200-
} finally {
201-
if (tmpUploadFolder) {
202-
fs.rm(tmpUploadFolder, { recursive: true, force: true }, () => {
203-
// We don't care if this errors
204-
});
205-
}
206-
releasePipelineSpan?.finish();
207-
transaction?.finish();
208-
await sentryClient.flush().then(null, () => {
209-
logger.warn("Sending of telemetry failed");
210-
});
211-
}
212-
213-
sentryHub.addBreadcrumb({
214-
category: "writeBundle:finish",
215-
level: "info",
216-
});
217147
},
218148
});
219149

150+
if (releaseName) {
151+
plugins.push(
152+
releaseManagementPlugin({
153+
logger,
154+
cliInstance: cli,
155+
releaseName: releaseName,
156+
shouldCleanArtifacts: options.cleanArtifacts,
157+
shouldUploadSourceMaps: options.uploadSourceMaps,
158+
shouldFinalizeRelease: options.finalize,
159+
include: options.include,
160+
setCommitsOption: options.setCommits,
161+
deployOptions: options.deploy,
162+
dist: options.dist,
163+
handleError,
164+
})
165+
);
166+
}
167+
220168
if (options.injectRelease && releaseName) {
221169
const injectionCode = generateGlobalInjectorCode({
222170
release: releaseName,
@@ -250,28 +198,6 @@ export function sentryUnpluginFactory({
250198
});
251199
}
252200

253-
function handleError(
254-
unknownError: unknown,
255-
logger: Logger,
256-
errorHandler: NormalizedOptions["errorHandler"]
257-
) {
258-
if (unknownError instanceof Error) {
259-
logger.error(unknownError.message);
260-
} else {
261-
logger.error(String(unknownError));
262-
}
263-
264-
if (errorHandler) {
265-
if (unknownError instanceof Error) {
266-
errorHandler(unknownError);
267-
} else {
268-
errorHandler(new Error("An unknown error occured"));
269-
}
270-
} else {
271-
throw unknownError;
272-
}
273-
}
274-
275201
export function getBuildInformation() {
276202
const packageJson = getPackageJson();
277203

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { SentryCliCommitsOptions, SentryCliNewDeployOptions } from "@sentry/cli";
2+
import { UnpluginOptions } from "unplugin";
3+
import { InternalIncludeEntry } from "../options-mapping";
4+
import { SentryCLILike } from "../sentry/cli";
5+
import { Logger } from "../sentry/logger";
6+
7+
interface DebugIdUploadPluginOptions {
8+
logger: Logger;
9+
cliInstance: SentryCLILike;
10+
releaseName: string;
11+
shouldCleanArtifacts: boolean;
12+
shouldUploadSourceMaps: boolean;
13+
shouldFinalizeRelease: boolean;
14+
include: InternalIncludeEntry[];
15+
setCommitsOption?: SentryCliCommitsOptions;
16+
deployOptions?: SentryCliNewDeployOptions;
17+
dist?: string;
18+
handleError: (error: unknown) => void;
19+
}
20+
21+
export function releaseManagementPlugin({
22+
cliInstance,
23+
releaseName,
24+
include,
25+
dist,
26+
setCommitsOption,
27+
shouldCleanArtifacts,
28+
shouldUploadSourceMaps,
29+
shouldFinalizeRelease,
30+
deployOptions,
31+
handleError,
32+
}: DebugIdUploadPluginOptions): UnpluginOptions {
33+
return {
34+
name: "sentry-debug-id-upload-plugin",
35+
async writeBundle() {
36+
try {
37+
await cliInstance.releases.new(releaseName);
38+
39+
if (shouldCleanArtifacts) {
40+
await cliInstance.releases.execute(
41+
["releases", "files", releaseName, "delete", "--all"],
42+
true
43+
);
44+
}
45+
46+
if (shouldUploadSourceMaps) {
47+
await cliInstance.releases.uploadSourceMaps(releaseName, { include, dist });
48+
}
49+
50+
if (setCommitsOption) {
51+
await cliInstance.releases.setCommits(releaseName, setCommitsOption);
52+
}
53+
54+
if (shouldFinalizeRelease) {
55+
await cliInstance.releases.finalize(releaseName);
56+
}
57+
58+
if (deployOptions) {
59+
await cliInstance.releases.newDeploy(releaseName, deployOptions);
60+
}
61+
} catch (e) {
62+
handleError(e);
63+
}
64+
},
65+
};
66+
}

0 commit comments

Comments
 (0)