Skip to content

Commit fb027a2

Browse files
authored
feat(toolkit-lib): environment variables for 'fromCdkApp' (#443)
Add an option `env` to `fromCdkApp`, to set the environment variables for the synth'ing subprocess. --- By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license
1 parent e1a9030 commit fb027a2

File tree

5 files changed

+58
-7
lines changed

5 files changed

+58
-7
lines changed

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { ExecutionEnvironment, assemblyFromDirectory } from './prepare-source';
88
import type { ToolkitServices } from '../../../toolkit/private';
99
import { IO } from '../../io/private';
1010
import { ToolkitError, AssemblyError } from '../../shared-public';
11-
import type { AssemblyBuilder } from '../source-builder';
11+
import type { AssemblyBuilder, FromCdkAppOptions } from '../source-builder';
1212
import { ReadableCloudAssembly } from './readable-assembly';
1313
import { Context } from '../../context';
1414
import { RWLock } from '../../rwlock';
@@ -142,7 +142,7 @@ export abstract class CloudAssemblySourceBuilder {
142142
* @param props additional configuration properties
143143
* @returns the CloudAssembly source
144144
*/
145-
public async fromCdkApp(app: string, props: AssemblySourceProps = {}): Promise<ICloudAssemblySource> {
145+
public async fromCdkApp(app: string, props: FromCdkAppOptions = {}): Promise<ICloudAssemblySource> {
146146
const services: ToolkitServices = await this.sourceBuilderServices();
147147
// @todo this definitely needs to read files from the CWD
148148
const context = new Context({ bag: new Settings(props.context ?? {}) });
@@ -171,7 +171,10 @@ export abstract class CloudAssemblySourceBuilder {
171171
await using execution = await ExecutionEnvironment.create(services, { outdir });
172172

173173
const commandLine = await execution.guessExecutable(app);
174-
const env = await execution.defaultEnvVars();
174+
const env = noUndefined({
175+
...await execution.defaultEnvVars(),
176+
...props.env,
177+
});
175178
return await execution.withContext(context.all, env, props.synthOptions, async (envWithContext, _ctx) => {
176179
await execInChildProcess(commandLine.join(' '), {
177180
eventPublisher: async (type, line) => {
@@ -200,3 +203,9 @@ export abstract class CloudAssemblySourceBuilder {
200203
}
201204
}
202205

206+
/**
207+
* Remove undefined values from a dictionary
208+
*/
209+
function noUndefined<A>(xs: Record<string, A>): Record<string, NonNullable<A>> {
210+
return Object.fromEntries(Object.entries(xs).filter(([_, v]) => v !== undefined)) as any;
211+
}

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,20 @@ export interface AssemblySourceProps {
8282
readonly loadAssemblyOptions?: LoadAssemblyOptions;
8383
}
8484

85+
/**
86+
* Options for the `fromCdkApp` Assembly Source constructor
87+
*/
88+
export interface FromCdkAppOptions extends AssemblySourceProps {
89+
/**
90+
* Additional environment variables
91+
*
92+
* These environment variables will be set in addition to the environment
93+
* variables currently set in the process. A value of `undefined` will
94+
* unset a particular environment variable.
95+
*/
96+
readonly env?: Record<string, string | undefined>;
97+
}
98+
8599
/**
86100
* Settings that are passed to a CDK app via the context
87101
*/
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import * as s3 from 'aws-cdk-lib/aws-s3';
2+
import * as core from 'aws-cdk-lib/core';
3+
4+
const stackName = process.env.STACK_NAME;
5+
if (!stackName) {
6+
throw new Error('$STACK_NAME not set!');
7+
}
8+
9+
const app = new core.App({ autoSynth: false });
10+
const stack = new core.Stack(app, stackName);
11+
new s3.Bucket(stack, 'MyBucket');
12+
app.synth();

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as fs from 'node:fs';
22
import * as os from 'node:os';
33
import * as path from 'node:path';
4-
import type { AssemblyDirectoryProps, ICloudAssemblySource } from '../../lib';
4+
import type { AssemblyDirectoryProps, FromCdkAppOptions, ICloudAssemblySource } from '../../lib';
55
import { ToolkitError } from '../../lib';
66
import type { CloudAssemblySourceBuilder } from '../../lib/api/cloud-assembly/private';
77

@@ -12,7 +12,7 @@ function fixturePath(...parts: string[]): string {
1212
return path.normalize(path.join(__dirname, '..', '_fixtures', ...parts));
1313
}
1414

15-
export async function appFixture(toolkit: CloudAssemblySourceBuilder, name: string, context?: { [key: string]: any }) {
15+
export async function appFixture(toolkit: CloudAssemblySourceBuilder, name: string, options?: Omit<FromCdkAppOptions, 'workingDirectory' | 'outdir'>) {
1616
const appPath = fixturePath(name, 'app.js');
1717
if (!fs.existsSync(appPath)) {
1818
throw new ToolkitError(`App Fixture ${name} does not exist in ${appPath}`);
@@ -21,7 +21,7 @@ export async function appFixture(toolkit: CloudAssemblySourceBuilder, name: stri
2121
return toolkit.fromCdkApp(app, {
2222
workingDirectory: path.join(__dirname, '..', '..'),
2323
outdir: tmpOutdir(),
24-
context,
24+
...options,
2525
});
2626
}
2727

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,9 @@ describe('fromCdkApp', () => {
117117
test('can provide context', async () => {
118118
// WHEN
119119
const cx = await appFixture(toolkit, 'external-context', {
120-
'externally-provided-bucket-name': 'amzn-s3-demo-bucket',
120+
context: {
121+
'externally-provided-bucket-name': 'amzn-s3-demo-bucket',
122+
},
121123
});
122124
await using assembly = await cx.produce();
123125
const stack = assembly.cloudAssembly.getStackByName('Stack1').template;
@@ -126,6 +128,20 @@ describe('fromCdkApp', () => {
126128
expect(JSON.stringify(stack)).toContain('amzn-s3-demo-bucket');
127129
});
128130

131+
test('can set environment variables', async () => {
132+
// WHEN
133+
const cx = await appFixture(toolkit, 'environment-variable', {
134+
env: {
135+
STACK_NAME: 'SomeStackName',
136+
},
137+
});
138+
await using assembly = await cx.produce();
139+
const stack = assembly.cloudAssembly.getStackByName('SomeStackName').template;
140+
141+
// THEN
142+
expect(stack).toBeDefined();
143+
});
144+
129145
test('will capture error output', async () => {
130146
// WHEN
131147
const cx = await appFixture(toolkit, 'validation-error');

0 commit comments

Comments
 (0)