From 7b68908dbebde02037d8b7650f4bb10d4c5db89d Mon Sep 17 00:00:00 2001 From: Rico Hermans Date: Fri, 10 Jan 2025 20:46:08 +0100 Subject: [PATCH] fix(cli): "no stack found in the main cloud assembly" (#32839) Reverts aws/aws-cdk#32636 It leads to a problem in case of applications where there are no top-level stacks, but only stacks in nested stages: #32836. Closes #32836. --- .../tests/cli-integ-tests/cli.integtest.ts | 14 ---- packages/aws-cdk/lib/cdk-toolkit.ts | 54 ---------------- packages/aws-cdk/test/cdk-toolkit.test.ts | 64 ------------------- 3 files changed, 132 deletions(-) diff --git a/packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/cli.integtest.ts b/packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/cli.integtest.ts index 34526ece88265..f4736584a065f 100644 --- a/packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/cli.integtest.ts +++ b/packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/cli.integtest.ts @@ -2634,20 +2634,6 @@ integTest('hotswap ECS deployment respects properties override', withDefaultFixt expect(describeServicesResponse.services?.[0].deploymentConfiguration?.maximumPercent).toEqual(ecsMaximumHealthyPercent); })); -integTest('cdk destroy does not fail even if the stacks do not exist', withDefaultFixture(async (fixture) => { - const nonExistingStackName1 = 'non-existing-stack-1'; - const nonExistingStackName2 = 'non-existing-stack-2'; - - await expect(fixture.cdkDestroy([nonExistingStackName1, nonExistingStackName2])).resolves.not.toThrow(); -})); - -integTest('cdk destroy with no force option exits without prompt if the stacks do not exist', withDefaultFixture(async (fixture) => { - const nonExistingStackName1 = 'non-existing-stack-1'; - const nonExistingStackName2 = 'non-existing-stack-2'; - - await expect(fixture.cdk(['destroy', ...fixture.fullStackName([nonExistingStackName1, nonExistingStackName2])])).resolves.not.toThrow(); -})); - async function listChildren(parent: string, pred: (x: string) => Promise) { const ret = new Array(); for (const child of await fs.readdir(parent, { encoding: 'utf-8' })) { diff --git a/packages/aws-cdk/lib/cdk-toolkit.ts b/packages/aws-cdk/lib/cdk-toolkit.ts index 64fa955912e4d..32f6616e5cdb6 100644 --- a/packages/aws-cdk/lib/cdk-toolkit.ts +++ b/packages/aws-cdk/lib/cdk-toolkit.ts @@ -4,7 +4,6 @@ import * as cxapi from '@aws-cdk/cx-api'; import * as chalk from 'chalk'; import * as chokidar from 'chokidar'; import * as fs from 'fs-extra'; -import { minimatch } from 'minimatch'; import * as promptly from 'promptly'; import * as uuid from 'uuid'; import { DeploymentMethod, SuccessfulDeployStackResult } from './api'; @@ -800,16 +799,6 @@ export class CdkToolkit { public async destroy(options: DestroyOptions) { let stacks = await this.selectStacksForDestroy(options.selector, options.exclusively); - await this.suggestStacks({ - selector: options.selector, - stacks, - exclusively: options.exclusively, - }); - if (stacks.stackArtifacts.length === 0) { - warning(`No stacks match the name(s): ${chalk.red(options.selector.patterns.join(', '))}`); - return; - } - // The stacks will have been ordered for deployment, so reverse them for deletion. stacks = stacks.reversed(); @@ -1173,49 +1162,6 @@ export class CdkToolkit { return stacks; } - private async suggestStacks(props: { - selector: StackSelector; - stacks: StackCollection; - exclusively?: boolean; - }) { - const assembly = await this.assembly(); - const selectorWithoutPatterns: StackSelector = { - ...props.selector, - allTopLevel: true, - patterns: [], - }; - const stacksWithoutPatterns = await assembly.selectStacks(selectorWithoutPatterns, { - extend: props.exclusively ? ExtendedStackSelection.None : ExtendedStackSelection.Downstream, - defaultBehavior: DefaultSelection.OnlySingle, - }); - - const patterns = props.selector.patterns.map(pattern => { - const notExist = !props.stacks.stackArtifacts.find(stack => - minimatch(stack.hierarchicalId, pattern), - ); - - const closelyMatched = notExist ? stacksWithoutPatterns.stackArtifacts.map(stack => { - if (minimatch(stack.hierarchicalId.toLowerCase(), pattern.toLowerCase())) { - return stack.hierarchicalId; - } - return; - }).filter((stack): stack is string => stack !== undefined) : []; - - return { - pattern, - notExist, - closelyMatched, - }; - }); - - for (const pattern of patterns) { - if (pattern.notExist) { - const closelyMatched = pattern.closelyMatched.length > 0 ? ` Do you mean ${chalk.blue(pattern.closelyMatched.join(', '))}?` : ''; - warning(`${chalk.red(pattern.pattern)} does not exist.${closelyMatched}`); - } - }; - } - /** * Validate the stacks for errors and warnings according to the CLI's current settings */ diff --git a/packages/aws-cdk/test/cdk-toolkit.test.ts b/packages/aws-cdk/test/cdk-toolkit.test.ts index 5ae3a2f9a45ae..8dad7142baea7 100644 --- a/packages/aws-cdk/test/cdk-toolkit.test.ts +++ b/packages/aws-cdk/test/cdk-toolkit.test.ts @@ -953,70 +953,6 @@ describe('destroy', () => { }); }).resolves; }); - - test('does not throw and warns if there are only non-existent stacks', async () => { - const toolkit = defaultToolkitSetup(); - - await toolkit.destroy({ - selector: { patterns: ['Test-Stack-X', 'Test-Stack-Y'] }, - exclusively: true, - force: true, - fromDeploy: true, - }); - - expect(flatten(stderrMock.mock.calls)).toEqual( - expect.arrayContaining([ - expect.stringMatching(/Test-Stack-X does not exist./), - expect.stringMatching(/Test-Stack-Y does not exist./), - expect.stringMatching(/No stacks match the name\(s\): Test-Stack-X, Test-Stack-Y/), - ]), - ); - }); - - test('does not throw and warns if there is a non-existent stack and the other exists', async () => { - const toolkit = defaultToolkitSetup(); - - await toolkit.destroy({ - selector: { patterns: ['Test-Stack-X', 'Test-Stack-B'] }, - exclusively: true, - force: true, - fromDeploy: true, - }); - - expect(flatten(stderrMock.mock.calls)).toEqual( - expect.arrayContaining([ - expect.stringMatching(/Test-Stack-X does not exist./), - ]), - ); - expect(flatten(stderrMock.mock.calls)).not.toEqual( - expect.arrayContaining([ - expect.stringMatching(/Test-Stack-B does not exist./), - ]), - ); - expect(flatten(stderrMock.mock.calls)).not.toEqual( - expect.arrayContaining([ - expect.stringMatching(/No stacks match the name\(s\)/), - ]), - ); - }); - - test('does not throw and suggests valid names if there is a non-existent but closely matching stack', async () => { - const toolkit = defaultToolkitSetup(); - - await toolkit.destroy({ - selector: { patterns: ['test-stack-b'] }, - exclusively: true, - force: true, - fromDeploy: true, - }); - - expect(flatten(stderrMock.mock.calls)).toEqual( - expect.arrayContaining([ - expect.stringMatching(/test-stack-b does not exist. Do you mean Test-Stack-B?/), - expect.stringMatching(/No stacks match the name\(s\): test-stack-b/), - ]), - ); - }); }); describe('watch', () => {