@@ -11,7 +11,7 @@ import { CloudRequest } from '../api/cloud_request'
11
11
import { isRetryableError } from '../network/is_retryable_error'
12
12
import { asyncRetry } from '../../util/async_retry'
13
13
import { postStudioSession } from '../api/studio/post_studio_session'
14
- import type { StudioStatus } from '@packages/types'
14
+ import type { StudioServerOptions , StudioStatus } from '@packages/types'
15
15
import path from 'path'
16
16
import os from 'os'
17
17
import { ensureStudioBundle } from './ensure_studio_bundle'
@@ -39,7 +39,6 @@ export class StudioLifecycleManager {
39
39
private currentStudioHash ?: string
40
40
41
41
private initializationParams ?: {
42
- projectId ?: string
43
42
cloudDataSource : CloudDataSource
44
43
cfg : Cfg
45
44
debugData : any
@@ -55,20 +54,17 @@ export class StudioLifecycleManager {
55
54
/**
56
55
* Initialize the studio manager and possibly set up protocol.
57
56
* Also registers this instance in the data context.
58
- * @param projectId The project ID
59
57
* @param cloudDataSource The cloud data source
60
58
* @param cfg The project configuration
61
59
* @param debugData Debug data for the configuration
62
60
* @param ctx Data context to register this instance with
63
61
*/
64
62
initializeStudioManager ( {
65
- projectId,
66
63
cloudDataSource,
67
64
cfg,
68
65
debugData,
69
66
ctx,
70
67
} : {
71
- projectId ?: string
72
68
cloudDataSource : CloudDataSource
73
69
cfg : Cfg
74
70
debugData : any
@@ -77,7 +73,7 @@ export class StudioLifecycleManager {
77
73
debug ( 'Initializing studio manager' )
78
74
79
75
// Store initialization parameters for retry
80
- this . initializationParams = { projectId , cloudDataSource, cfg, debugData, ctx }
76
+ this . initializationParams = { cloudDataSource, cfg, debugData, ctx }
81
77
82
78
// Register this instance in the data context
83
79
ctx . update ( ( data ) => {
@@ -88,48 +84,64 @@ export class StudioLifecycleManager {
88
84
89
85
this . updateStatus ( 'INITIALIZING' )
90
86
87
+ const getProjectOptions = async ( ) => {
88
+ const [ user , config ] = await Promise . all ( [
89
+ ctx . actions . auth . authApi . getUser ( ) ,
90
+ ctx . project . getConfig ( ) ,
91
+ ] )
92
+
93
+ return {
94
+ user,
95
+ projectSlug : config . projectId || undefined ,
96
+ }
97
+ }
98
+
91
99
const studioManagerPromise = this . createStudioManager ( {
92
- projectId,
93
100
cloudDataSource,
94
101
cfg,
95
102
debugData,
103
+ getProjectOptions,
96
104
} ) . catch ( async ( error ) => {
97
105
debug ( 'Error during studio manager setup: %o' , error )
98
106
99
- const { cloudUrl, cloudHeaders } = await getCloudMetadata ( cloudDataSource )
100
-
101
- reportStudioError ( {
102
- cloudApi : {
103
- cloudUrl,
104
- cloudHeaders,
105
- CloudRequest,
106
- isRetryableError,
107
- asyncRetry,
108
- } ,
109
- studioHash : projectId ,
110
- projectSlug : cfg . projectId ,
111
- error,
112
- studioMethod : 'initializeStudioManager' ,
113
- studioMethodArgs : [ ] ,
114
- } )
107
+ try {
108
+ const { cloudUrl, cloudHeaders } = await getCloudMetadata ( cloudDataSource )
109
+
110
+ reportStudioError ( {
111
+ cloudApi : {
112
+ cloudUrl,
113
+ cloudHeaders,
114
+ CloudRequest,
115
+ isRetryableError,
116
+ asyncRetry,
117
+ } ,
118
+ studioHash : this . currentStudioHash ,
119
+ projectSlug : ( await getProjectOptions ( ) ) . projectSlug ,
120
+ error,
121
+ studioMethod : 'initializeStudioManager' ,
122
+ studioMethodArgs : [ ] ,
123
+ } )
115
124
116
- this . updateStatus ( 'IN_ERROR' )
125
+ this . updateStatus ( 'IN_ERROR' )
117
126
118
- telemetryManager . mark ( BUNDLE_LIFECYCLE_MARK_NAMES . BUNDLE_LIFECYCLE_END )
119
- reportTelemetry ( BUNDLE_LIFECYCLE_TELEMETRY_GROUP_NAMES . COMPLETE_BUNDLE_LIFECYCLE , {
120
- success : false ,
121
- } )
127
+ telemetryManager . mark ( BUNDLE_LIFECYCLE_MARK_NAMES . BUNDLE_LIFECYCLE_END )
128
+ reportTelemetry ( BUNDLE_LIFECYCLE_TELEMETRY_GROUP_NAMES . COMPLETE_BUNDLE_LIFECYCLE , {
129
+ success : false ,
130
+ } )
131
+ } catch ( error ) {
132
+ debug ( 'Error reporting studio error: %o' , error )
133
+ }
122
134
123
135
return null
124
136
} )
125
137
126
138
this . studioManagerPromise = studioManagerPromise
127
139
128
140
this . setupWatcher ( {
129
- projectId,
130
141
cloudDataSource,
131
142
cfg,
132
143
debugData,
144
+ getProjectOptions,
133
145
} )
134
146
}
135
147
@@ -158,30 +170,32 @@ export class StudioLifecycleManager {
158
170
}
159
171
160
172
private async createStudioManager ( {
161
- projectId,
162
173
cloudDataSource,
163
174
cfg,
164
175
debugData,
176
+ getProjectOptions,
165
177
} : {
166
- projectId ?: string
167
178
cloudDataSource : CloudDataSource
168
179
cfg : Cfg
169
180
debugData : any
181
+ getProjectOptions : StudioServerOptions [ 'getProjectOptions' ]
170
182
} ) : Promise < StudioManager > {
171
183
let studioPath : string
172
184
let studioHash : string
173
185
let manifest : Record < string , string >
174
186
187
+ const currentProjectOptions = await getProjectOptions ( )
188
+
175
189
initializeTelemetryReporter ( {
176
- projectSlug : projectId ,
190
+ projectSlug : currentProjectOptions . projectSlug ,
177
191
cloudDataSource,
178
192
} )
179
193
180
194
telemetryManager . mark ( BUNDLE_LIFECYCLE_MARK_NAMES . BUNDLE_LIFECYCLE_START )
181
195
182
196
telemetryManager . mark ( BUNDLE_LIFECYCLE_MARK_NAMES . POST_STUDIO_SESSION_START )
183
197
const studioSession = await postStudioSession ( {
184
- projectId,
198
+ projectId : currentProjectOptions . projectSlug ,
185
199
} )
186
200
187
201
telemetryManager . mark ( BUNDLE_LIFECYCLE_MARK_NAMES . POST_STUDIO_SESSION_END )
@@ -192,22 +206,27 @@ export class StudioLifecycleManager {
192
206
studioHash = studioSession . studioUrl . split ( '/' ) . pop ( ) ?. split ( '.' ) [ 0 ] ?? ''
193
207
studioPath = path . join ( os . tmpdir ( ) , 'cypress' , 'studio' , studioHash )
194
208
209
+ debug ( 'Setting current studio hash: %s' , studioHash )
195
210
// Store the current studio hash so that we can clear the cache entry when retrying
196
211
this . currentStudioHash = studioHash
197
212
198
213
let hashLoadingPromise = StudioLifecycleManager . hashLoadingMap . get ( studioHash )
199
214
200
215
if ( ! hashLoadingPromise ) {
216
+ debug ( 'Ensuring studio bundle for hash: %s' , studioHash )
217
+
201
218
hashLoadingPromise = ensureStudioBundle ( {
202
219
studioUrl : studioSession . studioUrl ,
203
220
studioPath,
204
- projectId,
221
+ projectId : currentProjectOptions . projectSlug ,
205
222
} )
206
223
207
224
StudioLifecycleManager . hashLoadingMap . set ( studioHash , hashLoadingPromise )
208
225
}
209
226
210
227
manifest = await hashLoadingPromise
228
+
229
+ debug ( 'Manifest: %o' , manifest )
211
230
} else {
212
231
studioPath = process . env . CYPRESS_LOCAL_STUDIO_PATH
213
232
studioHash = 'local'
@@ -226,10 +245,14 @@ export class StudioLifecycleManager {
226
245
const actualHash = crypto . createHash ( 'sha256' ) . update ( script ) . digest ( 'hex' )
227
246
228
247
if ( ! expectedHash ) {
248
+ debug ( 'Expected hash %s for studio server script not found in manifest: %o' , expectedHash , manifest )
249
+
229
250
throw new Error ( 'Expected hash for studio server script not found in manifest' )
230
251
}
231
252
232
253
if ( actualHash !== expectedHash ) {
254
+ debug ( 'Invalid hash for studio server script: %s !== %s' , actualHash , expectedHash )
255
+
233
256
throw new Error ( 'Invalid hash for studio server script' )
234
257
}
235
258
}
@@ -244,16 +267,15 @@ export class StudioLifecycleManager {
244
267
script,
245
268
studioPath,
246
269
studioHash,
247
- projectSlug : projectId ,
248
270
cloudApi : {
249
271
cloudUrl,
250
272
cloudHeaders,
251
273
CloudRequest,
252
274
isRetryableError,
253
275
asyncRetry,
254
276
} ,
255
- shouldEnableStudio : this . cloudStudioRequested ,
256
277
manifest,
278
+ getProjectOptions,
257
279
} )
258
280
259
281
telemetryManager . mark ( BUNDLE_LIFECYCLE_MARK_NAMES . STUDIO_MANAGER_SETUP_END )
@@ -270,7 +292,7 @@ export class StudioLifecycleManager {
270
292
telemetryManager . mark ( BUNDLE_LIFECYCLE_MARK_NAMES . STUDIO_PROTOCOL_PREPARE_START )
271
293
await protocolManager . prepareProtocol ( script , {
272
294
runId : 'studio' ,
273
- projectId : cfg . projectId ,
295
+ projectId : currentProjectOptions . projectSlug ,
274
296
testingType : cfg . testingType ,
275
297
cloudApi : {
276
298
url : routes . apiUrl ,
@@ -320,15 +342,15 @@ export class StudioLifecycleManager {
320
342
}
321
343
322
344
private setupWatcher ( {
323
- projectId,
324
345
cloudDataSource,
325
346
cfg,
326
347
debugData,
348
+ getProjectOptions,
327
349
} : {
328
- projectId ?: string
329
350
cloudDataSource : CloudDataSource
330
351
cfg : Cfg
331
352
debugData : any
353
+ getProjectOptions : StudioServerOptions [ 'getProjectOptions' ]
332
354
} ) {
333
355
// Don't setup a watcher if the studio bundle is NOT local
334
356
if ( ! process . env . CYPRESS_LOCAL_STUDIO_PATH ) {
@@ -348,10 +370,10 @@ export class StudioLifecycleManager {
348
370
await this . studioManager ?. destroy ( )
349
371
this . studioManager = undefined
350
372
this . studioManagerPromise = this . createStudioManager ( {
351
- projectId,
352
373
cloudDataSource,
353
374
cfg,
354
375
debugData,
376
+ getProjectOptions,
355
377
} ) . then ( ( studioManager ) => {
356
378
// eslint-disable-next-line no-console
357
379
console . log ( 'Studio manager reloaded' )
0 commit comments