@@ -14,7 +14,7 @@ import { type RollbackOptions } from '../actions/rollback';
14
14
import { type SynthOptions } from '../actions/synth' ;
15
15
import { patternsArrayForWatch , WatchOptions } from '../actions/watch' ;
16
16
import { type SdkOptions } from '../api/aws-auth' ;
17
- import { DEFAULT_TOOLKIT_STACK_NAME , SdkProvider , SuccessfulDeployStackResult , StackCollection , Deployments , HotswapMode , StackActivityProgress , ResourceMigrator , obscureTemplate , serializeStructure , tagsForStack , CliIoHost , validateSnsTopicArn , Concurrency , WorkGraphBuilder , AssetBuildNode , AssetPublishNode , StackNode , formatErrorMessage , CloudWatchLogEventMonitor , findCloudWatchLogGroups } from '../api/aws-cdk' ;
17
+ import { DEFAULT_TOOLKIT_STACK_NAME , SdkProvider , SuccessfulDeployStackResult , StackCollection , Deployments , HotswapMode , StackActivityProgress , ResourceMigrator , obscureTemplate , serializeStructure , tagsForStack , CliIoHost , validateSnsTopicArn , Concurrency , WorkGraphBuilder , AssetBuildNode , AssetPublishNode , StackNode , formatErrorMessage , CloudWatchLogEventMonitor , findCloudWatchLogGroups , formatTime } from '../api/aws-cdk' ;
18
18
import { CachedCloudAssemblySource , IdentityCloudAssemblySource , StackAssembly , ICloudAssemblySource , StackSelectionStrategy } from '../api/cloud-assembly' ;
19
19
import { ALL_STACKS , CloudAssemblySourceBuilder } from '../api/cloud-assembly/private' ;
20
20
import { ToolkitError } from '../api/errors' ;
@@ -158,10 +158,12 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
158
158
*/
159
159
public async synth ( cx : ICloudAssemblySource , options : SynthOptions = { } ) : Promise < ICloudAssemblySource > {
160
160
const ioHost = withAction ( this . ioHost , 'synth' ) ;
161
+ const synthTimer = Timer . start ( ) ;
161
162
const assembly = await this . assemblyFromSource ( cx ) ;
162
163
const stacks = assembly . selectStacksV2 ( options . stacks ?? ALL_STACKS ) ;
163
164
const autoValidateStacks = options . validateStacks ? [ assembly . selectStacksForValidation ( ) ] : [ ] ;
164
165
await this . validateStacksMetadata ( stacks . concat ( ...autoValidateStacks ) , ioHost ) ;
166
+ await synthTimer . endAs ( ioHost , 'synth' ) ;
165
167
166
168
// if we have a single stack, print it to STDOUT
167
169
const message = `Successfully synthesized to ${ chalk . blue ( path . resolve ( stacks . assembly . directory ) ) } ` ;
@@ -175,7 +177,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
175
177
const firstStack = stacks . firstStack ! ;
176
178
const template = firstStack . template ;
177
179
const obscuredTemplate = obscureTemplate ( template ) ;
178
- await ioHost . notify ( result ( message , 'CDK_TOOLKIT_I0001 ' , {
180
+ await ioHost . notify ( result ( message , 'CDK_TOOLKIT_I1901 ' , {
179
181
...assemblyData ,
180
182
stack : {
181
183
stackName : firstStack . stackName ,
@@ -187,7 +189,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
187
189
} ) ) ;
188
190
} else {
189
191
// not outputting template to stdout, let's explain things to the user a little bit...
190
- await ioHost . notify ( result ( chalk . green ( message ) , 'CDK_TOOLKIT_I0002 ' , assemblyData ) ) ;
192
+ await ioHost . notify ( result ( chalk . green ( message ) , 'CDK_TOOLKIT_I1902 ' , assemblyData ) ) ;
191
193
await ioHost . notify ( info ( `Supply a stack id (${ stacks . stackArtifacts . map ( ( s ) => chalk . green ( s . hierarchicalId ) ) . join ( ', ' ) } ) to display its template.` ) ) ;
192
194
}
193
195
@@ -235,14 +237,10 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
235
237
*/
236
238
private async _deploy ( assembly : StackAssembly , action : 'deploy' | 'watch' , options : ExtendedDeployOptions = { } ) {
237
239
const ioHost = withAction ( this . ioHost , action ) ;
238
- const timer = Timer . start ( ) ;
240
+ const synthTimer = Timer . start ( ) ;
239
241
const stackCollection = assembly . selectStacksV2 ( options . stacks ?? ALL_STACKS ) ;
240
242
await this . validateStacksMetadata ( stackCollection , ioHost ) ;
241
-
242
- const synthTime = timer . end ( ) ;
243
- await ioHost . notify ( info ( `\n✨ Synthesis time: ${ synthTime . asSec } s\n` , 'CDK_TOOLKIT_I5001' , {
244
- time : synthTime . asMs ,
245
- } ) ) ;
243
+ const synthDuration = await synthTimer . endAs ( ioHost , 'synth' ) ;
246
244
247
245
if ( stackCollection . stackCount === 0 ) {
248
246
await ioHost . notify ( error ( 'This app contains no stacks' ) ) ;
@@ -352,14 +350,14 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
352
350
await ioHost . notify (
353
351
info ( `${ chalk . bold ( stack . displayName ) } : deploying... [${ stackIndex } /${ stackCollection . stackCount } ]` ) ,
354
352
) ;
355
- const startDeployTime = Timer . start ( ) ;
353
+ const deployTimer = Timer . start ( ) ;
356
354
357
355
let tags = options . tags ;
358
356
if ( ! tags || tags . length === 0 ) {
359
357
tags = tagsForStack ( stack ) ;
360
358
}
361
359
362
- let elapsedDeployTime ;
360
+ let deployDuration ;
363
361
try {
364
362
let deployResult : SuccessfulDeployStackResult | undefined ;
365
363
@@ -405,7 +403,6 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
405
403
if ( options . force ) {
406
404
await ioHost . notify ( warn ( `${ motivation } . Rolling back first (--force).` ) ) ;
407
405
} else {
408
- // @todo reintroduce concurrency and corked logging in CliHost
409
406
const confirmed = await ioHost . requestResponse ( confirm ( 'CDK_TOOLKIT_I5050' , question , motivation , true , concurrency ) ) ;
410
407
if ( ! confirmed ) { throw new ToolkitError ( 'Aborted by user' ) ; }
411
408
}
@@ -429,7 +426,6 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
429
426
if ( options . force ) {
430
427
await ioHost . notify ( warn ( `${ motivation } . Proceeding with regular deployment (--force).` ) ) ;
431
428
} else {
432
- // @todo reintroduce concurrency and corked logging in CliHost
433
429
const confirmed = await ioHost . requestResponse ( confirm ( 'CDK_TOOLKIT_I5050' , question , motivation , true , concurrency ) ) ;
434
430
if ( ! confirmed ) { throw new ToolkitError ( 'Aborted by user' ) ; }
435
431
}
@@ -448,24 +444,20 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
448
444
? ` ✅ ${ stack . displayName } (no changes)`
449
445
: ` ✅ ${ stack . displayName } ` ;
450
446
451
- await ioHost . notify ( success ( '\n' + message ) ) ;
452
- elapsedDeployTime = startDeployTime . end ( ) ;
453
- await ioHost . notify ( info ( `\n✨ Deployment time: ${ elapsedDeployTime . asSec } s\n` ) ) ;
447
+ await ioHost . notify ( result ( chalk . green ( '\n' + message ) , 'CDK_TOOLKIT_I5900' , deployResult ) ) ;
448
+ deployDuration = await deployTimer . endAs ( ioHost , 'deploy' ) ;
454
449
455
450
if ( Object . keys ( deployResult . outputs ) . length > 0 ) {
456
- await ioHost . notify ( info ( 'Outputs:' ) ) ;
457
-
451
+ const buffer = [ 'Outputs:' ] ;
458
452
stackOutputs [ stack . stackName ] = deployResult . outputs ;
459
- }
460
453
461
- for ( const name of Object . keys ( deployResult . outputs ) . sort ( ) ) {
462
- const value = deployResult . outputs [ name ] ;
463
- await ioHost . notify ( info ( `${ chalk . cyan ( stack . id ) } .${ chalk . cyan ( name ) } = ${ chalk . underline ( chalk . cyan ( value ) ) } ` ) ) ;
454
+ for ( const name of Object . keys ( deployResult . outputs ) . sort ( ) ) {
455
+ const value = deployResult . outputs [ name ] ;
456
+ buffer . push ( `${ chalk . cyan ( stack . id ) } .${ chalk . cyan ( name ) } = ${ chalk . underline ( chalk . cyan ( value ) ) } ` ) ;
457
+ }
458
+ await ioHost . notify ( info ( buffer . join ( '\n' ) ) ) ;
464
459
}
465
-
466
- await ioHost . notify ( info ( 'Stack ARN:' ) ) ;
467
-
468
- await ioHost . notify ( info ( deployResult . stackArn ) ) ;
460
+ await ioHost . notify ( info ( `Stack ARN:\n${ deployResult . stackArn } ` ) ) ;
469
461
} catch ( e : any ) {
470
462
// It has to be exactly this string because an integration test tests for
471
463
// "bold(stackname) failed: ResourceNotReady: <error>"
@@ -482,7 +474,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
482
474
foundLogGroupsResult . sdk ,
483
475
foundLogGroupsResult . logGroupNames ,
484
476
) ;
485
- await ioHost . notify ( info ( `The following log groups are added: ${ foundLogGroupsResult . logGroupNames } ` , 'CDK_TOOLKIT_I3001 ' ) ) ;
477
+ await ioHost . notify ( info ( `The following log groups are added: ${ foundLogGroupsResult . logGroupNames } ` , 'CDK_TOOLKIT_I5031 ' ) ) ;
486
478
}
487
479
488
480
// If an outputs file has been specified, create the file path and write stack outputs to it once.
@@ -496,7 +488,8 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
496
488
} ) ;
497
489
}
498
490
}
499
- await ioHost . notify ( info ( `\n✨ Total time: ${ synthTime . asSec + elapsedDeployTime . asSec } s\n` ) ) ;
491
+ const duration = synthDuration . asMs + ( deployDuration ?. asMs ?? 0 ) ;
492
+ await ioHost . notify ( info ( `\n✨ Total time: ${ formatTime ( duration ) } s\n` , 'CDK_TOOLKIT_I5001' , { duration } ) ) ;
500
493
} ;
501
494
502
495
const assetBuildTime = options . assetBuildTime ?? AssetBuildTime . ALL_BEFORE_DEPLOY ;
@@ -649,13 +642,10 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
649
642
*/
650
643
private async _rollback ( assembly : StackAssembly , action : 'rollback' | 'deploy' | 'watch' , options : RollbackOptions ) : Promise < void > {
651
644
const ioHost = withAction ( this . ioHost , action ) ;
652
- const timer = Timer . start ( ) ;
645
+ const synthTimer = Timer . start ( ) ;
653
646
const stacks = assembly . selectStacksV2 ( options . stacks ) ;
654
647
await this . validateStacksMetadata ( stacks , ioHost ) ;
655
- const synthTime = timer . end ( ) ;
656
- await ioHost . notify ( info ( `\n✨ Synthesis time: ${ synthTime . asSec } s\n` , 'CDK_TOOLKIT_I5001' , {
657
- time : synthTime . asMs ,
658
- } ) ) ;
648
+ await synthTimer . endAs ( ioHost , 'synth' ) ;
659
649
660
650
if ( stacks . stackCount === 0 ) {
661
651
await ioHost . notify ( error ( 'No stacks selected' ) ) ;
@@ -666,7 +656,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
666
656
667
657
for ( const stack of stacks . stackArtifacts ) {
668
658
await ioHost . notify ( info ( `Rolling back ${ chalk . bold ( stack . displayName ) } ` ) ) ;
669
- const startRollbackTime = Timer . start ( ) ;
659
+ const rollbackTimer = Timer . start ( ) ;
670
660
const deployments = await this . deploymentsForAction ( 'rollback' ) ;
671
661
try {
672
662
const stackResult = await deployments . rollbackStack ( {
@@ -680,8 +670,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
680
670
if ( ! stackResult . notInRollbackableState ) {
681
671
anyRollbackable = true ;
682
672
}
683
- const elapsedRollbackTime = startRollbackTime . end ( ) ;
684
- await ioHost . notify ( info ( `\n✨ Rollback time: ${ elapsedRollbackTime . asSec } s\n` ) ) ;
673
+ await rollbackTimer . endAs ( ioHost , 'rollback' ) ;
685
674
} catch ( e : any ) {
686
675
await ioHost . notify ( error ( `\n ❌ ${ chalk . bold ( stack . displayName ) } failed: ${ formatErrorMessage ( e ) } ` ) ) ;
687
676
throw new ToolkitError ( 'Rollback failed (use --force to orphan failing resources)' ) ;
@@ -707,8 +696,10 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
707
696
*/
708
697
private async _destroy ( assembly : StackAssembly , action : 'deploy' | 'destroy' , options : DestroyOptions ) : Promise < void > {
709
698
const ioHost = withAction ( this . ioHost , action ) ;
699
+ const synthTimer = Timer . start ( ) ;
710
700
// The stacks will have been ordered for deployment, so reverse them for deletion.
711
701
const stacks = await assembly . selectStacksV2 ( options . stacks ) . reversed ( ) ;
702
+ await synthTimer . endAs ( ioHost , 'synth' ) ;
712
703
713
704
const motivation = 'Destroying stacks is an irreversible action' ;
714
705
const question = `Are you sure you want to delete: ${ chalk . red ( stacks . hierarchicalIds . join ( ', ' ) ) } ` ;
@@ -717,21 +708,26 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
717
708
return ioHost . notify ( error ( 'Aborted by user' ) ) ;
718
709
}
719
710
720
- for ( const [ index , stack ] of stacks . stackArtifacts . entries ( ) ) {
721
- await ioHost . notify ( success ( `${ chalk . blue ( stack . displayName ) } : destroying... [${ index + 1 } /${ stacks . stackCount } ]` ) ) ;
722
- try {
723
- const deployments = await this . deploymentsForAction ( action ) ;
724
- await deployments . destroyStack ( {
725
- stack,
726
- deployName : stack . stackName ,
727
- roleArn : options . roleArn ,
728
- ci : options . ci ,
729
- } ) ;
730
- await ioHost . notify ( success ( `\n ✅ ${ chalk . blue ( stack . displayName ) } : ${ action } ed` ) ) ;
731
- } catch ( e ) {
732
- await ioHost . notify ( error ( `\n ❌ ${ chalk . blue ( stack . displayName ) } : ${ action } failed ${ e } ` ) ) ;
733
- throw e ;
711
+ const destroyTimer = Timer . start ( ) ;
712
+ try {
713
+ for ( const [ index , stack ] of stacks . stackArtifacts . entries ( ) ) {
714
+ await ioHost . notify ( success ( `${ chalk . blue ( stack . displayName ) } : destroying... [${ index + 1 } /${ stacks . stackCount } ]` ) ) ;
715
+ try {
716
+ const deployments = await this . deploymentsForAction ( action ) ;
717
+ await deployments . destroyStack ( {
718
+ stack,
719
+ deployName : stack . stackName ,
720
+ roleArn : options . roleArn ,
721
+ ci : options . ci ,
722
+ } ) ;
723
+ await ioHost . notify ( success ( `\n ✅ ${ chalk . blue ( stack . displayName ) } : ${ action } ed` ) ) ;
724
+ } catch ( e ) {
725
+ await ioHost . notify ( error ( `\n ❌ ${ chalk . blue ( stack . displayName ) } : ${ action } failed ${ e } ` ) ) ;
726
+ throw e ;
727
+ }
734
728
}
729
+ } finally {
730
+ await destroyTimer . endAs ( ioHost , 'destroy' ) ;
735
731
}
736
732
}
737
733
0 commit comments