diff --git a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/schema.ts b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/schema.ts index 4ac55eccf..e593328e9 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/schema.ts +++ b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/schema.ts @@ -100,7 +100,12 @@ export interface ArtifactManifest { readonly properties?: ArtifactProperties; /** - * A string that represents this artifact. Should only be used in user interfaces. + * A string that can be shown to a user to uniquely identify this artifact inside a cloud assembly tree + * + * Is used by the CLI to present a list of stacks to the user in a way that + * makes sense to them. Even though the property name "display name" doesn't + * imply it, this field is used to select stacks as well, so all stacks should + * have a unique display name. * * @default - no display name */ diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json index 06d442784..855b0ba0b 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json @@ -83,7 +83,7 @@ ] }, "displayName": { - "description": "A string that represents this artifact. Should only be used in user interfaces. (Default - no display name)", + "description": "A string that can be shown to a user to uniquely identify this artifact inside a cloud assembly tree\n\nIs used by the CLI to present a list of stacks to the user in a way that\nmakes sense to them. Even though the property name \"display name\" doesn't\nimply it, this field is used to select stacks as well, so all stacks should\nhave a unique display name. (Default - no display name)", "type": "string" } }, diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/version.json b/packages/@aws-cdk/cloud-assembly-schema/schema/version.json index 8e791e665..8caae8e30 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/version.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/version.json @@ -1,4 +1,4 @@ { - "schemaHash": "78936b0f9299bbe47497dd77f8065d71e65d8d739a0413ad7698ad03b22ef83e", + "schemaHash": "96958a4c40e0a00e850f0c14dd6e9c0fc8db0b075f1f155cea41ab198c0514be", "revision": 43 } \ No newline at end of file diff --git a/packages/@aws-cdk/toolkit-lib/lib/toolkit/index.ts b/packages/@aws-cdk/toolkit-lib/lib/toolkit/index.ts index a34eb3b9c..c450e67f2 100644 --- a/packages/@aws-cdk/toolkit-lib/lib/toolkit/index.ts +++ b/packages/@aws-cdk/toolkit-lib/lib/toolkit/index.ts @@ -1,2 +1,4 @@ export * from './toolkit'; export * from './non-interactive-io-host'; +export * from './types'; +export * from '../api/shared-public'; diff --git a/packages/@aws-cdk/toolkit-lib/lib/toolkit/toolkit.ts b/packages/@aws-cdk/toolkit-lib/lib/toolkit/toolkit.ts index 9ecf71f45..59fb5df19 100644 --- a/packages/@aws-cdk/toolkit-lib/lib/toolkit/toolkit.ts +++ b/packages/@aws-cdk/toolkit-lib/lib/toolkit/toolkit.ts @@ -6,6 +6,7 @@ import * as fs from 'fs-extra'; import { NonInteractiveIoHost } from './non-interactive-io-host'; import type { ToolkitServices } from './private'; import { assemblyFromSource } from './private'; +import type { DeployResult } from './types'; import type { BootstrapEnvironments, BootstrapOptions, BootstrapResult, EnvironmentBootstrapResult } from '../actions/bootstrap'; import { BootstrapSource } from '../actions/bootstrap'; import { AssetBuildTime, type DeployOptions } from '../actions/deploy'; @@ -322,7 +323,7 @@ export class Toolkit extends CloudAssemblySourceBuilder { * * Deploys the selected stacks into an AWS account */ - public async deploy(cx: ICloudAssemblySource, options: DeployOptions = {}): Promise { + public async deploy(cx: ICloudAssemblySource, options: DeployOptions = {}): Promise { const ioHelper = asIoHelper(this.ioHost, 'deploy'); const assembly = await assemblyFromSource(ioHelper, cx); return this._deploy(assembly, 'deploy', options); @@ -331,7 +332,7 @@ export class Toolkit extends CloudAssemblySourceBuilder { /** * Helper to allow deploy being called as part of the watch action. */ - private async _deploy(assembly: StackAssembly, action: 'deploy' | 'watch', options: ExtendedDeployOptions = {}) { + private async _deploy(assembly: StackAssembly, action: 'deploy' | 'watch', options: ExtendedDeployOptions = {}): Promise { const ioHelper = asIoHelper(this.ioHost, action); const selectStacks = options.stacks ?? ALL_STACKS; const synthSpan = await ioHelper.span(SPAN.SYNTH_ASSEMBLY).begin({ stacks: selectStacks }); @@ -339,9 +340,13 @@ export class Toolkit extends CloudAssemblySourceBuilder { await this.validateStacksMetadata(stackCollection, ioHelper); const synthDuration = await synthSpan.end(); + const ret: DeployResult = { + stacks: [], + }; + if (stackCollection.stackCount === 0) { await ioHelper.notify(IO.CDK_TOOLKIT_E5001.msg('This app contains no stacks')); - return; + return ret; } const deployments = await this.deploymentsForAction('deploy'); @@ -564,6 +569,17 @@ export class Toolkit extends CloudAssemblySourceBuilder { await ioHelper.notify(IO.CDK_TOOLKIT_I5901.msg(buffer.join('\n'))); } await ioHelper.notify(IO.CDK_TOOLKIT_I5901.msg(`Stack ARN:\n${deployResult.stackArn}`)); + + ret.stacks.push({ + stackName: stack.stackName, + environment: { + account: stack.environment.account, + region: stack.environment.region, + }, + stackArn: deployResult.stackArn, + outputs: deployResult.outputs, + hierarchicalId: stack.hierarchicalId, + }); } catch (e: any) { // It has to be exactly this string because an integration test tests for // "bold(stackname) failed: ResourceNotReady: " @@ -624,6 +640,8 @@ export class Toolkit extends CloudAssemblySourceBuilder { buildAsset, publishAsset, }); + + return ret; } /** diff --git a/packages/@aws-cdk/toolkit-lib/lib/toolkit/types.ts b/packages/@aws-cdk/toolkit-lib/lib/toolkit/types.ts new file mode 100644 index 000000000..732a1ef7a --- /dev/null +++ b/packages/@aws-cdk/toolkit-lib/lib/toolkit/types.ts @@ -0,0 +1,130 @@ +/** + * Result interface for toolkit.deploy operation + */ +export interface DeployResult { + /** + * Map of deployed stacks by artifact ID. + */ + readonly stacks: DeployedStack[]; +} + +/** + * Information about a deployed stack + */ +export interface DeployedStack { + /** + * The name of the deployed stack + * + * A stack name is unique inside its environment, but not unique globally. + */ + readonly stackName: string; + + /** + * The environment where the stack was deployed + * + * This environment is always concrete, because even though the CDK app's + * stack may be region-agnostic, in order to be deployed it will have to have + * been specialized. + */ + readonly environment: Environment; + + /** + * Hierarchical identifier + * + * This uniquely identifies the stack inside the CDK app. + * + * In practice this will be the stack's construct path, but unfortunately the + * Cloud Assembly contract doesn't require or guarantee that. + */ + readonly hierarchicalId: string; + + /** + * The ARN of the deployed stack + */ + readonly stackArn: string; + + /** + * The outputs of the deployed CloudFormation stack + */ + readonly outputs: { [key: string]: string }; +} + +/** + * An environment, which is an (account, region) pair + */ +export interface Environment { + /** + * The account number + */ + readonly account: string; + + /** + * The region number + */ + readonly region: string; +} +/** + * Result interface for toolkit.deploy operation + */ +export interface DeployResult { + /** + * List of stacks deployed by this operation + */ + readonly stacks: DeployedStack[]; +} + +/** + * Information about a deployed stack + */ +export interface DeployedStack { + /** + * The name of the deployed stack + * + * A stack name is unique inside its environment, but not unique globally. + */ + readonly stackName: string; + + /** + * The environment where the stack was deployed + * + * This environment is always concrete, because even though the CDK app's + * stack may be region-agnostic, in order to be deployed it will have to have + * been specialized. + */ + readonly environment: Environment; + + /** + * Hierarchical identifier + * + * This uniquely identifies the stack inside the CDK app. + * + * In practice this will be the stack's construct path, but unfortunately the + * Cloud Assembly contract doesn't require or guarantee that. + */ + readonly hierarchicalId: string; + + /** + * The ARN of the deployed stack + */ + readonly stackArn: string; + + /** + * The outputs of the deployed CloudFormation stack + */ + readonly outputs: { [key: string]: string }; +} + +/** + * An environment, which is an (account, region) pair + */ +export interface Environment { + /** + * The account number + */ + readonly account: string; + + /** + * The region number + */ + readonly region: string; +} diff --git a/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/app.ts b/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/app.ts new file mode 100644 index 000000000..cc7b2e03c --- /dev/null +++ b/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/app.ts @@ -0,0 +1,7 @@ +import * as cdk from 'aws-cdk-lib/core'; + +const app = new cdk.App({ autoSynth: false }); +const stage = new cdk.Stage(app, 'Stage'); +new cdk.Stack(stage, 'Stack1'); + +app.synth(); diff --git a/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.json b/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.json new file mode 100644 index 000000000..b49bb19e0 --- /dev/null +++ b/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.json @@ -0,0 +1,3 @@ +{ + "app": "$(npx -c 'which tsx') app.ts" +} diff --git a/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.out/assembly-Stage/StageStack130339B27.assets.json b/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.out/assembly-Stage/StageStack130339B27.assets.json new file mode 100644 index 000000000..4028f38b9 --- /dev/null +++ b/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.out/assembly-Stage/StageStack130339B27.assets.json @@ -0,0 +1,19 @@ +{ + "version": "40.0.0", + "files": { + "7f4fb64a3afca08edbdbaa369e00317cb253697278406a83c78e849e89045f9d": { + "source": { + "path": "StageStack130339B27.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "7f4fb64a3afca08edbdbaa369e00317cb253697278406a83c78e849e89045f9d.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.out/assembly-Stage/StageStack130339B27.template.json b/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.out/assembly-Stage/StageStack130339B27.template.json new file mode 100644 index 000000000..a7668a10b --- /dev/null +++ b/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.out/assembly-Stage/StageStack130339B27.template.json @@ -0,0 +1,314 @@ +{ + "Resources": { + "CDKMetadata": { + "Type": "AWS::CDK::Metadata", + "Properties": { + "Analytics": "v2:deflate64:H4sIAAAAAAAA/zPSM7Qw1TNQTCwv1k1OydbNyUzSCy5JTM7WyctPSdXLKtYvMzLSMzQEKskqzszULSrNK8nMTdULgtAAJ5JKTz8AAAA=" + }, + "Metadata": { + "aws:cdk:path": "Stage/Stack1/CDKMetadata/Default" + }, + "Condition": "CDKMetadataAvailable" + } + }, + "Conditions": { + "CDKMetadataAvailable": { + "Fn::Or": [ + { + "Fn::Or": [ + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "af-south-1" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "ap-east-1" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "ap-northeast-1" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "ap-northeast-2" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "ap-northeast-3" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "ap-south-1" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "ap-south-2" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "ap-southeast-1" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "ap-southeast-2" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "ap-southeast-3" + ] + } + ] + }, + { + "Fn::Or": [ + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "ap-southeast-4" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "ca-central-1" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "ca-west-1" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "cn-north-1" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "cn-northwest-1" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "eu-central-1" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "eu-central-2" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "eu-north-1" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "eu-south-1" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "eu-south-2" + ] + } + ] + }, + { + "Fn::Or": [ + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "eu-west-1" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "eu-west-2" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "eu-west-3" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "il-central-1" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "me-central-1" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "me-south-1" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "sa-east-1" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "us-east-1" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "us-east-2" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "us-west-1" + ] + } + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "us-west-2" + ] + } + ] + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.out/assembly-Stage/cdk.out b/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.out/assembly-Stage/cdk.out new file mode 100644 index 000000000..1e02a2deb --- /dev/null +++ b/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.out/assembly-Stage/cdk.out @@ -0,0 +1 @@ +{"version":"40.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.out/assembly-Stage/manifest.json b/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.out/assembly-Stage/manifest.json new file mode 100644 index 000000000..52119eb46 --- /dev/null +++ b/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.out/assembly-Stage/manifest.json @@ -0,0 +1,66 @@ +{ + "version": "40.0.0", + "artifacts": { + "StageStack130339B27.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "StageStack130339B27.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "StageStack130339B27": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "StageStack130339B27.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/7f4fb64a3afca08edbdbaa369e00317cb253697278406a83c78e849e89045f9d.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "StageStack130339B27.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + }, + "stackName": "Stage-Stack1" + }, + "dependencies": [ + "StageStack130339B27.assets" + ], + "metadata": { + "/Stage/Stack1/CDKMetadata/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "CDKMetadata" + } + ], + "/Stage/Stack1/CDKMetadata/Condition": [ + { + "type": "aws:cdk:logicalId", + "data": "CDKMetadataAvailable" + } + ], + "/Stage/Stack1/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/Stage/Stack1/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "Stage/Stack1" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.out/cdk.out b/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.out/cdk.out new file mode 100644 index 000000000..1e02a2deb --- /dev/null +++ b/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.out/cdk.out @@ -0,0 +1 @@ +{"version":"40.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.out/manifest.json b/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.out/manifest.json new file mode 100644 index 000000000..53a5301de --- /dev/null +++ b/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.out/manifest.json @@ -0,0 +1,18 @@ +{ + "version": "40.0.0", + "artifacts": { + "assembly-Stage": { + "type": "cdk:cloud-assembly", + "properties": { + "directoryName": "assembly-Stage", + "displayName": "Stage" + } + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.out/tree.json b/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.out/tree.json new file mode 100644 index 000000000..00871b5fa --- /dev/null +++ b/packages/@aws-cdk/toolkit-lib/test/_fixtures/nested-assembly/cdk.out/tree.json @@ -0,0 +1 @@ +{"version":"tree-0.1","tree":{"id":"App","path":"","children":{"Stage":{"id":"Stage","path":"Stage","children":{"Stack1":{"id":"Stack1","path":"Stage/Stack1","children":{"CDKMetadata":{"id":"CDKMetadata","path":"Stage/Stack1/CDKMetadata","children":{"Default":{"id":"Default","path":"Stage/Stack1/CDKMetadata/Default","constructInfo":{"fqn":"aws-cdk-lib.CfnResource","version":"2.185.0"}},"Condition":{"id":"Condition","path":"Stage/Stack1/CDKMetadata/Condition","constructInfo":{"fqn":"aws-cdk-lib.CfnCondition","version":"2.185.0"}}},"constructInfo":{"fqn":"constructs.Construct","version":"10.4.2"}},"BootstrapVersion":{"id":"BootstrapVersion","path":"Stage/Stack1/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"2.185.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"Stage/Stack1/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"2.185.0"}}},"constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"2.185.0"}}},"constructInfo":{"fqn":"aws-cdk-lib.Stage","version":"2.185.0"}},"Tree":{"id":"Tree","path":"Tree","constructInfo":{"fqn":"constructs.Construct","version":"10.4.2"}}},"constructInfo":{"fqn":"aws-cdk-lib.App","version":"2.185.0"}}} \ No newline at end of file diff --git a/packages/@aws-cdk/toolkit-lib/test/actions/deploy.test.ts b/packages/@aws-cdk/toolkit-lib/test/actions/deploy.test.ts index 26f52a375..496da63a6 100644 --- a/packages/@aws-cdk/toolkit-lib/test/actions/deploy.test.ts +++ b/packages/@aws-cdk/toolkit-lib/test/actions/deploy.test.ts @@ -3,7 +3,7 @@ import * as awsCdkApi from '../../lib/api/aws-cdk'; import type { DeployStackOptions, DeployStackResult } from '../../lib/api/aws-cdk'; import { RequireApproval } from '../../lib/api/shared-private'; import { Toolkit } from '../../lib/toolkit'; -import { builderFixture, TestIoHost } from '../_helpers'; +import { builderFixture, cdkOutFixture, TestIoHost } from '../_helpers'; import { MockSdk } from '../util/aws-cdk'; let ioHost: TestIoHost; @@ -280,6 +280,86 @@ describe('deploy', () => { successfulDeployment(); }); }); + + test('deploy returns stack information', async () => { + // GIVEN + mockDeployStack.mockResolvedValue({ + type: 'did-deploy-stack', + stackArn: 'arn:aws:cloudformation:region:account:stack/test-stack', + outputs: { + OutputKey1: 'OutputValue1', + OutputKey2: 'OutputValue2', + }, + noOp: false, + }); + + // WHEN + const cx = await builderFixture(toolkit, 'two-empty-stacks'); + const result = await toolkit.deploy(cx); + + // THEN + expect(result).toEqual({ + stacks: [ + { + stackName: 'Stack1', + hierarchicalId: 'Stack1', + environment: { + // This wouldn't normally work like this, but this is the information in the manifest so that's what we assert + account: 'unknown-account', + region: 'unknown-region', + }, + // This just comes from the mocked function above + stackArn: 'arn:aws:cloudformation:region:account:stack/test-stack', + outputs: { + OutputKey1: 'OutputValue1', + OutputKey2: 'OutputValue2', + }, + }, + { + stackName: 'Stack2', + hierarchicalId: 'Stack2', + environment: { + // This wouldn't normally work like this, but this is the information in the manifest so that's what we assert + account: 'unknown-account', + region: 'unknown-region', + }, + // This just comes from the mocked function above + stackArn: 'arn:aws:cloudformation:region:account:stack/test-stack', + outputs: { + OutputKey1: 'OutputValue1', + OutputKey2: 'OutputValue2', + // omg + }, + }, + ], + }); + }); + + test('deploy contains nested assembly hierarchical id', async () => { + // GIVEN + mockDeployStack.mockResolvedValue({ + type: 'did-deploy-stack', + stackArn: 'arn:aws:cloudformation:region:account:stack/test-stack', + outputs: { + OutputKey1: 'OutputValue1', + OutputKey2: 'OutputValue2', + }, + noOp: false, + }); + + // WHEN + const cx = await cdkOutFixture(toolkit, 'nested-assembly'); + const result = await toolkit.deploy(cx); + + // THEN + expect(result).toEqual({ + stacks: [ + expect.objectContaining({ + hierarchicalId: 'Stage/Stack1', + }), + ], + }); + }); }); function successfulDeployment() {