Skip to content

Commit dfbbc78

Browse files
author
Luca Forstner
committed
forceTransaction debug-id-sourcemap-upload
1 parent be19213 commit dfbbc78

File tree

1 file changed

+128
-122
lines changed

1 file changed

+128
-122
lines changed

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

Lines changed: 128 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -56,146 +56,152 @@ export function createDebugIdUploadFunction({
5656
const freeGlobalDependencyOnSourcemapFiles = createDependencyOnSourcemapFiles();
5757

5858
return async (buildArtifactPaths: string[]) => {
59-
await startSpan({ name: "debug-id-sourcemap-upload", scope: sentryScope }, async () => {
60-
let folderToCleanUp: string | undefined;
59+
await startSpan(
60+
// This is `forceTransaction`ed because this span is used in dashboards in the form of indexed transactions.
61+
{ name: "debug-id-sourcemap-upload", scope: sentryScope, forceTransaction: true },
62+
async () => {
63+
let folderToCleanUp: string | undefined;
64+
65+
// It is possible that this writeBundle hook (which calls this function) is called multiple times in one build (for example when reusing the plugin, or when using build tooling like `@vitejs/plugin-legacy`)
66+
// Therefore we need to actually register the execution of this hook as dependency on the sourcemap files.
67+
const freeUploadDependencyOnSourcemapFiles = createDependencyOnSourcemapFiles();
68+
69+
try {
70+
const tmpUploadFolder = await startSpan(
71+
{ name: "mkdtemp", scope: sentryScope },
72+
async () => {
73+
return await fs.promises.mkdtemp(
74+
path.join(os.tmpdir(), "sentry-bundler-plugin-upload-")
75+
);
76+
}
77+
);
6178

62-
// It is possible that this writeBundle hook (which calls this function) is called multiple times in one build (for example when reusing the plugin, or when using build tooling like `@vitejs/plugin-legacy`)
63-
// Therefore we need to actually register the execution of this hook as dependency on the sourcemap files.
64-
const freeUploadDependencyOnSourcemapFiles = createDependencyOnSourcemapFiles();
79+
folderToCleanUp = tmpUploadFolder;
6580

66-
try {
67-
const tmpUploadFolder = await startSpan(
68-
{ name: "mkdtemp", scope: sentryScope },
69-
async () => {
70-
return await fs.promises.mkdtemp(
71-
path.join(os.tmpdir(), "sentry-bundler-plugin-upload-")
81+
let globAssets: string | string[];
82+
if (assets) {
83+
globAssets = assets;
84+
} else {
85+
logger.debug(
86+
"No `sourcemaps.assets` option provided, falling back to uploading detected build artifacts."
7287
);
88+
globAssets = buildArtifactPaths;
7389
}
74-
);
75-
76-
folderToCleanUp = tmpUploadFolder;
7790

78-
let globAssets: string | string[];
79-
if (assets) {
80-
globAssets = assets;
81-
} else {
82-
logger.debug(
83-
"No `sourcemaps.assets` option provided, falling back to uploading detected build artifacts."
91+
const globResult = await startSpan(
92+
{ name: "glob", scope: sentryScope },
93+
async () => await glob(globAssets, { absolute: true, nodir: true, ignore: ignore })
8494
);
85-
globAssets = buildArtifactPaths;
86-
}
87-
88-
const globResult = await startSpan(
89-
{ name: "glob", scope: sentryScope },
90-
async () => await glob(globAssets, { absolute: true, nodir: true, ignore: ignore })
91-
);
9295

93-
const debugIdChunkFilePaths = globResult.filter((debugIdChunkFilePath) => {
94-
return !!stripQueryAndHashFromPath(debugIdChunkFilePath).match(/\.(js|mjs|cjs)$/);
95-
});
96+
const debugIdChunkFilePaths = globResult.filter((debugIdChunkFilePath) => {
97+
return !!stripQueryAndHashFromPath(debugIdChunkFilePath).match(/\.(js|mjs|cjs)$/);
98+
});
9699

97-
// The order of the files output by glob() is not deterministic
98-
// Ensure order within the files so that {debug-id}-{chunkIndex} coupling is consistent
99-
debugIdChunkFilePaths.sort();
100+
// The order of the files output by glob() is not deterministic
101+
// Ensure order within the files so that {debug-id}-{chunkIndex} coupling is consistent
102+
debugIdChunkFilePaths.sort();
100103

101-
if (Array.isArray(assets) && assets.length === 0) {
102-
logger.debug(
103-
"Empty `sourcemaps.assets` option provided. Will not upload sourcemaps with debug ID."
104-
);
105-
} else if (debugIdChunkFilePaths.length === 0) {
106-
logger.warn(
107-
"Didn't find any matching sources for debug ID upload. Please check the `sourcemaps.assets` option."
108-
);
109-
} else {
110-
await startSpan(
111-
{ name: "prepare-bundles", scope: sentryScope },
112-
async (prepBundlesSpan) => {
113-
// Preparing the bundles can be a lot of work and doing it all at once has the potential of nuking the heap so
114-
// instead we do it with a maximum of 16 concurrent workers
115-
const preparationTasks = debugIdChunkFilePaths.map(
116-
(chunkFilePath, chunkIndex) => async () => {
117-
await prepareBundleForDebugIdUpload(
118-
chunkFilePath,
119-
tmpUploadFolder,
120-
chunkIndex,
121-
logger,
122-
rewriteSourcesHook ?? defaultRewriteSourcesHook
123-
);
124-
}
125-
);
126-
const workers: Promise<void>[] = [];
127-
const worker = async () => {
128-
while (preparationTasks.length > 0) {
129-
const task = preparationTasks.shift();
130-
if (task) {
131-
await task();
104+
if (Array.isArray(assets) && assets.length === 0) {
105+
logger.debug(
106+
"Empty `sourcemaps.assets` option provided. Will not upload sourcemaps with debug ID."
107+
);
108+
} else if (debugIdChunkFilePaths.length === 0) {
109+
logger.warn(
110+
"Didn't find any matching sources for debug ID upload. Please check the `sourcemaps.assets` option."
111+
);
112+
} else {
113+
await startSpan(
114+
{ name: "prepare-bundles", scope: sentryScope },
115+
async (prepBundlesSpan) => {
116+
// Preparing the bundles can be a lot of work and doing it all at once has the potential of nuking the heap so
117+
// instead we do it with a maximum of 16 concurrent workers
118+
const preparationTasks = debugIdChunkFilePaths.map(
119+
(chunkFilePath, chunkIndex) => async () => {
120+
await prepareBundleForDebugIdUpload(
121+
chunkFilePath,
122+
tmpUploadFolder,
123+
chunkIndex,
124+
logger,
125+
rewriteSourcesHook ?? defaultRewriteSourcesHook
126+
);
132127
}
128+
);
129+
const workers: Promise<void>[] = [];
130+
const worker = async () => {
131+
while (preparationTasks.length > 0) {
132+
const task = preparationTasks.shift();
133+
if (task) {
134+
await task();
135+
}
136+
}
137+
};
138+
for (let workerIndex = 0; workerIndex < 16; workerIndex++) {
139+
workers.push(worker());
133140
}
134-
};
135-
for (let workerIndex = 0; workerIndex < 16; workerIndex++) {
136-
workers.push(worker());
137-
}
138141

139-
await Promise.all(workers);
142+
await Promise.all(workers);
140143

141-
const files = await fs.promises.readdir(tmpUploadFolder);
142-
const stats = files.map((file) => fs.promises.stat(path.join(tmpUploadFolder, file)));
143-
const uploadSize = (await Promise.all(stats)).reduce(
144-
(accumulator, { size }) => accumulator + size,
145-
0
146-
);
144+
const files = await fs.promises.readdir(tmpUploadFolder);
145+
const stats = files.map((file) =>
146+
fs.promises.stat(path.join(tmpUploadFolder, file))
147+
);
148+
const uploadSize = (await Promise.all(stats)).reduce(
149+
(accumulator, { size }) => accumulator + size,
150+
0
151+
);
147152

148-
setMeasurement("files", files.length, "none", prepBundlesSpan);
149-
setMeasurement("upload_size", uploadSize, "byte", prepBundlesSpan);
150-
151-
await startSpan({ name: "upload", scope: sentryScope }, async (uploadSpan) => {
152-
const cliInstance = new SentryCli(null, {
153-
...sentryCliOptions,
154-
headers: {
155-
"sentry-trace": spanToTraceHeader(uploadSpan),
156-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
157-
baggage: dynamicSamplingContextToSentryBaggageHeader(
158-
getDynamicSamplingContextFromSpan(uploadSpan)
159-
)!,
160-
...sentryCliOptions.headers,
161-
},
153+
setMeasurement("files", files.length, "none", prepBundlesSpan);
154+
setMeasurement("upload_size", uploadSize, "byte", prepBundlesSpan);
155+
156+
await startSpan({ name: "upload", scope: sentryScope }, async (uploadSpan) => {
157+
const cliInstance = new SentryCli(null, {
158+
...sentryCliOptions,
159+
headers: {
160+
"sentry-trace": spanToTraceHeader(uploadSpan),
161+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
162+
baggage: dynamicSamplingContextToSentryBaggageHeader(
163+
getDynamicSamplingContextFromSpan(uploadSpan)
164+
)!,
165+
...sentryCliOptions.headers,
166+
},
167+
});
168+
169+
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+
{
172+
include: [
173+
{
174+
paths: [tmpUploadFolder],
175+
rewrite: false,
176+
dist: dist,
177+
},
178+
],
179+
useArtifactBundle: true,
180+
}
181+
);
162182
});
183+
}
184+
);
163185

164-
await cliInstance.releases.uploadSourceMaps(
165-
releaseName ?? "undefined", // unfortunetly this needs a value for now but it will not matter since debug IDs overpower releases anyhow
166-
{
167-
include: [
168-
{
169-
paths: [tmpUploadFolder],
170-
rewrite: false,
171-
dist: dist,
172-
},
173-
],
174-
useArtifactBundle: true,
175-
}
176-
);
177-
});
178-
}
179-
);
180-
181-
logger.info("Successfully uploaded source maps to Sentry");
182-
}
183-
} catch (e) {
184-
sentryScope.captureException('Error in "debugIdUploadPlugin" writeBundle hook');
185-
handleRecoverableError(e);
186-
} finally {
187-
if (folderToCleanUp) {
188-
void startSpan({ name: "cleanup", scope: sentryScope }, async () => {
189-
if (folderToCleanUp) {
190-
await fs.promises.rm(folderToCleanUp, { recursive: true, force: true });
191-
}
192-
});
186+
logger.info("Successfully uploaded source maps to Sentry");
187+
}
188+
} catch (e) {
189+
sentryScope.captureException('Error in "debugIdUploadPlugin" writeBundle hook');
190+
handleRecoverableError(e);
191+
} finally {
192+
if (folderToCleanUp) {
193+
void startSpan({ name: "cleanup", scope: sentryScope }, async () => {
194+
if (folderToCleanUp) {
195+
await fs.promises.rm(folderToCleanUp, { recursive: true, force: true });
196+
}
197+
});
198+
}
199+
freeGlobalDependencyOnSourcemapFiles();
200+
freeUploadDependencyOnSourcemapFiles();
201+
await safeFlushTelemetry(sentryClient);
193202
}
194-
freeGlobalDependencyOnSourcemapFiles();
195-
freeUploadDependencyOnSourcemapFiles();
196-
await safeFlushTelemetry(sentryClient);
197203
}
198-
});
204+
);
199205
};
200206
}
201207

0 commit comments

Comments
 (0)