@@ -56,146 +56,152 @@ export function createDebugIdUploadFunction({
56
56
const freeGlobalDependencyOnSourcemapFiles = createDependencyOnSourcemapFiles ( ) ;
57
57
58
58
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
+ ) ;
61
78
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 ;
65
80
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."
72
87
) ;
88
+ globAssets = buildArtifactPaths ;
73
89
}
74
- ) ;
75
-
76
- folderToCleanUp = tmpUploadFolder ;
77
90
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 } )
84
94
) ;
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
- ) ;
92
95
93
- const debugIdChunkFilePaths = globResult . filter ( ( debugIdChunkFilePath ) => {
94
- return ! ! stripQueryAndHashFromPath ( debugIdChunkFilePath ) . match ( / \. ( j s | m j s | c j s ) $ / ) ;
95
- } ) ;
96
+ const debugIdChunkFilePaths = globResult . filter ( ( debugIdChunkFilePath ) => {
97
+ return ! ! stripQueryAndHashFromPath ( debugIdChunkFilePath ) . match ( / \. ( j s | m j s | c j s ) $ / ) ;
98
+ } ) ;
96
99
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 ( ) ;
100
103
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
+ ) ;
132
127
}
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 ( ) ) ;
133
140
}
134
- } ;
135
- for ( let workerIndex = 0 ; workerIndex < 16 ; workerIndex ++ ) {
136
- workers . push ( worker ( ) ) ;
137
- }
138
141
139
- await Promise . all ( workers ) ;
142
+ await Promise . all ( workers ) ;
140
143
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
+ ) ;
147
152
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
+ ) ;
162
182
} ) ;
183
+ }
184
+ ) ;
163
185
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 ) ;
193
202
}
194
- freeGlobalDependencyOnSourcemapFiles ( ) ;
195
- freeUploadDependencyOnSourcemapFiles ( ) ;
196
- await safeFlushTelemetry ( sentryClient ) ;
197
203
}
198
- } ) ;
204
+ ) ;
199
205
} ;
200
206
}
201
207
0 commit comments