Skip to content

Commit 847ef78

Browse files
committed
fix(cx-api): cannot detect CloudAssembly across different library versions
1 parent cdc5b38 commit 847ef78

File tree

10 files changed

+49
-12
lines changed

10 files changed

+49
-12
lines changed

packages/@aws-cdk/cx-api/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
"gen": "cdk-copy cx-api",
6363
"watch": "cdk-watch",
6464
"lint": "cdk-lint && madge --circular --extensions js lib",
65-
"test": "cdk-test",
65+
"test": "jest",
6666
"pkglint": "pkglint -f",
6767
"package": "cdk-package",
6868
"awslint": "cdk-awslint",

packages/@aws-cdk/toolkit/lib/api/cloud-assembly/private/source-builder.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import * as cxapi from '@aws-cdk/cx-api';
12
import * as fs from 'fs-extra';
23
import type { ICloudAssemblySource } from '../';
34
import { ContextAwareCloudAssembly, ContextAwareCloudAssemblyProps } from './context-aware-source';
@@ -9,6 +10,12 @@ import { ToolkitError } from '../../errors';
910
import { debug } from '../../io/private';
1011
import { AssemblyBuilder, CdkAppSourceProps } from '../source-builder';
1112

13+
// bypass loading from disk if we already have a supported object
14+
const CLOUD_ASSEMBLY_SYMBOL = Symbol.for('@aws-cdk/cx-api.CloudAssembly');
15+
function isCloudAssembly(x: any): x is cxapi.CloudAssembly {
16+
return x !== null && typeof(x) === 'object' && CLOUD_ASSEMBLY_SYMBOL in x;
17+
}
18+
1219
export abstract class CloudAssemblySourceBuilder {
1320

1421
/**
@@ -40,13 +47,19 @@ export abstract class CloudAssemblySourceBuilder {
4047
produce: async () => {
4148
const outdir = determineOutputDirectory(props.outdir);
4249
const env = await prepareDefaultEnvironment(services, { outdir });
43-
return changeDir(async () =>
50+
const assembly = await changeDir(async () =>
4451
withContext(context.all, env, props.synthOptions ?? {}, async (envWithContext, ctx) =>
4552
withEnv(envWithContext, () => builder({
4653
outdir,
4754
context: ctx,
4855
})),
4956
), props.workingDirectory);
57+
58+
if (isCloudAssembly(assembly)) {
59+
return assembly;
60+
}
61+
62+
return new cxapi.CloudAssembly(assembly.directory);
5063
},
5164
},
5265
contextAssemblyProps,

packages/@aws-cdk/toolkit/lib/api/cloud-assembly/source-builder.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type * as cxapi from '@aws-cdk/cx-api';
1+
import type * as cxschema from '@aws-cdk/cloud-assembly-schema';
22

33
export interface AppProps {
44
/**
@@ -12,7 +12,7 @@ export interface AppProps {
1212
readonly context?: { [key: string]: any };
1313
}
1414

15-
export type AssemblyBuilder = (props: AppProps) => Promise<cxapi.CloudAssembly>;
15+
export type AssemblyBuilder = (props: AppProps) => Promise<cxschema.ICloudAssembly>;
1616

1717
/**
1818
* Configuration for creating a CLI from an AWS CDK App directory

packages/@aws-cdk/toolkit/test/_fixtures/external-context/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ export default async () => {
77
new s3.Bucket(stack, 'MyBucket', {
88
bucketName: app.node.tryGetContext('externally-provided-bucket-name'),
99
});
10-
return app.synth() as any;
10+
return app.synth();
1111
};

packages/@aws-cdk/toolkit/test/_fixtures/stack-with-bucket/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk/toolkit/test/_fixtures/stack-with-bucket/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ export default async () => {
55
const app = new core.App();
66
const stack = new core.Stack(app, 'Stack1');
77
new s3.Bucket(stack, 'MyBucket');
8-
return app.synth() as any;
8+
return app.synth();
99
};

packages/@aws-cdk/toolkit/test/_fixtures/two-empty-stacks/index.js

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk/toolkit/test/_fixtures/two-empty-stacks/index.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,5 @@ export default async () => {
55
new core.Stack(app, 'Stack1');
66
new core.Stack(app, 'Stack2');
77

8-
// @todo fix api
9-
return app.synth() as any;
8+
return app.synth();
109
};

packages/aws-cdk-lib/cx-api/lib/cloud-assembly.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
import * as fs from 'fs';
22
import * as os from 'os';
33
import * as path from 'path';
4+
// This is deliberately importing the interface from the external package.
5+
// We want this, so that jsii language packages can depend on @aws-cdk/cloud-assembly-schema
6+
// instead of being forced to take a dependency on the much larger aws-cdk-lib.
7+
import type { ICloudAssembly } from '@aws-cdk/cloud-assembly-schema';
48
import { CloudFormationStackArtifact } from './artifacts/cloudformation-artifact';
59
import { NestedCloudAssemblyArtifact } from './artifacts/nested-cloud-assembly-artifact';
610
import { TreeCloudArtifact } from './artifacts/tree-cloud-artifact';
711
import { CloudArtifact } from './cloud-artifact';
812
import { topologicalSort } from './toposort';
913
import * as cxschema from '../../cloud-assembly-schema';
1014

15+
const CLOUD_ASSEMBLY_SYMBOL = Symbol.for('@aws-cdk/cx-api.CloudAssembly');
16+
1117
/**
1218
* The name of the root manifest file of the assembly.
1319
*/
@@ -16,7 +22,16 @@ const MANIFEST_FILE = 'manifest.json';
1622
/**
1723
* Represents a deployable cloud application.
1824
*/
19-
export class CloudAssembly {
25+
export class CloudAssembly implements ICloudAssembly {
26+
/**
27+
* Return whether the given object is a CloudAssembly.
28+
*
29+
* We do attribute detection since we can't reliably use 'instanceof'.
30+
*/
31+
public static isCloudAssembly(x: any): x is CloudAssembly {
32+
return x !== null && typeof(x) === 'object' && CLOUD_ASSEMBLY_SYMBOL in x;
33+
}
34+
2035
/**
2136
* The root directory of the cloud assembly.
2237
*/
@@ -54,6 +69,8 @@ export class CloudAssembly {
5469
this.artifacts = this.renderArtifacts(loadOptions?.topoSort ?? true);
5570
this.runtime = this.manifest.runtime || { libraries: { } };
5671

72+
Object.defineProperty(this, CLOUD_ASSEMBLY_SYMBOL, { value: true });
73+
5774
// force validation of deps by accessing 'depends' on all artifacts
5875
this.validateDeps();
5976
}

packages/aws-cdk-lib/cx-api/test/cloud-assembly.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,12 @@ test('getStackArtifact retrieves a stack by artifact id from a nested assembly',
185185
expect(assembly.getStackArtifact('stack1').id).toEqual('stack1');
186186
expect(assembly.getStackArtifact('stack2').id).toEqual('stack2');
187187
});
188+
189+
test('isCloudAssembly correctly detects Cloud Assemblies', () => {
190+
const assembly = new CloudAssembly(path.join(FIXTURES, 'nested-assemblies'));
191+
const inheritedAssembly = new (class extends CloudAssembly {})(path.join(FIXTURES, 'nested-assemblies'));
192+
193+
expect(CloudAssembly.isCloudAssembly(assembly)).toBe(true);
194+
expect(CloudAssembly.isCloudAssembly(inheritedAssembly)).toBe(true);
195+
expect(CloudAssembly.isCloudAssembly({})).toBe(false);
196+
});

0 commit comments

Comments
 (0)