Skip to content

Commit

Permalink
fix(cx-api): cannot detect CloudAssembly across different library ver…
Browse files Browse the repository at this point in the history
…sions
  • Loading branch information
mrgrain committed Jan 20, 2025
1 parent 8d5ad38 commit ededa21
Show file tree
Hide file tree
Showing 10 changed files with 49 additions and 12 deletions.
2 changes: 1 addition & 1 deletion packages/@aws-cdk/cx-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"gen": "cdk-copy cx-api",
"watch": "cdk-watch",
"lint": "cdk-lint && madge --circular --extensions js lib",
"test": "cdk-test",
"test": "jest",
"pkglint": "pkglint -f",
"package": "cdk-package",
"awslint": "cdk-awslint",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as cxapi from '@aws-cdk/cx-api';
import * as fs from 'fs-extra';
import type { ICloudAssemblySource } from '../';
import { ContextAwareCloudAssembly, ContextAwareCloudAssemblyProps } from './context-aware-source';
Expand All @@ -9,6 +10,12 @@ import { ToolkitError } from '../../errors';
import { debug } from '../../io/private';
import { AssemblyBuilder, CdkAppSourceProps } from '../source-builder';

// bypass loading from disk if we already have a supported object
const CLOUD_ASSEMBLY_SYMBOL = Symbol.for('@aws-cdk/cx-api.CloudAssembly');
function isCloudAssembly(x: any): x is cxapi.CloudAssembly {
return x !== null && typeof(x) === 'object' && CLOUD_ASSEMBLY_SYMBOL in x;
}

export abstract class CloudAssemblySourceBuilder {

/**
Expand Down Expand Up @@ -40,13 +47,19 @@ export abstract class CloudAssemblySourceBuilder {
produce: async () => {
const outdir = determineOutputDirectory(props.outdir);
const env = await prepareDefaultEnvironment(services, { outdir });
return changeDir(async () =>
const assembly = await changeDir(async () =>
withContext(context.all, env, props.synthOptions ?? {}, async (envWithContext, ctx) =>
withEnv(envWithContext, () => builder({
outdir,
context: ctx,
})),
), props.workingDirectory);

if (isCloudAssembly(assembly)) {
return assembly;
}

return new cxapi.CloudAssembly(assembly.directory);
},
},
contextAssemblyProps,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type * as cxapi from '@aws-cdk/cx-api';
import type * as cxschema from "@aws-cdk/cloud-assembly-schema";

export interface AppProps {
/**
Expand All @@ -12,7 +12,7 @@ export interface AppProps {
readonly context?: { [key: string]: any };
}

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

/**
* Configuration for creating a CLI from an AWS CDK App directory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ export default async () => {
new s3.Bucket(stack, 'MyBucket', {
bucketName: app.node.tryGetContext('externally-provided-bucket-name'),
});
return app.synth() as any;
return app.synth();
};

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ export default async () => {
const app = new core.App();
const stack = new core.Stack(app, 'Stack1');
new s3.Bucket(stack, 'MyBucket');
return app.synth() as any;
return app.synth();
};

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@ export default async () => {
new core.Stack(app, 'Stack1');
new core.Stack(app, 'Stack2');

// @todo fix api
return app.synth() as any;
return app.synth();
};
19 changes: 18 additions & 1 deletion packages/aws-cdk-lib/cx-api/lib/cloud-assembly.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
// This is deliberately importing the interface from the external package.
// We want this, so that jsii language packages can depend on @aws-cdk/cloud-assembly-schema
// instead of being forced to take a dependency on the much larger aws-cdk-lib.
import type { ICloudAssembly } from '@aws-cdk/cloud-assembly-schema';
import { CloudFormationStackArtifact } from './artifacts/cloudformation-artifact';
import { NestedCloudAssemblyArtifact } from './artifacts/nested-cloud-assembly-artifact';
import { TreeCloudArtifact } from './artifacts/tree-cloud-artifact';
import { CloudArtifact } from './cloud-artifact';
import { topologicalSort } from './toposort';
import * as cxschema from '../../cloud-assembly-schema';

const CLOUD_ASSEMBLY_SYMBOL = Symbol.for('@aws-cdk/cx-api.CloudAssembly');

/**
* The name of the root manifest file of the assembly.
*/
Expand All @@ -16,7 +22,16 @@ const MANIFEST_FILE = 'manifest.json';
/**
* Represents a deployable cloud application.
*/
export class CloudAssembly {
export class CloudAssembly implements ICloudAssembly {
/**
* Return whether the given object is a CloudAssembly.
*
* We do attribute detection since we can't reliably use 'instanceof'.
*/
public static isCloudAssembly(x: any): x is CloudAssembly {
return x !== null && typeof(x) === 'object' && CLOUD_ASSEMBLY_SYMBOL in x;
}

/**
* The root directory of the cloud assembly.
*/
Expand Down Expand Up @@ -54,6 +69,8 @@ export class CloudAssembly {
this.artifacts = this.renderArtifacts(loadOptions?.topoSort ?? true);
this.runtime = this.manifest.runtime || { libraries: { } };

Object.defineProperty(this, CLOUD_ASSEMBLY_SYMBOL, { value: true });

// force validation of deps by accessing 'depends' on all artifacts
this.validateDeps();
}
Expand Down
9 changes: 9 additions & 0 deletions packages/aws-cdk-lib/cx-api/test/cloud-assembly.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,12 @@ test('getStackArtifact retrieves a stack by artifact id from a nested assembly',
expect(assembly.getStackArtifact('stack1').id).toEqual('stack1');
expect(assembly.getStackArtifact('stack2').id).toEqual('stack2');
});

test('isCloudAssembly correctly detects Cloud Assemblies', () => {
const assembly = new CloudAssembly(path.join(FIXTURES, 'nested-assemblies'));
const inheritedAssembly = new (class extends CloudAssembly {})(path.join(FIXTURES, 'nested-assemblies'));

expect(CloudAssembly.isCloudAssembly(assembly)).toBe(true);
expect(CloudAssembly.isCloudAssembly(inheritedAssembly)).toBe(true);
expect(CloudAssembly.isCloudAssembly({})).toBe(false);
});

0 comments on commit ededa21

Please # to comment.