Skip to content

Commit cdf20c7

Browse files
committed
fix(toolkit-lib): fromCdkApp fails if working directory is given and outdir is relative
The documentation of `fromCdkApp` falsely claimed that it would create a temporary directory and clean it up. It does not, it emits into `cdk.out` which won't get cleaned up. Update the docs.
1 parent e1a9030 commit cdf20c7

File tree

3 files changed

+50
-15
lines changed

3 files changed

+50
-15
lines changed

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import * as path from 'path';
12
import * as cxapi from '@aws-cdk/cx-api';
23
import * as fs from 'fs-extra';
34
import type { AssemblyDirectoryProps, AssemblySourceProps, ICloudAssemblySource } from '../';
@@ -129,9 +130,10 @@ export abstract class CloudAssemblySourceBuilder {
129130
/**
130131
* Use a directory containing an AWS CDK app as source.
131132
*
132-
* A temporary output directory will be created if no output directory is
133-
* explicitly given. This directory will be cleaned up if synthesis fails, or
134-
* when the Cloud Assembly produced by this source is disposed.
133+
* The output directory will be evaluated with respect to the working
134+
* directory if relative. If not given, it will synthesize into a `cdk.out`
135+
* subdirectory. This directory will not be cleaned up (but you should still
136+
* call `.dispose()` on the Cloud Assembly returned by this assembly source).
135137
*
136138
* A write lock will be acquired on the output directory for the duration of
137139
* the CDK app synthesis (which means that no two apps can synthesize at the
@@ -152,6 +154,9 @@ export abstract class CloudAssemblySourceBuilder {
152154
lookups: props.lookups,
153155
};
154156

157+
const workingDirectory = props.workingDirectory ?? process.cwd();
158+
const outdir = path.resolve(workingDirectory, props.outdir ?? 'cdk.out');
159+
155160
return new ContextAwareCloudAssemblySource(
156161
{
157162
produce: async () => {
@@ -161,7 +166,6 @@ export abstract class CloudAssemblySourceBuilder {
161166
// await execInChildProcess(build, { cwd: props.workingDirectory });
162167
// }
163168

164-
const outdir = props.outdir ?? 'cdk.out';
165169
try {
166170
fs.mkdirpSync(outdir);
167171
} catch (e: any) {
@@ -185,7 +189,7 @@ export abstract class CloudAssemblySourceBuilder {
185189
}
186190
},
187191
extraEnv: envWithContext,
188-
cwd: props.workingDirectory,
192+
cwd: workingDirectory,
189193
});
190194

191195
const asm = await assemblyFromDirectory(outdir, services.ioHelper, props.loadAssemblyOptions);

packages/@aws-cdk/toolkit-lib/test/_helpers/index.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,29 @@ import type { CloudAssemblySourceBuilder } from '../../lib/api/cloud-assembly/pr
88
export * from './test-cloud-assembly-source';
99
export * from './test-io-host';
1010

11-
function fixturePath(...parts: string[]): string {
12-
return path.normalize(path.join(__dirname, '..', '_fixtures', ...parts));
11+
function appFixturePath(...parts: string[]): string {
12+
const ret = path.normalize(path.join(__dirname, '..', '_fixtures', ...parts));
13+
if (!fs.existsSync(ret)) {
14+
throw new ToolkitError(`App Fixture not found: ${ret}`);
15+
}
16+
return ret;
1317
}
1418

15-
export async function appFixture(toolkit: CloudAssemblySourceBuilder, name: string, context?: { [key: string]: any }) {
16-
const appPath = fixturePath(name, 'app.js');
17-
if (!fs.existsSync(appPath)) {
18-
throw new ToolkitError(`App Fixture ${name} does not exist in ${appPath}`);
19-
}
20-
const app = `cat ${appPath} | node --input-type=module`;
21-
return toolkit.fromCdkApp(app, {
19+
/**
20+
* Return config we can send into `fromCdkApp` to execute a given app fixture
21+
*/
22+
export function appFixtureCommand(name: string) {
23+
const appPath = appFixturePath(name, 'app.js');
24+
return {
25+
app: `cat ${appPath} | node --input-type=module`,
2226
workingDirectory: path.join(__dirname, '..', '..'),
27+
};
28+
}
29+
30+
export async function appFixture(toolkit: CloudAssemblySourceBuilder, name: string, context?: { [key: string]: any }) {
31+
const app = appFixtureCommand(name);
32+
return toolkit.fromCdkApp(app.app, {
33+
workingDirectory: app.workingDirectory,
2334
outdir: tmpOutdir(),
2435
context,
2536
});

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
import { promises as fs } from 'fs';
2+
import * as path from 'path';
13
import { RWLock } from '../../../lib/api/rwlock';
24
import { contextproviders } from '../../../lib/api/shared-private';
35
import { ToolkitError } from '../../../lib/api/shared-public';
46
import { Toolkit } from '../../../lib/toolkit/toolkit';
5-
import { appFixture, autoCleanOutDir, builderFixture, cdkOutFixture, TestIoHost } from '../../_helpers';
7+
import { appFixture, appFixtureCommand, autoCleanOutDir, builderFixture, cdkOutFixture, TestIoHost } from '../../_helpers';
68

79
// these tests often run a bit longer than the default
810
jest.setTimeout(10_000);
@@ -114,6 +116,24 @@ describe('fromCdkApp', () => {
114116
expect(assembly.cloudAssembly.stacksRecursively.map(s => s.hierarchicalId)).toEqual(['Stack1', 'Stack2']);
115117
});
116118

119+
test('synth succeeds when working directory is given and outdir is relative', async () => {
120+
// WHEN
121+
const app = await appFixtureCommand('two-empty-stacks');
122+
const cx = await toolkit.fromCdkApp(app.app, {
123+
workingDirectory: app.workingDirectory,
124+
outdir: 'relative.dir',
125+
});
126+
127+
try {
128+
await using assembly = await cx.produce();
129+
130+
// THEN - synth succeeds
131+
expect(assembly.cloudAssembly.stacksRecursively.map(s => s.hierarchicalId)).toEqual(['Stack1', 'Stack2']);
132+
} finally {
133+
await fs.rm(path.join(app.workingDirectory, 'relative.dir'), { force: true, recursive: true });
134+
}
135+
});
136+
117137
test('can provide context', async () => {
118138
// WHEN
119139
const cx = await appFixture(toolkit, 'external-context', {

0 commit comments

Comments
 (0)