@@ -43,9 +43,12 @@ interface FlagOperationsParams {
43
43
unconfigured ?: boolean ;
44
44
safe ?: boolean ;
45
45
concurrency ?: number ;
46
+
47
+ /** User provided --app option */
48
+ app ?: string ;
46
49
}
47
50
48
- export async function handleFlags ( flagData : FeatureFlag [ ] , ioHelper : IoHelper , options : FlagsOptions , toolkit : Toolkit ) {
51
+ export async function handleFlags ( flagData : FeatureFlag [ ] , ioHelper : IoHelper , options : FlagsOptions & { app ?: string } , toolkit : Toolkit ) {
49
52
flagData = flagData . filter ( flag => ! OBSOLETE_FLAGS . includes ( flag . name ) ) ;
50
53
51
54
if ( flagData . length == 0 ) {
@@ -65,6 +68,7 @@ export async function handleFlags(flagData: FeatureFlag[], ioHelper: IoHelper, o
65
68
unconfigured : options . unconfigured ,
66
69
safe : options . safe ,
67
70
concurrency : options . concurrency ,
71
+ app : options . app ,
68
72
} ;
69
73
70
74
const interactiveOptions = Object . values ( FlagsMenuOptions ) ;
@@ -249,41 +253,82 @@ async function setFlag(params: FlagOperationsParams, interactive?: boolean) {
249
253
}
250
254
}
251
255
252
- async function testFlagSafety (
253
- flag : FeatureFlag ,
256
+ async function setSafeFlags ( params : FlagOperationsParams ) : Promise < void > {
257
+ const { flagData, toolkit, ioHelper, concurrency, app : appOption } = params ;
258
+
259
+ const cdkJson = await JSON . parse ( await fs . readFile ( path . join ( process . cwd ( ) , 'cdk.json' ) , 'utf-8' ) ) ;
260
+ const app = appOption || cdkJson . app ;
261
+
262
+ const isUsingTsNode = app . includes ( 'ts-node' ) ;
263
+
264
+ if ( isUsingTsNode && ! app . includes ( '-T' ) && ! app . includes ( '--transpileOnly' ) ) {
265
+ await ioHelper . defaults . info ( 'Repeated synths with ts-node will type-check the application on every synth. Add --transpileOnly to cdk.json\'s "app" command to make this operation faster.' ) ;
266
+ }
267
+
268
+ const unconfiguredFlags = flagData . filter ( flag =>
269
+ flag . userValue === undefined &&
270
+ isBooleanFlag ( flag ) &&
271
+ ( flag . unconfiguredBehavesLike ?. v2 !== flag . recommendedValue ) ,
272
+ ) ;
273
+
274
+ if ( unconfiguredFlags . length === 0 ) {
275
+ await ioHelper . defaults . info ( 'No unconfigured feature flags found.' ) ;
276
+ return ;
277
+ }
278
+
279
+ const baseContext = new CdkAppMultiContext ( process . cwd ( ) ) ;
280
+ const baseContextValues = await baseContext . read ( ) ;
281
+
282
+ const baseSource = await toolkit . fromCdkApp ( app , {
283
+ contextStore : baseContext ,
284
+ outdir : path . join ( process . cwd ( ) , 'baseline' ) ,
285
+ } ) ;
286
+
287
+ const baseCx = await toolkit . synth ( baseSource ) ;
288
+ const baseAssembly = baseCx . cloudAssembly ;
289
+ const allStacks = baseAssembly . stacksRecursively ;
290
+
291
+ const queue = new PQueue ( { concurrency : concurrency } ) ;
292
+
293
+ const safeFlags = await batchTestFlags ( unconfiguredFlags , baseContextValues , toolkit , app , allStacks , queue ) ;
294
+
295
+ await fs . remove ( path . join ( process . cwd ( ) , 'baseline' ) ) ;
296
+
297
+ if ( safeFlags . length > 0 ) {
298
+ await ioHelper . defaults . info ( 'Safe flags that can be set without template changes:' ) ;
299
+ for ( const flag of safeFlags ) {
300
+ await ioHelper . defaults . info ( `- ${ flag . name } -> ${ flag . recommendedValue } ` ) ;
301
+ }
302
+
303
+ await handleUserResponse ( params , safeFlags . map ( flag => flag . name ) ) ;
304
+ } else {
305
+ await ioHelper . defaults . info ( 'No flags can be safely set without causing template changes.' ) ;
306
+ }
307
+ }
308
+
309
+ async function batchTestFlags (
310
+ flags : FeatureFlag [ ] ,
254
311
baseContextValues : Record < string , any > ,
255
312
toolkit : Toolkit ,
256
313
app : string ,
257
314
allStacks : any [ ] ,
258
- ) : Promise < boolean > {
259
- const testContext = new MemoryContext ( baseContextValues ) ;
260
- const newValue = toBooleanValue ( flag . recommendedValue ) ;
261
- await testContext . update ( { [ flag . name ] : newValue } ) ;
315
+ queue : PQueue ,
316
+ ) : Promise < FeatureFlag [ ] > {
317
+ if ( flags . length === 0 ) {
318
+ return [ ] ;
319
+ }
262
320
263
- const testSource = await toolkit . fromCdkApp ( app , {
264
- contextStore : testContext ,
265
- outdir : path . join ( process . cwd ( ) , `test- ${ flag . name } ` ) ,
321
+ const allFlagsContext = { ... baseContextValues } ;
322
+ flags . forEach ( flag => {
323
+ allFlagsContext [ flag . name ] = toBooleanValue ( flag . recommendedValue ) ;
266
324
} ) ;
267
325
268
- const testCx = await toolkit . synth ( testSource ) ;
269
-
270
- for ( const stack of allStacks ) {
271
- const templatePath = stack . templateFullPath ;
272
- const diff = await toolkit . diff ( testCx , {
273
- method : DiffMethod . LocalFile ( templatePath ) ,
274
- stacks : {
275
- strategy : StackSelectionStrategy . PATTERN_MUST_MATCH_SINGLE ,
276
- patterns : [ stack . hierarchicalId ] ,
277
- } ,
278
- } ) ;
279
-
280
- for ( const stackDiff of Object . values ( diff ) ) {
281
- if ( stackDiff . differenceCount > 0 ) {
282
- return false ;
283
- }
284
- }
326
+ const allSafe = await testBatch ( allFlagsContext , toolkit , app , allStacks , 'batch-all' ) ;
327
+ if ( allSafe ) {
328
+ return flags ;
285
329
}
286
- return true ;
330
+
331
+ return isolateUnsafeFlags ( flags , baseContextValues , toolkit , app , allStacks , queue ) ;
287
332
}
288
333
289
334
async function testBatch (
@@ -373,81 +418,41 @@ async function isolateUnsafeFlags(
373
418
return safeFlags ;
374
419
}
375
420
376
- async function batchTestFlags (
377
- flags : FeatureFlag [ ] ,
421
+ async function testFlagSafety (
422
+ flag : FeatureFlag ,
378
423
baseContextValues : Record < string , any > ,
379
424
toolkit : Toolkit ,
380
425
app : string ,
381
426
allStacks : any [ ] ,
382
- queue : PQueue ,
383
- ) : Promise < FeatureFlag [ ] > {
384
- if ( flags . length === 0 ) {
385
- return [ ] ;
386
- }
387
-
388
- const allFlagsContext = { ...baseContextValues } ;
389
- flags . forEach ( flag => {
390
- allFlagsContext [ flag . name ] = toBooleanValue ( flag . recommendedValue ) ;
391
- } ) ;
392
-
393
- const allSafe = await testBatch ( allFlagsContext , toolkit , app , allStacks , 'batch-all' ) ;
394
- if ( allSafe ) {
395
- return flags ;
396
- }
397
-
398
- return isolateUnsafeFlags ( flags , baseContextValues , toolkit , app , allStacks , queue ) ;
399
- }
400
-
401
- async function setSafeFlags ( params : FlagOperationsParams ) : Promise < void > {
402
- const { flagData, toolkit, ioHelper, concurrency } = params ;
403
-
404
- const cdkJson = await JSON . parse ( await fs . readFile ( path . join ( process . cwd ( ) , 'cdk.json' ) , 'utf-8' ) ) ;
405
- const app = cdkJson . app ;
406
-
407
- const isUsingTsNode = app . includes ( 'ts-node' ) ;
408
- if ( isUsingTsNode && ! app . includes ( '-T' ) && ! app . includes ( '--transpileOnly' ) ) {
409
- await ioHelper . defaults . info ( 'You are currently running with ts-node. Adding --transpileOnly may make this operation faster.' ) ;
410
- }
411
-
412
- const unconfiguredFlags = flagData . filter ( flag =>
413
- flag . userValue === undefined &&
414
- isBooleanFlag ( flag ) &&
415
- ( flag . unconfiguredBehavesLike ?. v2 !== flag . recommendedValue ) ,
416
- ) ;
417
-
418
- if ( unconfiguredFlags . length === 0 ) {
419
- await ioHelper . defaults . info ( 'No unconfigured feature flags found.' ) ;
420
- return ;
421
- }
422
-
423
- const baseContext = new CdkAppMultiContext ( process . cwd ( ) ) ;
424
- const baseContextValues = await baseContext . read ( ) ;
427
+ ) : Promise < boolean > {
428
+ const testContext = new MemoryContext ( baseContextValues ) ;
429
+ const newValue = toBooleanValue ( flag . recommendedValue ) ;
430
+ await testContext . update ( { [ flag . name ] : newValue } ) ;
425
431
426
- const baseSource = await toolkit . fromCdkApp ( app , {
427
- contextStore : baseContext ,
428
- outdir : path . join ( process . cwd ( ) , 'baseline' ) ,
432
+ const testSource = await toolkit . fromCdkApp ( app , {
433
+ contextStore : testContext ,
434
+ outdir : path . join ( process . cwd ( ) , `test- ${ flag . name } ` ) ,
429
435
} ) ;
430
436
431
- const baseCx = await toolkit . synth ( baseSource ) ;
432
- const baseAssembly = baseCx . cloudAssembly ;
433
- const allStacks = baseAssembly . stacksRecursively ;
434
-
435
- const queue = new PQueue ( { concurrency : concurrency } ) ;
436
-
437
- const safeFlags = await batchTestFlags ( unconfiguredFlags , baseContextValues , toolkit , app , allStacks , queue ) ;
437
+ const testCx = await toolkit . synth ( testSource ) ;
438
438
439
- await fs . remove ( path . join ( process . cwd ( ) , 'baseline' ) ) ;
439
+ for ( const stack of allStacks ) {
440
+ const templatePath = stack . templateFullPath ;
441
+ const diff = await toolkit . diff ( testCx , {
442
+ method : DiffMethod . LocalFile ( templatePath ) ,
443
+ stacks : {
444
+ strategy : StackSelectionStrategy . PATTERN_MUST_MATCH_SINGLE ,
445
+ patterns : [ stack . hierarchicalId ] ,
446
+ } ,
447
+ } ) ;
440
448
441
- if ( safeFlags . length > 0 ) {
442
- await ioHelper . defaults . info ( 'Safe flags that can be set without template changes:' ) ;
443
- for ( const flag of safeFlags ) {
444
- await ioHelper . defaults . info ( `- ${ flag . name } -> ${ flag . recommendedValue } ` ) ;
449
+ for ( const stackDiff of Object . values ( diff ) ) {
450
+ if ( stackDiff . differenceCount > 0 ) {
451
+ return false ;
452
+ }
445
453
}
446
-
447
- await handleUserResponse ( params , safeFlags . map ( flag => flag . name ) ) ;
448
- } else {
449
- await ioHelper . defaults . info ( 'No flags can be safely set without causing template changes.' ) ;
450
454
}
455
+ return true ;
451
456
}
452
457
453
458
async function prototypeChanges (
0 commit comments