generated from amazon-archives/__template_Apache-2.0
-
Notifications
You must be signed in to change notification settings - Fork 80
Expand file tree
/
Copy pathprepare-source.ts
More file actions
179 lines (159 loc) · 6.12 KB
/
prepare-source.ts
File metadata and controls
179 lines (159 loc) · 6.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
import * as os from 'node:os';
import * as path from 'node:path';
import * as cxschema from '@aws-cdk/cloud-assembly-schema';
import * as cxapi from '@aws-cdk/cx-api';
import * as fs from 'fs-extra';
import { lte } from 'semver';
import { prepareDefaultEnvironment as oldPrepare, prepareContext, spaceAvailableForContext, Settings, loadTree, some, splitBySize, versionNumber } from '../../../api/aws-cdk';
import { ToolkitServices } from '../../../toolkit/private';
import { ToolkitError } from '../../errors';
import { ActionAwareIoHost, asLogger, CODES, error } from '../../io/private';
import type { AppSynthOptions } from '../source-builder';
export { guessExecutable } from '../../../api/aws-cdk';
type Env = { [key: string]: string };
type Context = { [key: string]: any };
/**
* Turn the given optional output directory into a fixed output directory
*/
export function determineOutputDirectory(outdir?: string) {
return outdir ?? fs.mkdtempSync(path.join(fs.realpathSync(os.tmpdir()), 'cdk.out'));
}
/**
* If we don't have region/account defined in context, we fall back to the default SDK behavior
* where region is retrieved from ~/.aws/config and account is based on default credentials provider
* chain and then STS is queried.
*
* This is done opportunistically: for example, if we can't access STS for some reason or the region
* is not configured, the context value will be 'null' and there could failures down the line. In
* some cases, synthesis does not require region/account information at all, so that might be perfectly
* fine in certain scenarios.
*
* @param context The context key/value bash.
*/
export async function prepareDefaultEnvironment(services: ToolkitServices, props: { outdir?: string } = {}): Promise<Env> {
const logFn = asLogger(services.ioHost, 'ASSEMBLY').debug;
const env = await oldPrepare(services.sdkProvider, logFn);
if (props.outdir) {
env[cxapi.OUTDIR_ENV] = props.outdir;
await logFn('outdir:', props.outdir);
}
// CLI version information
env[cxapi.CLI_ASM_VERSION_ENV] = cxschema.Manifest.version();
env[cxapi.CLI_VERSION_ENV] = versionNumber();
await logFn('env:', env);
return env;
}
/**
* Run code from a different working directory
*/
export async function changeDir<T>(block: () => Promise<T>, workingDir?: string) {
const originalWorkingDir = process.cwd();
try {
if (workingDir) {
process.chdir(workingDir);
}
return await block();
} finally {
if (workingDir) {
process.chdir(originalWorkingDir);
}
}
}
/**
* Run code with additional environment variables
*/
export async function withEnv<T>(env: Env = {}, block: () => Promise<T>) {
const originalEnv = process.env;
try {
process.env = {
...originalEnv,
...env,
};
return await block();
} finally {
process.env = originalEnv;
}
}
/**
* Run code with context setup inside the environment
*/
export async function withContext<T>(
inputContext: Context,
env: Env,
synthOpts: AppSynthOptions = {},
block: (env: Env, context: Context) => Promise<T>,
) {
const context = await prepareContext(synthOptsDefaults(synthOpts), inputContext, env);
let contextOverflowLocation = null;
try {
const envVariableSizeLimit = os.platform() === 'win32' ? 32760 : 131072;
const [smallContext, overflow] = splitBySize(context, spaceAvailableForContext(env, envVariableSizeLimit));
// Store the safe part in the environment variable
env[cxapi.CONTEXT_ENV] = JSON.stringify(smallContext);
// If there was any overflow, write it to a temporary file
if (Object.keys(overflow ?? {}).length > 0) {
const contextDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cdk-context'));
contextOverflowLocation = path.join(contextDir, 'context-overflow.json');
fs.writeJSONSync(contextOverflowLocation, overflow);
env[cxapi.CONTEXT_OVERFLOW_LOCATION_ENV] = contextOverflowLocation;
}
// call the block code with new environment
return await block(env, context);
} finally {
if (contextOverflowLocation) {
fs.removeSync(path.dirname(contextOverflowLocation));
}
}
}
/**
* Checks if a given assembly supports context overflow, warn otherwise.
*
* @param assembly the assembly to check
*/
export async function checkContextOverflowSupport(assembly: cxapi.CloudAssembly, ioHost: ActionAwareIoHost): Promise<void> {
const logFn = asLogger(ioHost, 'ASSEMBLY').warn;
const tree = loadTree(assembly);
const frameworkDoesNotSupportContextOverflow = some(tree, node => {
const fqn = node.constructInfo?.fqn;
const version = node.constructInfo?.version;
return (fqn === 'aws-cdk-lib.App' && version != null && lte(version, '2.38.0')) // v2
|| fqn === '@aws-cdk/core.App'; // v1
});
// We're dealing with an old version of the framework here. It is unaware of the temporary
// file, which means that it will ignore the context overflow.
if (frameworkDoesNotSupportContextOverflow) {
await logFn('Part of the context could not be sent to the application. Please update the AWS CDK library to the latest version.');
}
}
/**
* Safely create an assembly from a cloud assembly directory
*/
export async function assemblyFromDirectory(assemblyDir: string, ioHost: ActionAwareIoHost) {
try {
const assembly = new cxapi.CloudAssembly(assemblyDir, {
// We sort as we deploy
topoSort: false,
});
await checkContextOverflowSupport(assembly, ioHost);
return assembly;
} catch (err: any) {
if (err.message.includes(cxschema.VERSION_MISMATCH)) {
// this means the CLI version is too old.
// we instruct the user to upgrade.
const message = 'This AWS CDK Toolkit is not compatible with the AWS CDK library used by your application. Please upgrade to the latest version.';
await ioHost.notify(error(message, CODES.CDK_ASSEMBLY_E1111, { error: err.message }));
throw new ToolkitError(`${message}\n(${err.message}`);
}
throw err;
}
}
function synthOptsDefaults(synthOpts: AppSynthOptions = {}): Settings {
return new Settings({
debug: false,
pathMetadata: true,
versionReporting: true,
assetMetadata: true,
assetStaging: true,
...synthOpts,
}, true);
}