Skip to content

Commit 4b00ffe

Browse files
authored
fix(cli): deployment errors are printed 3 times (#31389)
The CLI prints deployment errors 3 times. This is caused by an catching an error, printing it, and then throwing it again; to another `catch` statement that catches the error, prints it, and then throws it again. In this PR, get rid of one catch and change the error that gets rethrown in a different case. Also in this PR: fix the inconsistency of printing the progress of asset publishing. Compared to the progress of stack deployments, the stack name isn't bold and there is a single space offset. (A little work to change the printing, a LOT of work to get the integration+regression tests to pass, that all assert way too many specifics about the error messages that get printed to the screen) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent bcf9209 commit 4b00ffe

File tree

7 files changed

+37
-36
lines changed

7 files changed

+37
-36
lines changed

packages/@aws-cdk-testing/cli-integ/lib/with-cdk-app.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { shell, ShellOptions, ShellHelper, rimraf } from './shell';
1313
import { AwsContext, withAws } from './with-aws';
1414
import { withTimeout } from './with-timeout';
1515

16-
export const DEFAULT_TEST_TIMEOUT_S = 10 * 60;
16+
export const DEFAULT_TEST_TIMEOUT_S = 20 * 60;
1717
export const EXTENDED_TEST_TIMEOUT_S = 30 * 60;
1818

1919
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# This test asserts too much about the output of the CLI.
2+
security related changes without a CLI are expected to fail when approval is required

packages/@aws-cdk-testing/cli-integ/skip-tests.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# This file is empty on purpose. Leave it here as documentation
22
# and an example.
33
#
4-
# Copy this file to cli-regression-patches/vX.Y.Z/skip-tests.txt
4+
# Copy this file to resources/cli-regression-patches/vX.Y.Z/skip-tests.txt
55
# and edit it there if you want to exclude certain tests from running
66
# when performing a certain version's regression tests.
77
#

packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/cli-lib.integtest.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ integTest(
7575
'This deployment will make potentially sensitive changes according to your current security approval level',
7676
);
7777
expect(stdErr).toContain(
78-
'Deployment failed: Error: "--require-approval" is enabled and stack includes security-sensitive updates',
78+
'"--require-approval" is enabled and stack includes security-sensitive updates',
7979
);
8080

8181
// Ensure stack was not deployed

packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/cli.integtest.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import {
1919
import { InvokeCommand } from '@aws-sdk/client-lambda';
2020
import { CreateTopicCommand, DeleteTopicCommand } from '@aws-sdk/client-sns';
2121
import { AssumeRoleCommand, GetCallerIdentityCommand } from '@aws-sdk/client-sts';
22-
import * as chalk from 'chalk';
2322
import {
2423
integTest,
2524
cloneDirectory,
@@ -2198,8 +2197,7 @@ integTest(
21982197
allowErrExit: true,
21992198
});
22002199

2201-
const stackName = `${fixture.stackNamePrefix}-ecs-hotswap`;
2202-
const expectedSubstring = `❌ ${chalk.bold(stackName)} failed: ResourceNotReady: Resource is not in the state deploymentCompleted`;
2200+
const expectedSubstring = 'Resource is not in the state deploymentCompleted';
22032201

22042202
expect(deployOutput).toContain(expectedSubstring);
22052203
expect(deployOutput).not.toContain('hotswapped!');

packages/aws-cdk/lib/api/deployments.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as cxapi from '@aws-cdk/cx-api';
22
import * as cdk_assets from 'cdk-assets';
33
import { AssetManifest, IManifestEntry } from 'cdk-assets';
4+
import * as chalk from 'chalk';
45
import { Tag } from '../cdk-toolkit';
56
import { debug, warning, error } from '../logging';
67
import { Mode } from './aws-auth/credentials';
@@ -700,7 +701,7 @@ export class Deployments {
700701
if (existing) {
701702
return existing;
702703
}
703-
const prefix = stackName ? `${stackName}: ` : '';
704+
const prefix = stackName ? `${chalk.bold(stackName)}: ` : '';
704705
const publisher = new cdk_assets.AssetPublishing(assetManifest, {
705706
aws: new PublishingAws(this.sdkProvider, env),
706707
progressListener: new ParallelSafeAssetProgress(prefix, this.props.quiet ?? false),
@@ -719,7 +720,7 @@ class ParallelSafeAssetProgress implements cdk_assets.IPublishProgressListener {
719720

720721
public onPublishEvent(type: cdk_assets.EventType, event: cdk_assets.IPublishProgress): void {
721722
const handler = this.quiet && type !== 'fail' ? debug : EVENT_TO_LOGGER[type];
722-
handler(`${this.prefix} ${type}: ${event.message}`);
723+
handler(`${this.prefix}${type}: ${event.message}`);
723724
}
724725
}
725726

packages/aws-cdk/lib/cdk-toolkit.ts

+28-28
Original file line numberDiff line numberDiff line change
@@ -371,9 +371,14 @@ export class CdkToolkit {
371371
print('Stack ARN:');
372372

373373
data(result.stackArn);
374-
} catch (e) {
375-
error('\n ❌ %s failed: %s', chalk.bold(stack.displayName), e);
376-
throw e;
374+
} catch (e: any) {
375+
// It has to be exactly this string because an integration test tests for
376+
// "bold(stackname) failed: ResourceNotReady: <error>"
377+
throw new Error([
378+
`❌ ${chalk.bold(stack.stackName)} failed:`,
379+
...e.code ? [`${e.code}:`] : [],
380+
e.message,
381+
].join(' '));
377382
} finally {
378383
if (options.cloudWatchLogMonitor) {
379384
const foundLogGroupsResult = await findCloudWatchLogGroups(this.props.sdkProvider, stack);
@@ -401,33 +406,28 @@ export class CdkToolkit {
401406
warning('⚠️ The --concurrency flag only supports --progress "events". Switching to "events".');
402407
}
403408

404-
try {
405-
const stacksAndTheirAssetManifests = stacks.flatMap(stack => [
406-
stack,
407-
...stack.dependencies.filter(cxapi.AssetManifestArtifact.isAssetManifestArtifact),
408-
]);
409-
const workGraph = new WorkGraphBuilder(prebuildAssets).build(stacksAndTheirAssetManifests);
410-
411-
// Unless we are running with '--force', skip already published assets
412-
if (!options.force) {
413-
await this.removePublishedAssets(workGraph, options);
414-
}
409+
const stacksAndTheirAssetManifests = stacks.flatMap(stack => [
410+
stack,
411+
...stack.dependencies.filter(cxapi.AssetManifestArtifact.isAssetManifestArtifact),
412+
]);
413+
const workGraph = new WorkGraphBuilder(prebuildAssets).build(stacksAndTheirAssetManifests);
415414

416-
const graphConcurrency: Concurrency = {
417-
'stack': concurrency,
418-
'asset-build': 1, // This will be CPU-bound/memory bound, mostly matters for Docker builds
419-
'asset-publish': (options.assetParallelism ?? true) ? 8 : 1, // This will be I/O-bound, 8 in parallel seems reasonable
420-
};
421-
422-
await workGraph.doParallel(graphConcurrency, {
423-
deployStack,
424-
buildAsset,
425-
publishAsset,
426-
});
427-
} catch (e) {
428-
error('\n ❌ Deployment failed: %s', e);
429-
throw e;
415+
// Unless we are running with '--force', skip already published assets
416+
if (!options.force) {
417+
await this.removePublishedAssets(workGraph, options);
430418
}
419+
420+
const graphConcurrency: Concurrency = {
421+
'stack': concurrency,
422+
'asset-build': 1, // This will be CPU-bound/memory bound, mostly matters for Docker builds
423+
'asset-publish': (options.assetParallelism ?? true) ? 8 : 1, // This will be I/O-bound, 8 in parallel seems reasonable
424+
};
425+
426+
await workGraph.doParallel(graphConcurrency, {
427+
deployStack,
428+
buildAsset,
429+
publishAsset,
430+
});
431431
}
432432

433433
public async watch(options: WatchOptions) {

0 commit comments

Comments
 (0)