@aws-cdk-testing/cli-integ 3.1.0 → 3.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/NOTICE +1 -1
  2. package/lib/aws.d.ts +8 -2
  3. package/lib/aws.js +22 -8
  4. package/lib/aws.ts +28 -10
  5. package/lib/cli/run-suite.js +2 -1
  6. package/lib/cli/run-suite.ts +1 -1
  7. package/lib/cli/stage-distribution.js +1 -1
  8. package/lib/cli/stage-distribution.ts +0 -5
  9. package/lib/github.js +1 -2
  10. package/lib/github.ts +1 -1
  11. package/lib/integ-test.d.ts +1 -0
  12. package/lib/integ-test.js +3 -2
  13. package/lib/integ-test.ts +4 -1
  14. package/lib/package-sources/release-source.js +1 -1
  15. package/lib/package-sources/release-source.ts +2 -1
  16. package/lib/package-sources/repo-source.d.ts +4 -4
  17. package/lib/package-sources/repo-source.js +5 -5
  18. package/lib/package-sources/repo-source.ts +6 -5
  19. package/lib/proxy.d.ts +13 -0
  20. package/lib/proxy.js +49 -0
  21. package/lib/proxy.ts +64 -0
  22. package/lib/with-aws.d.ts +3 -0
  23. package/lib/with-aws.js +54 -6
  24. package/lib/with-aws.ts +52 -5
  25. package/lib/with-cdk-app.d.ts +21 -2
  26. package/lib/with-cdk-app.js +73 -73
  27. package/lib/with-cdk-app.ts +96 -95
  28. package/lib/with-cli-lib.d.ts +2 -2
  29. package/lib/with-cli-lib.js +12 -2
  30. package/lib/with-cli-lib.ts +18 -4
  31. package/lib/with-sam.js +2 -1
  32. package/lib/with-sam.ts +1 -0
  33. package/package.json +6 -5
  34. package/resources/cdk-apps/sam_cdk_integ_app/src/docker/DockerImageFunctionConstruct/Dockerfile +2 -0
  35. package/resources/integ.jest.config.js +4 -1
  36. package/tests/cli-integ-tests/bootstrapping.integtest.js +20 -1
  37. package/tests/cli-integ-tests/bootstrapping.integtest.ts +25 -1
  38. package/tests/cli-integ-tests/cli.integtest.js +70 -38
  39. package/tests/cli-integ-tests/cli.integtest.ts +79 -51
  40. package/tests/cli-integ-tests/proxy.integtest.d.ts +1 -0
  41. package/tests/cli-integ-tests/proxy.integtest.js +147 -0
  42. package/tests/cli-integ-tests/proxy.integtest.ts +167 -0
  43. package/tests/init-javascript/init-javascript.integtest.js +1 -1
  44. package/tests/init-javascript/init-javascript.integtest.ts +0 -1
  45. package/tests/init-typescript-app/init-typescript-app.integtest.js +2 -2
  46. package/tests/init-typescript-app/init-typescript-app.integtest.ts +1 -1
  47. package/tests/uberpackage/uberpackage.integtest.js +2 -2
  48. package/tests/uberpackage/uberpackage.integtest.ts +2 -2
@@ -1,5 +1,4 @@
1
1
  import { existsSync, promises as fs } from 'fs';
2
- import * as querystring from 'node:querystring';
3
2
  import * as os from 'os';
4
3
  import * as path from 'path';
5
4
  import {
@@ -23,8 +22,6 @@ import { InvokeCommand } from '@aws-sdk/client-lambda';
23
22
  import { PutObjectLockConfigurationCommand } from '@aws-sdk/client-s3';
24
23
  import { CreateTopicCommand, DeleteTopicCommand } from '@aws-sdk/client-sns';
25
24
  import { AssumeRoleCommand, GetCallerIdentityCommand } from '@aws-sdk/client-sts';
26
- import * as mockttp from 'mockttp';
27
- import { CompletedRequest } from 'mockttp';
28
25
  import {
29
26
  cloneDirectory,
30
27
  integTest,
@@ -41,6 +38,7 @@ import {
41
38
  withSamIntegrationFixture,
42
39
  withSpecificFixture,
43
40
  } from '../../lib';
41
+ import { awsActionsFromRequests, startProxyServer } from '../../lib/proxy';
44
42
 
45
43
  jest.setTimeout(2 * 60 * 60_000); // Includes the time to acquire locks, worst-case single-threaded runtime
46
44
 
@@ -363,7 +361,8 @@ integTest('doubly nested stack',
363
361
  await fixture.cdkDeploy('with-doubly-nested-stack', {
364
362
  captureStderr: false,
365
363
  });
366
- }));
364
+ }),
365
+ );
367
366
 
368
367
  integTest(
369
368
  'nested stack with parameters',
@@ -406,7 +405,7 @@ integTest(
406
405
  );
407
406
  expect(response.Stacks?.[0].StackStatus).toEqual('REVIEW_IN_PROGRESS');
408
407
 
409
- //verify a change set was created with the provided name
408
+ // verify a change set was created with the provided name
410
409
  const changeSetResponse = await fixture.aws.cloudFormation.send(
411
410
  new ListChangeSetsCommand({
412
411
  StackName: stackArn,
@@ -487,6 +486,65 @@ integTest(
487
486
  }),
488
487
  );
489
488
 
489
+ integTest('deploy with import-existing-resources true', withDefaultFixture(async (fixture) => {
490
+ const stackArn = await fixture.cdkDeploy('test-2', {
491
+ options: ['--no-execute', '--import-existing-resources'],
492
+ captureStderr: false,
493
+ });
494
+ // verify that we only deployed a single stack (there's a single ARN in the output)
495
+ expect(stackArn.split('\n').length).toEqual(1);
496
+
497
+ const response = await fixture.aws.cloudFormation.send(new DescribeStacksCommand({
498
+ StackName: stackArn,
499
+ }));
500
+ expect(response.Stacks?.[0].StackStatus).toEqual('REVIEW_IN_PROGRESS');
501
+
502
+ // verify a change set was successfully created
503
+ // Here, we do not test whether a resource is actually imported, because that is a CloudFormation feature, not a CDK feature.
504
+ const changeSetResponse = await fixture.aws.cloudFormation.send(new ListChangeSetsCommand({
505
+ StackName: stackArn,
506
+ }));
507
+ const changeSets = changeSetResponse.Summaries || [];
508
+ expect(changeSets.length).toEqual(1);
509
+ expect(changeSets[0].Status).toEqual('CREATE_COMPLETE');
510
+ expect(changeSets[0].ImportExistingResources).toEqual(true);
511
+ }));
512
+
513
+ integTest('deploy without import-existing-resources', withDefaultFixture(async (fixture) => {
514
+ const stackArn = await fixture.cdkDeploy('test-2', {
515
+ options: ['--no-execute'],
516
+ captureStderr: false,
517
+ });
518
+ // verify that we only deployed a single stack (there's a single ARN in the output)
519
+ expect(stackArn.split('\n').length).toEqual(1);
520
+
521
+ const response = await fixture.aws.cloudFormation.send(new DescribeStacksCommand({
522
+ StackName: stackArn,
523
+ }));
524
+ expect(response.Stacks?.[0].StackStatus).toEqual('REVIEW_IN_PROGRESS');
525
+
526
+ // verify a change set was successfully created and ImportExistingResources = false
527
+ const changeSetResponse = await fixture.aws.cloudFormation.send(new ListChangeSetsCommand({
528
+ StackName: stackArn,
529
+ }));
530
+ const changeSets = changeSetResponse.Summaries || [];
531
+ expect(changeSets.length).toEqual(1);
532
+ expect(changeSets[0].Status).toEqual('CREATE_COMPLETE');
533
+ expect(changeSets[0].ImportExistingResources).toEqual(false);
534
+ }));
535
+
536
+ integTest('deploy with method=direct and import-existing-resources fails', withDefaultFixture(async (fixture) => {
537
+ const stackName = 'iam-test';
538
+ await expect(fixture.cdkDeploy(stackName, {
539
+ options: ['--import-existing-resources', '--method=direct'],
540
+ })).rejects.toThrow('exited with error');
541
+
542
+ // Ensure stack was not deployed
543
+ await expect(fixture.aws.cloudFormation.send(new DescribeStacksCommand({
544
+ StackName: fixture.fullStackName(stackName),
545
+ }))).rejects.toThrow('does not exist');
546
+ }));
547
+
490
548
  integTest(
491
549
  'update to stack in ROLLBACK_COMPLETE state will delete stack and create a new one',
492
550
  withDefaultFixture(async (fixture) => {
@@ -1204,7 +1262,6 @@ integTest(
1204
1262
  integTest(
1205
1263
  'cdk diff doesnt show resource metadata changes',
1206
1264
  withDefaultFixture(async (fixture) => {
1207
-
1208
1265
  // GIVEN - small initial stack with default resource metadata
1209
1266
  await fixture.cdkDeploy('metadata');
1210
1267
 
@@ -1224,7 +1281,6 @@ integTest(
1224
1281
  integTest(
1225
1282
  'cdk diff shows resource metadata changes with --no-change-set',
1226
1283
  withDefaultFixture(async (fixture) => {
1227
-
1228
1284
  // GIVEN - small initial stack with default resource metadata
1229
1285
  await fixture.cdkDeploy('metadata');
1230
1286
 
@@ -2488,6 +2544,8 @@ integTest(
2488
2544
  // THEN
2489
2545
  const expectedSubstring = 'Resource is not in the expected state due to waiter status: TIMEOUT';
2490
2546
  expect(deployOutput).toContain(expectedSubstring);
2547
+ expect(deployOutput).toContain('Observed responses:');
2548
+ expect(deployOutput).toContain('200: OK');
2491
2549
  expect(deployOutput).not.toContain('hotswapped!');
2492
2550
  }),
2493
2551
  );
@@ -2776,7 +2834,6 @@ integTest(
2776
2834
  );
2777
2835
 
2778
2836
  integTest('cdk notices are displayed correctly', withDefaultFixture(async (fixture) => {
2779
-
2780
2837
  const cache = {
2781
2838
  expiration: 4125963264000, // year 2100 so we never overwrite the cache
2782
2839
  notices: [
@@ -2810,65 +2867,36 @@ integTest('cdk notices are displayed correctly', withDefaultFixture(async (fixtu
2810
2867
 
2811
2868
  // assert dynamic environments are resolved
2812
2869
  expect(output).toContain(`AffectedEnvironments:<aws://${await fixture.aws.account()}/${fixture.aws.region}>`);
2813
-
2814
2870
  }));
2815
2871
 
2816
2872
  integTest('requests go through a proxy when configured',
2817
2873
  withDefaultFixture(async (fixture) => {
2818
- // Set up key and certificate
2819
- const { key, cert } = await mockttp.generateCACertificate();
2820
- const certDir = await fs.mkdtemp(path.join(os.tmpdir(), 'cdk-'));
2821
- const certPath = path.join(certDir, 'cert.pem');
2822
- const keyPath = path.join(certDir, 'key.pem');
2823
- await fs.writeFile(keyPath, key);
2824
- await fs.writeFile(certPath, cert);
2825
-
2826
- const proxyServer = mockttp.getLocal({
2827
- https: { keyPath, certPath },
2828
- });
2829
-
2830
- // We don't need to modify any request, so the proxy
2831
- // passes through all requests to the target host.
2832
- const endpoint = await proxyServer
2833
- .forAnyRequest()
2834
- .thenPassThrough();
2835
-
2836
- proxyServer.enableDebug();
2837
- await proxyServer.start();
2838
-
2839
- // The proxy is now ready to intercept requests
2840
-
2874
+ const proxyServer = await startProxyServer();
2841
2875
  try {
2876
+ // Delete notices cache if it exists
2877
+ await fs.rm(path.join(process.env.HOME ?? os.userInfo().homedir, '.cdk/cache/notices.json'), { force: true });
2878
+
2842
2879
  await fixture.cdkDeploy('test-2', {
2843
2880
  captureStderr: true,
2844
2881
  options: [
2845
2882
  '--proxy', proxyServer.url,
2846
- '--ca-bundle-path', certPath,
2883
+ '--ca-bundle-path', proxyServer.certPath,
2847
2884
  ],
2848
2885
  modEnv: {
2849
2886
  CDK_HOME: fixture.integTestDir,
2850
2887
  },
2851
2888
  });
2852
- } finally {
2853
- await fs.rm(certDir, { recursive: true, force: true });
2854
- await proxyServer.stop();
2855
- }
2856
2889
 
2857
- const requests = await endpoint.getSeenRequests();
2890
+ const requests = await proxyServer.getSeenRequests();
2858
2891
 
2859
- expect(requests.map(req => req.url))
2860
- .toContain('https://cli.cdk.dev-tools.aws.dev/notices.json');
2892
+ expect(requests.map(req => req.url))
2893
+ .toContain('https://cli.cdk.dev-tools.aws.dev/notices.json');
2861
2894
 
2862
- const actionsUsed = actions(requests);
2863
- expect(actionsUsed).toContain('AssumeRole');
2864
- expect(actionsUsed).toContain('CreateChangeSet');
2895
+ const actionsUsed = awsActionsFromRequests(requests);
2896
+ expect(actionsUsed).toContain('AssumeRole');
2897
+ expect(actionsUsed).toContain('CreateChangeSet');
2898
+ } finally {
2899
+ await proxyServer.stop();
2900
+ }
2865
2901
  }),
2866
2902
  );
2867
-
2868
- function actions(requests: CompletedRequest[]): string[] {
2869
- return [...new Set(requests
2870
- .map(req => req.body.buffer.toString('utf-8'))
2871
- .map(body => querystring.decode(body))
2872
- .map(x => x.Action as string)
2873
- .filter(action => action != null))];
2874
- }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ var _a;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const fs_1 = require("fs");
5
+ const os = require("os");
6
+ const path = require("path");
7
+ const integ_test_1 = require("../../lib/integ-test");
8
+ const proxy_1 = require("../../lib/proxy");
9
+ const with_cdk_app_1 = require("../../lib/with-cdk-app");
10
+ const docker = (_a = process.env.CDK_DOCKER) !== null && _a !== void 0 ? _a : 'docker';
11
+ (0, integ_test_1.integTest)('all calls from isolated container go through proxy', (0, with_cdk_app_1.withDefaultFixture)(async (fixture) => {
12
+ // Find the 'cdk' command and make sure it is mounted into the container
13
+ const cdkFullpath = (await fixture.shell(['which', 'cdk'])).trim();
14
+ let cdkTop = findMountableParent(cdkFullpath);
15
+ // Run a 'cdk deploy' inside the container
16
+ const commands = [
17
+ `env ${renderEnv(fixture.cdkShellEnv())} ${cdkFullpath} ${fixture.cdkDeployCommandLine('test-2').join(' ')} -v`,
18
+ ];
19
+ await runInIsolatedContainer(fixture, [cdkTop], commands);
20
+ }));
21
+ async function runInIsolatedContainer(fixture, pathsToMount, testCommands) {
22
+ pathsToMount.push(`${process.env.HOME}`, fixture.integTestDir);
23
+ // Resolve credential provider to an access key that we can pass into the container
24
+ const credentials = await fixture.aws.credentials();
25
+ const proxy = await (0, proxy_1.startProxyServer)(fixture.integTestDir);
26
+ try {
27
+ const proxyPort = proxy.port;
28
+ const setupCommands = [
29
+ 'apt-get update -qq',
30
+ 'apt-get install -qqy nodejs > /dev/null',
31
+ ...isolatedDockerCommands(proxyPort, proxy.certPath),
32
+ ];
33
+ const scriptName = path.join(fixture.integTestDir, 'script.sh');
34
+ // Write a script file
35
+ await fs_1.promises.writeFile(scriptName, [
36
+ '#!/bin/bash',
37
+ 'set -x',
38
+ 'set -eu',
39
+ ...setupCommands,
40
+ ...testCommands,
41
+ ].join('\n'), 'utf-8');
42
+ await fs_1.promises.chmod(scriptName, 0o755);
43
+ // Run commands in a Docker shell
44
+ await fixture.shell([
45
+ docker, 'run', '--net=bridge', '--rm',
46
+ ...pathsToMount.flatMap(p => ['-v', `${p}:${p}`]),
47
+ ...['HOME', 'AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AWS_SESSION_TOKEN'].flatMap(e => ['-e', e]),
48
+ '-w', fixture.integTestDir,
49
+ '--cap-add=NET_ADMIN',
50
+ 'public.ecr.aws/ubuntu/ubuntu:24.04_stable',
51
+ `${scriptName}`,
52
+ ], {
53
+ stdio: 'inherit',
54
+ modEnv: {
55
+ AWS_ACCESS_KEY_ID: credentials.accessKeyId,
56
+ AWS_SECRET_ACCESS_KEY: credentials.secretAccessKey,
57
+ AWS_SESSION_TOKEN: credentials.sessionToken,
58
+ },
59
+ });
60
+ }
61
+ finally {
62
+ await proxy.stop();
63
+ }
64
+ }
65
+ /**
66
+ * Return the commands necessary to isolate the inside of the container from the internet,
67
+ * except by going through the proxy
68
+ */
69
+ function isolatedDockerCommands(proxyPort, caBundlePath) {
70
+ let defaultBridgeIp = '172.17.0.1';
71
+ // The host's default IP is different on CodeBuild than it is on other Docker systems.
72
+ if (process.env.CODEBUILD_BUILD_ARN) {
73
+ defaultBridgeIp = '172.18.0.1';
74
+ }
75
+ return [
76
+ 'echo Working...',
77
+ 'apt-get install -qqy curl net-tools iputils-ping dnsutils iptables > /dev/null',
78
+ '',
79
+ // Looking up `host.docker.internal` is necessary on MacOS Finch and Docker Desktop
80
+ // on Mac. The magic IP address is necessary on CodeBuild and GitHub Actions.
81
+ //
82
+ // I tried `--add-host=host.docker.internal:host-gateway` on the Linuxes but
83
+ // it doesn't change anything on either GHA or CodeBuild, so we're left with this
84
+ // backup solution.
85
+ 'gateway=$(dig +short host.docker.internal)',
86
+ 'if [[ -z "$gateway" ]]; then',
87
+ ` gateway=${defaultBridgeIp}`,
88
+ 'fi',
89
+ '',
90
+ '# Some iptables manipulation; there might be unnecessary commands in here, not an expert',
91
+ 'iptables -F',
92
+ 'iptables -X',
93
+ 'iptables -P INPUT DROP',
94
+ 'iptables -P OUTPUT DROP',
95
+ 'iptables -P FORWARD DROP',
96
+ 'iptables -A INPUT -i lo -j ACCEPT',
97
+ 'iptables -A OUTPUT -o lo -j ACCEPT',
98
+ 'iptables -A OUTPUT -d $gateway -j ACCEPT',
99
+ 'iptables -A INPUT -s $gateway -j ACCEPT',
100
+ '',
101
+ '',
102
+ `if [[ ! -f ${caBundlePath} ]]; then`,
103
+ ` echo "Could not find ${caBundlePath}, this will probably not go well. Exiting." >&2`,
104
+ ' exit 1',
105
+ 'fi',
106
+ '',
107
+ '# Configure a bunch of tools to work with the proxy',
108
+ 'echo "+-------------------------------------------------------------------------------------+"',
109
+ 'echo "| Direct network traffic has been blocked, everything must go through the proxy. |"',
110
+ 'echo "+-------------------------------------------------------------------------------------+"',
111
+ `export HTTP_PROXY=http://$gateway:${proxyPort}/`,
112
+ `export HTTPS_PROXY=http://$gateway:${proxyPort}/`,
113
+ `export NODE_EXTRA_CA_CERTS=${caBundlePath}`,
114
+ `export AWS_CA_BUNDLE=${caBundlePath}`,
115
+ `export SSL_CERT_FILE=${caBundlePath}`,
116
+ 'echo "Acquire::http::proxy \"$HTTP_PROXY\";" >> /etc/apt/apt.conf.d/95proxies',
117
+ 'echo "Acquire::https::proxy \"$HTTPS_PROXY\";" >> /etc/apt/apt.conf.d/95proxies',
118
+ ];
119
+ }
120
+ function renderEnv(env) {
121
+ return Object.entries(env).filter(([_, v]) => v).map(([k, v]) => `${k}='${v}'`).join(' ');
122
+ }
123
+ /**
124
+ * Find a broadly mountable parent of the given directory
125
+ *
126
+ * We don't want to just mount the top-level directory, because
127
+ * it could end up being `/var` (top-level dir of tmpdir on Mac).
128
+ */
129
+ function findMountableParent(dir) {
130
+ const candidates = [
131
+ os.tmpdir(),
132
+ process.env.TMPDIR,
133
+ process.env.HOME,
134
+ ];
135
+ for (const cand of candidates) {
136
+ if (cand === undefined) {
137
+ continue;
138
+ }
139
+ if (dir.startsWith(cand)) {
140
+ const suffix = dir.substring(cand.length);
141
+ const firstChildDir = suffix.split('/')[0];
142
+ return path.join(cand, firstChildDir);
143
+ }
144
+ }
145
+ return dir;
146
+ }
147
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJveHkuaW50ZWd0ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsicHJveHkuaW50ZWd0ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLDJCQUFvQztBQUNwQyx5QkFBeUI7QUFDekIsNkJBQTZCO0FBQzdCLHFEQUFpRDtBQUNqRCwyQ0FBbUQ7QUFDbkQseURBQXlFO0FBRXpFLE1BQU0sTUFBTSxHQUFHLE1BQUEsT0FBTyxDQUFDLEdBQUcsQ0FBQyxVQUFVLG1DQUFJLFFBQVEsQ0FBQztBQUVsRCxJQUFBLHNCQUFTLEVBQ1Asb0RBQW9ELEVBQ3BELElBQUEsaUNBQWtCLEVBQUMsS0FBSyxFQUFFLE9BQU8sRUFBRSxFQUFFO0lBQ25DLHdFQUF3RTtJQUN4RSxNQUFNLFdBQVcsR0FBRyxDQUFDLE1BQU0sT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDbkUsSUFBSSxNQUFNLEdBQUcsbUJBQW1CLENBQUMsV0FBVyxDQUFDLENBQUM7SUFFOUMsMENBQTBDO0lBQzFDLE1BQU0sUUFBUSxHQUFHO1FBQ2YsT0FBTyxTQUFTLENBQUMsT0FBTyxDQUFDLFdBQVcsRUFBRSxDQUFDLElBQUksV0FBVyxJQUFJLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxRQUFRLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEtBQUs7S0FDaEgsQ0FBQztJQUVGLE1BQU0sc0JBQXNCLENBQUMsT0FBTyxFQUFFLENBQUMsTUFBTSxDQUFDLEVBQUUsUUFBUSxDQUFDLENBQUM7QUFDNUQsQ0FBQyxDQUFDLENBQ0gsQ0FBQztBQUVGLEtBQUssVUFBVSxzQkFBc0IsQ0FBQyxPQUFvQixFQUFFLFlBQXNCLEVBQUUsWUFBc0I7SUFDeEcsWUFBWSxDQUFDLElBQUksQ0FDZixHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLEVBQ3JCLE9BQU8sQ0FBQyxZQUFZ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QyxNQUFNLGFBQWEsR0FBRyxNQUFNLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBRTNDLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsYUFBYSxDQUFDLENBQUM7UUFDeEMsQ0FBQztJQUNILENBQUM7SUFDRCxPQUFPLEdBQUcsQ0FBQztBQUNiLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBwcm9taXNlcyBhcyBmcyB9IGZyb20gJ2ZzJztcbmltcG9ydCAqIGFzIG9zIGZyb20gJ29zJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBpbnRlZ1Rlc3QgfSBmcm9tICcuLi8uLi9saWIvaW50ZWctdGVzdCc7XG5pbXBvcnQgeyBzdGFydFByb3h5U2VydmVyIH0gZnJvbSAnLi4vLi4vbGliL3Byb3h5JztcbmltcG9ydCB7IFRlc3RGaXh0dXJlLCB3aXRoRGVmYXVsdEZpeHR1cmUgfSBmcm9tICcuLi8uLi9saWIvd2l0aC1jZGstYXBwJztcblxuY29uc3QgZG9ja2VyID0gcHJvY2Vzcy5lbnYuQ0RLX0RPQ0tFUiA/PyAnZG9ja2VyJztcblxuaW50ZWdUZXN0KFxuICAnYWxsIGNhbGxzIGZyb20gaXNvbGF0ZWQgY29udGFpbmVyIGdvIHRocm91Z2ggcHJveHknLFxuICB3aXRoRGVmYXVsdEZpeHR1cmUoYXN5bmMgKGZpeHR1cmUpID0+IHtcbiAgICAvLyBGaW5kIHRoZSAnY2RrJyBjb21tYW5kIGFuZCBtYWtlIHN1cmUgaXQgaXMgbW91bnRlZCBpbnRvIHRoZSBjb250YWluZXJcbiAgICBjb25zdCBjZGtGdWxscGF0aCA9IChhd2FpdCBmaXh0dXJlLnNoZWxsKFsnd2hpY2gnLCAnY2RrJ10pKS50cmltKCk7XG4gICAgbGV0IGNka1RvcCA9IGZpbmRNb3VudGFibGVQYXJlbnQoY2RrRnVsbHBhdGgpO1xuXG4gICAgLy8gUnVuIGEgJ2NkayBkZXBsb3knIGluc2lkZSB0aGUgY29udGFpbmVyXG4gICAgY29uc3QgY29tbWFuZHMgPSBbXG4gICAgICBgZW52ICR7cmVuZGVyRW52KGZpeHR1cmUuY2RrU2hlbGxFbnYoKSl9ICR7Y2RrRnVsbHBhdGh9ICR7Zml4dHVyZS5jZGtEZXBsb3lDb21tYW5kTGluZSgndGVzdC0yJykuam9pbignICcpfSAtdmAsXG4gICAgXTtcblxuICAgIGF3YWl0IHJ1bkluSXNvbGF0ZWRDb250YWluZXIoZml4dHVyZSwgW2Nka1RvcF0sIGNvbW1hbmRzKTtcbiAgfSksXG4pO1xuXG5hc3luYyBmdW5jdGlvbiBydW5Jbklzb2xhdGVkQ29udGFpbmVyKGZpeHR1cmU6IFRlc3RGaXh0dXJlLCBwYXRoc1RvTW91bnQ6IHN0cmluZ1tdLCB0ZXN0Q29tbWFuZHM6IHN0cmluZ1tdKSB7XG4gIHBhdGhzVG9Nb3VudC5wdXNoKFxuICAgIGAke3Byb2Nlc3MuZW52LkhPTUV9YCxcbiAgICBmaXh0dXJlLmludGVnVGVzdERpcixcbiAgKTtcblxuICAvLyBSZXNvbHZlIGNyZWRlbnRpYWwgcHJvdmlkZXIgdG8gYW4gYWNjZXNzIGtleSB0aGF0IHdlIGNhbiBwYXNzIGludG8gdGhlIGNvbnRhaW5lclxuICBjb25zdCBjcmVkZW50aWFscyA9IGF3YWl0IGZpeHR1cmUuYXdzLmNyZWRlbnRpYWxzKCk7XG5cbiAgY29uc3QgcHJveHkgPSBhd2FpdCBzdGFydFByb3h5U2VydmVyKGZpeHR1cmUuaW50ZWdUZXN0RGlyKTtcbiAgdHJ5IHtcbiAgICBjb25zdCBwcm94eVBvcnQgPSBwcm94eS5wb3J0O1xuXG4gICAgY29uc3Qgc2V0dXBDb21tYW5kcyA9IFtcbiAgICAgICdhcHQtZ2V0IHVwZGF0ZSAtcXEnLFxuICAgICAgJ2FwdC1nZXQgaW5zdGFsbCAtcXF5IG5vZGVqcyA+IC9kZXYvbnVsbCcsXG4gICAgICAuLi5pc29sYXRlZERvY2tlckNvbW1hbmRzKHByb3h5UG9ydCwgcHJveHkuY2VydFBhdGgpLFxuICAgIF07XG5cbiAgICBjb25zdCBzY3JpcHROYW1lID0gcGF0aC5qb2luKGZpeHR1cmUuaW50ZWdUZXN0RGlyLCAnc2NyaXB0LnNoJyk7XG5cbiAgICAvLyBXcml0ZSBhIHNjcmlwdCBmaWxlXG4gICAgYXdhaXQgZnMud3JpdGVGaWxlKHNjcmlwdE5hbWUsIFtcbiAgICAgICcjIS9iaW4vYmFzaCcsXG4gICAgICAnc2V0IC14JyxcbiAgICAgICdzZXQgLWV1JyxcbiAgICAgIC4uLnNldHVwQ29tbWFuZHMsXG4gICAgICAuLi50ZXN0Q29tbWFuZHMsXG4gICAgXS5qb2luKCdcXG4nKSwgJ3V0Zi04Jyk7XG5cbiAgICBhd2FpdCBmcy5jaG1vZChzY3JpcHROYW1lLCAwbzc1NSk7XG5cbiAgICAvLyBSdW4gY29tbWFuZHMgaW4gYSBEb2NrZXIgc2hlbGxcbiAgICBhd2FpdCBmaXh0dXJlLnNoZWxsKFtcbiAgICAgIGRvY2tlciwgJ3J1bicsICctLW5ldD1icmlkZ2UnLCAnLS1ybScsXG4gICAgICAuLi5wYXRoc1RvTW91bnQuZmxhdE1hcChwID0+IFsnLXYnLCBgJHtwfToke3B9YF0pLFxuICAgICAgLi4uWydIT01FJywgJ0FXU19BQ0NFU1NfS0VZX0lEJywgJ0FXU19TRUNSRVRfQUNDRVNTX0tFWScsICdBV1NfU0VTU0lPTl9UT0tFTiddLmZsYXRNYXAoZSA9PiBbJy1lJywgZV0pLFxuICAgICAgJy13JywgZml4dHVyZS5pbnRlZ1Rlc3REaXIsXG4gICAgICAnLS1jYXAtYWRkPU5FVF9BRE1JTicsXG4gICAgICAncHVibGljLmVjci5hd3MvdWJ1bnR1L3VidW50dToyNC4wNF9zdGFibGUnLFxuICAgICAgYCR7c2NyaXB0TmFtZX1gLFxuICAgIF0sIHtcbiAgICAgIHN0ZGlvOiAnaW5oZXJpdCcsXG4gICAgICBtb2RFbnY6IHtcbiAgICAgICAgQVdTX0FDQ0VTU19LRVlfSUQ6IGNyZWRlbnRpYWxzLmFjY2Vzc0tleUlkLFxuICAgICAgICBBV1NfU0VDUkVUX0FDQ0VTU19LRVk6IGNyZWRlbnRpYWxzLnNlY3JldEFjY2Vzc0tleSxcbiAgICAgICAgQVdTX1NFU1NJT05fVE9LRU46IGNyZWRlbnRpYWxzLnNlc3Npb25Ub2tlbixcbiAgICAgIH0sXG4gICAgfSk7XG4gIH0gZmluYWxseSB7XG4gICAgYXdhaXQgcHJveHkuc3RvcCgpO1xuICB9XG59XG5cbi8qKlxuICogUmV0dXJuIHRoZSBjb21tYW5kcyBuZWNlc3NhcnkgdG8gaXNvbGF0ZSB0aGUgaW5zaWRlIG9mIHRoZSBjb250YWluZXIgZnJvbSB0aGUgaW50ZXJuZXQsXG4gKiBleGNlcHQgYnkgZ29pbmcgdGhyb3VnaCB0aGUgcHJveHlcbiAqL1xuZnVuY3Rpb24gaXNvbGF0ZWREb2NrZXJDb21tYW5kcyhwcm94eVBvcnQ6IG51bWJlciwgY2FCdW5kbGVQYXRoOiBzdHJpbmcpIHtcbiAgbGV0IGRlZmF1bHRCcmlkZ2VJcCA9ICcxNzIuMTcuMC4xJztcblxuICAvLyBUaGUgaG9zdCdzIGRlZmF1bHQgSVAgaXMgZGlmZmVyZW50IG9uIENvZGVCdWlsZCB0aGFuIGl0IGlzIG9uIG90aGVyIERvY2tlciBzeXN0ZW1zLlxuICBpZiAocHJvY2Vzcy5lbnYuQ09ERUJVSUxEX0JVSUxEX0FSTikge1xuICAgIGRlZmF1bHRCcmlkZ2VJcCA9ICcxNzIuMTguMC4xJztcbiAgfVxuXG4gIHJldHVybiBbXG4gICAgJ2VjaG8gV29ya2luZy4uLicsXG4gICAgJ2FwdC1nZXQgaW5zdGFsbCAtcXF5IGN1cmwgbmV0LXRvb2xzIGlwdXRpbHMtcGluZyBkbnN1dGlscyBpcHRhYmxlcyA+IC9kZXYvbnVsbCcsXG4gICAgJycsXG4gICAgLy8gTG9va2luZyB1cCBgaG9zdC5kb2NrZXIuaW50ZXJuYWxgIGlzIG5lY2Vzc2FyeSBvbiBNYWNPUyBGaW5jaCBhbmQgRG9ja2VyIERlc2t0b3BcbiAgICAvLyBvbiBNYWMuIFRoZSBtYWdpYyBJUCBhZGRyZXNzIGlzIG5lY2Vzc2FyeSBvbiBDb2RlQnVpbGQgYW5kIEdpdEh1YiBBY3Rpb25zLlxuICAgIC8vXG4gICAgLy8gSSB0cmllZCBgLS1hZGQtaG9zdD1ob3N0LmRvY2tlci5pbnRlcm5hbDpob3N0LWdhdGV3YXlgIG9uIHRoZSBMaW51eGVzIGJ1dFxuICAgIC8vIGl0IGRvZXNuJ3QgY2hhbmdlIGFueXRoaW5nIG9uIGVpdGhlciBHSEEgb3IgQ29kZUJ1aWxkLCBzbyB3ZSdyZSBsZWZ0IHdpdGggdGhpc1xuICAgIC8vIGJhY2t1cCBzb2x1dGlvbi5cbiAgICAnZ2F0ZXdheT0kKGRpZyArc2hvcnQgaG9zdC5kb2NrZXIuaW50ZXJuYWwpJyxcbiAgICAnaWYgW1sgLXogXCIkZ2F0ZXdheVwiIF1dOyB0aGVuJyxcbiAgICBgICBnYXRld2F5PSR7ZGVmYXVsdEJyaWRnZUlwfWAsXG4gICAgJ2ZpJyxcbiAgICAnJyxcbiAgICAnIyBTb21lIGlwdGFibGVzIG1hbmlwdWxhdGlvbjsgdGhlcmUgbWlnaHQgYmUgdW5uZWNlc3NhcnkgY29tbWFuZHMgaW4gaGVyZSwgbm90IGFuIGV4cGVydCcsXG4gICAgJ2lwdGFibGVzIC1GJyxcbiAgICAnaXB0YWJsZXMgLVgnLFxuICAgICdpcHRhYmxlcyAtUCBJTlBVVCBEUk9QJyxcbiAgICAnaXB0YWJsZXMgLVAgT1VUUFVUIERST1AnLFxuICAgICdpcHRhYmxlcyAtUCBGT1JXQVJEIERST1AnLFxuICAgICdpcHRhYmxlcyAtQSBJTlBVVCAtaSBsbyAtaiBBQ0NFUFQnLFxuICAgICdpcHRhYmxlcyAtQSBPVVRQVVQgLW8gbG8gLWogQUNDRVBUJyxcbiAgICAnaXB0YWJsZXMgLUEgT1VUUFVUIC1kICRnYXRld2F5IC1qIEFDQ0VQVCcsXG4gICAgJ2lwdGFibGVzIC1BIElOUFVUIC1zICRnYXRld2F5IC1qIEFDQ0VQVCcsXG4gICAgJycsXG4gICAgJycsXG4gICAgYGlmIFtbICEgLWYgJHtjYUJ1bmRsZVBhdGh9IF1dOyB0aGVuYCxcbiAgICBgICAgIGVjaG8gXCJDb3VsZCBub3QgZmluZCAke2NhQnVuZGxlUGF0aH0sIHRoaXMgd2lsbCBwcm9iYWJseSBub3QgZ28gd2VsbC4gRXhpdGluZy5cIiA+JjJgLFxuICAgICcgICAgZXhpdCAxJyxcbiAgICAnZmknLFxuICAgICcnLFxuICAgICcjIENvbmZpZ3VyZSBhIGJ1bmNoIG9mIHRvb2xzIHRvIHdvcmsgd2l0aCB0aGUgcHJveHknLFxuICAgICdlY2hvIFwiKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rXCInLFxuICAgICdlY2hvIFwifCAgRGlyZWN0IG5ldHdvcmsgdHJhZmZpYyBoYXMgYmVlbiBibG9ja2VkLCBldmVyeXRoaW5nIG11c3QgZ28gdGhyb3VnaCB0aGUgcHJveHkuICAgICB8XCInLFxuICAgICdlY2hvIFwiKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rXCInLFxuICAgIGBleHBvcnQgSFRUUF9QUk9YWT1odHRwOi8vJGdhdGV3YXk6JHtwcm94eVBvcnR9L2AsXG4gICAgYGV4cG9ydCBIVFRQU19QUk9YWT1odHRwOi8vJGdhdGV3YXk6JHtwcm94eVBvcnR9L2AsXG4gICAgYGV4cG9ydCBOT0RFX0VYVFJBX0NBX0NFUlRTPSR7Y2FCdW5kbGVQYXRofWAsXG4gICAgYGV4cG9ydCBBV1NfQ0FfQlVORExFPSR7Y2FCdW5kbGVQYXRofWAsXG4gICAgYGV4cG9ydCBTU0xfQ0VSVF9GSUxFPSR7Y2FCdW5kbGVQYXRofWAsXG4gICAgJ2VjaG8gXCJBY3F1aXJlOjpodHRwOjpwcm94eSBcXFwiJEhUVFBfUFJPWFlcXFwiO1wiID4+IC9ldGMvYXB0L2FwdC5jb25mLmQvOTVwcm94aWVzJyxcbiAgICAnZWNobyBcIkFjcXVpcmU6Omh0dHBzOjpwcm94eSBcXFwiJEhUVFBTX1BST1hZXFxcIjtcIiA+PiAvZXRjL2FwdC9hcHQuY29uZi5kLzk1cHJveGllcycsXG4gIF07XG59XG5cbmZ1bmN0aW9uIHJlbmRlckVudihlbnY6IFJlY29yZDxzdHJpbmcsIHN0cmluZyB8IHVuZGVmaW5lZD4pIHtcbiAgcmV0dXJuIE9iamVjdC5lbnRyaWVzKGVudikuZmlsdGVyKChbXywgdl0pID0+IHYpLm1hcCgoW2ssIHZdKSA9PiBgJHtrfT0nJHt2fSdgKS5qb2luKCcgJyk7XG59XG5cbi8qKlxuICogRmluZCBhIGJyb2FkbHkgbW91bnRhYmxlIHBhcmVudCBvZiB0aGUgZ2l2ZW4gZGlyZWN0b3J5XG4gKlxuICogV2UgZG9uJ3Qgd2FudCB0byBqdXN0IG1vdW50IHRoZSB0b3AtbGV2ZWwgZGlyZWN0b3J5LCBiZWNhdXNlXG4gKiBpdCBjb3VsZCBlbmQgdXAgYmVpbmcgYC92YXJgICh0b3AtbGV2ZWwgZGlyIG9mIHRtcGRpciBvbiBNYWMpLlxuICovXG5mdW5jdGlvbiBmaW5kTW91bnRhYmxlUGFyZW50KGRpcjogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgY2FuZGlkYXRlcyA9IFtcbiAgICBvcy50bXBkaXIoKSxcbiAgICBwcm9jZXNzLmVudi5UTVBESVIsXG4gICAgcHJvY2Vzcy5lbnYuSE9NRSxcbiAgXTtcbiAgZm9yIChjb25zdCBjYW5kIG9mIGNhbmRpZGF0ZXMpIHtcbiAgICBpZiAoY2FuZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG5cbiAgICBpZiAoZGlyLnN0YXJ0c1dpdGgoY2FuZCkpIHtcbiAgICAgIGNvbnN0IHN1ZmZpeCA9IGRpci5zdWJzdHJpbmcoY2FuZC5sZW5ndGgpO1xuICAgICAgY29uc3QgZmlyc3RDaGlsZERpciA9IHN1ZmZpeC5zcGxpdCgnLycpWzBdO1xuXG4gICAgICByZXR1cm4gcGF0aC5qb2luKGNhbmQsIGZpcnN0Q2hpbGREaXIpO1xuICAgIH1cbiAgfVxuICByZXR1cm4gZGlyO1xufVxuIl19
@@ -0,0 +1,167 @@
1
+ import { promises as fs } from 'fs';
2
+ import * as os from 'os';
3
+ import * as path from 'path';
4
+ import { integTest } from '../../lib/integ-test';
5
+ import { startProxyServer } from '../../lib/proxy';
6
+ import { TestFixture, withDefaultFixture } from '../../lib/with-cdk-app';
7
+
8
+ const docker = process.env.CDK_DOCKER ?? 'docker';
9
+
10
+ integTest(
11
+ 'all calls from isolated container go through proxy',
12
+ withDefaultFixture(async (fixture) => {
13
+ // Find the 'cdk' command and make sure it is mounted into the container
14
+ const cdkFullpath = (await fixture.shell(['which', 'cdk'])).trim();
15
+ let cdkTop = findMountableParent(cdkFullpath);
16
+
17
+ // Run a 'cdk deploy' inside the container
18
+ const commands = [
19
+ `env ${renderEnv(fixture.cdkShellEnv())} ${cdkFullpath} ${fixture.cdkDeployCommandLine('test-2').join(' ')} -v`,
20
+ ];
21
+
22
+ await runInIsolatedContainer(fixture, [cdkTop], commands);
23
+ }),
24
+ );
25
+
26
+ async function runInIsolatedContainer(fixture: TestFixture, pathsToMount: string[], testCommands: string[]) {
27
+ pathsToMount.push(
28
+ `${process.env.HOME}`,
29
+ fixture.integTestDir,
30
+ );
31
+
32
+ // Resolve credential provider to an access key that we can pass into the container
33
+ const credentials = await fixture.aws.credentials();
34
+
35
+ const proxy = await startProxyServer(fixture.integTestDir);
36
+ try {
37
+ const proxyPort = proxy.port;
38
+
39
+ const setupCommands = [
40
+ 'apt-get update -qq',
41
+ 'apt-get install -qqy nodejs > /dev/null',
42
+ ...isolatedDockerCommands(proxyPort, proxy.certPath),
43
+ ];
44
+
45
+ const scriptName = path.join(fixture.integTestDir, 'script.sh');
46
+
47
+ // Write a script file
48
+ await fs.writeFile(scriptName, [
49
+ '#!/bin/bash',
50
+ 'set -x',
51
+ 'set -eu',
52
+ ...setupCommands,
53
+ ...testCommands,
54
+ ].join('\n'), 'utf-8');
55
+
56
+ await fs.chmod(scriptName, 0o755);
57
+
58
+ // Run commands in a Docker shell
59
+ await fixture.shell([
60
+ docker, 'run', '--net=bridge', '--rm',
61
+ ...pathsToMount.flatMap(p => ['-v', `${p}:${p}`]),
62
+ ...['HOME', 'AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AWS_SESSION_TOKEN'].flatMap(e => ['-e', e]),
63
+ '-w', fixture.integTestDir,
64
+ '--cap-add=NET_ADMIN',
65
+ 'public.ecr.aws/ubuntu/ubuntu:24.04_stable',
66
+ `${scriptName}`,
67
+ ], {
68
+ stdio: 'inherit',
69
+ modEnv: {
70
+ AWS_ACCESS_KEY_ID: credentials.accessKeyId,
71
+ AWS_SECRET_ACCESS_KEY: credentials.secretAccessKey,
72
+ AWS_SESSION_TOKEN: credentials.sessionToken,
73
+ },
74
+ });
75
+ } finally {
76
+ await proxy.stop();
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Return the commands necessary to isolate the inside of the container from the internet,
82
+ * except by going through the proxy
83
+ */
84
+ function isolatedDockerCommands(proxyPort: number, caBundlePath: string) {
85
+ let defaultBridgeIp = '172.17.0.1';
86
+
87
+ // The host's default IP is different on CodeBuild than it is on other Docker systems.
88
+ if (process.env.CODEBUILD_BUILD_ARN) {
89
+ defaultBridgeIp = '172.18.0.1';
90
+ }
91
+
92
+ return [
93
+ 'echo Working...',
94
+ 'apt-get install -qqy curl net-tools iputils-ping dnsutils iptables > /dev/null',
95
+ '',
96
+ // Looking up `host.docker.internal` is necessary on MacOS Finch and Docker Desktop
97
+ // on Mac. The magic IP address is necessary on CodeBuild and GitHub Actions.
98
+ //
99
+ // I tried `--add-host=host.docker.internal:host-gateway` on the Linuxes but
100
+ // it doesn't change anything on either GHA or CodeBuild, so we're left with this
101
+ // backup solution.
102
+ 'gateway=$(dig +short host.docker.internal)',
103
+ 'if [[ -z "$gateway" ]]; then',
104
+ ` gateway=${defaultBridgeIp}`,
105
+ 'fi',
106
+ '',
107
+ '# Some iptables manipulation; there might be unnecessary commands in here, not an expert',
108
+ 'iptables -F',
109
+ 'iptables -X',
110
+ 'iptables -P INPUT DROP',
111
+ 'iptables -P OUTPUT DROP',
112
+ 'iptables -P FORWARD DROP',
113
+ 'iptables -A INPUT -i lo -j ACCEPT',
114
+ 'iptables -A OUTPUT -o lo -j ACCEPT',
115
+ 'iptables -A OUTPUT -d $gateway -j ACCEPT',
116
+ 'iptables -A INPUT -s $gateway -j ACCEPT',
117
+ '',
118
+ '',
119
+ `if [[ ! -f ${caBundlePath} ]]; then`,
120
+ ` echo "Could not find ${caBundlePath}, this will probably not go well. Exiting." >&2`,
121
+ ' exit 1',
122
+ 'fi',
123
+ '',
124
+ '# Configure a bunch of tools to work with the proxy',
125
+ 'echo "+-------------------------------------------------------------------------------------+"',
126
+ 'echo "| Direct network traffic has been blocked, everything must go through the proxy. |"',
127
+ 'echo "+-------------------------------------------------------------------------------------+"',
128
+ `export HTTP_PROXY=http://$gateway:${proxyPort}/`,
129
+ `export HTTPS_PROXY=http://$gateway:${proxyPort}/`,
130
+ `export NODE_EXTRA_CA_CERTS=${caBundlePath}`,
131
+ `export AWS_CA_BUNDLE=${caBundlePath}`,
132
+ `export SSL_CERT_FILE=${caBundlePath}`,
133
+ 'echo "Acquire::http::proxy \"$HTTP_PROXY\";" >> /etc/apt/apt.conf.d/95proxies',
134
+ 'echo "Acquire::https::proxy \"$HTTPS_PROXY\";" >> /etc/apt/apt.conf.d/95proxies',
135
+ ];
136
+ }
137
+
138
+ function renderEnv(env: Record<string, string | undefined>) {
139
+ return Object.entries(env).filter(([_, v]) => v).map(([k, v]) => `${k}='${v}'`).join(' ');
140
+ }
141
+
142
+ /**
143
+ * Find a broadly mountable parent of the given directory
144
+ *
145
+ * We don't want to just mount the top-level directory, because
146
+ * it could end up being `/var` (top-level dir of tmpdir on Mac).
147
+ */
148
+ function findMountableParent(dir: string): string {
149
+ const candidates = [
150
+ os.tmpdir(),
151
+ process.env.TMPDIR,
152
+ process.env.HOME,
153
+ ];
154
+ for (const cand of candidates) {
155
+ if (cand === undefined) {
156
+ continue;
157
+ }
158
+
159
+ if (dir.startsWith(cand)) {
160
+ const suffix = dir.substring(cand.length);
161
+ const firstChildDir = suffix.split('/')[0];
162
+
163
+ return path.join(cand, firstChildDir);
164
+ }
165
+ }
166
+ return dir;
167
+ }
@@ -50,4 +50,4 @@ new TestjsStack(app, 'TestjsStack');
50
50
  await fs.writeJson(path.join(context.integTestDir, 'cdk.json'), cdkJson);
51
51
  await shell.shell(['cdk', 'synth']);
52
52
  })));
53
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5pdC1qYXZhc2NyaXB0LmludGVndGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImluaXQtamF2YXNjcmlwdC5pbnRlZ3Rlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSw2QkFBNkI7QUFDN0IsK0JBQStCO0FBQy9CLG1DQUF5RjtBQUV6RixDQUFDLEtBQUssRUFBRSxZQUFZLENBQUMsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLEVBQUU7SUFDdkMsSUFBQSxlQUFTLEVBQUMsbUJBQW1CLFFBQVEsRUFBRSxFQUFFLElBQUEsNEJBQXNCLEVBQUMsSUFBQSxrQkFBWSxFQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsRUFBRTtRQUM3RixNQUFNLEtBQUssR0FBRyxpQkFBVyxDQUFDLFdBQVcsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUMvQyxNQUFNLE9BQU8sQ0FBQyxRQUFRLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztRQUUxQyxNQUFNLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxZQUFZLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQztRQUNqRSxNQUFNLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLEVBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQztRQUNwQyxNQUFNLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLG9EQUFvRDtRQUN0RixNQUFNLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLEVBQUUsS0FBSyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7UUFFMUMsTUFBTSxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFDdEMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ1AsQ0FBQyxDQUFDLENBQUM7QUFFSCxJQUFBLGVBQVMsRUFBQyw2QkFBNkIsRUFBRSxJQUFBLDRCQUFzQixFQUFDLElBQUEsa0JBQVksRUFBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLEVBQUU7SUFDN0YsdUVBQXVFO0lBQ3ZFLE1BQU0sS0FBSyxHQUFHLGlCQUFXLENBQUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQy9DLE1BQU0sT0FBTyxDQUFDLFFBQVEsQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO0lBRTFDLE1BQU0sS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLEtBQUssRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLFlBQVksRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDO0lBRTlELHFCQUFxQjtJQUNyQixNQUFNLEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsWUFBWSxFQUFFLG9CQUFvQixDQUFDLEVBQUU7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0NBdUIzRSxFQUFFLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUM7SUFFeEIsMkNBQTJDO0lBQzNDLE1BQU0sT0FBTyxHQUFHLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLEVBQUUsVUFBVSxDQUFDLENBQUMsQ0FBQztJQUMvRSxPQUFPLENBQUMsR0FBRyxHQUFHLHlCQUF5QixDQUFDO0lBQ3hDLE1BQU0sRUFBRSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLEVBQUUsVUFBVSxDQUFDLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFFekUsTUFBTSxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUM7QUFFdEMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzLWV4dHJhJztcbmltcG9ydCB7IGludGVnVGVzdCwgd2l0aFRlbXBvcmFyeURpcmVjdG9yeSwgU2hlbGxIZWxwZXIsIHdpdGhQYWNrYWdlcyB9IGZyb20gJy4uLy4uL2xpYic7XG5cblsnYXBwJywgJ3NhbXBsZS1hcHAnXS5mb3JFYWNoKHRlbXBsYXRlID0+IHtcbiAgaW50ZWdUZXN0KGBpbml0IGphdmFzY3JpcHQgJHt0ZW1wbGF0ZX1gLCB3aXRoVGVtcG9yYXJ5RGlyZWN0b3J5KHdpdGhQYWNrYWdlcyhhc3luYyAoY29udGV4dCkgPT4ge1xuICAgIGNvbnN0IHNoZWxsID0gU2hlbGxIZWxwZXIuZnJvbUNvbnRleHQoY29udGV4dCk7XG4gICAgYXdhaXQgY29udGV4dC5wYWNrYWdlcy5tYWtlQ2xpQXZhaWxhYmxlKCk7XG5cbiAgICBhd2FpdCBzaGVsbC5zaGVsbChbJ2NkaycsICdpbml0JywgJy1sJywgJ2phdmFzY3JpcHQnLCB0ZW1wbGF0ZV0pO1xuICAgIGF3YWl0IHNoZWxsLnNoZWxsKFsnbnBtJywgJ3BydW5lJ10pO1xuICAgIGF3YWl0IHNoZWxsLnNoZWxsKFsnbnBtJywgJ2xzJ10pOyAvLyB0aGlzIHdpbGwgZmFpbCBpZiB3ZSBoYXZlIHVubWV0IHBlZXIgZGVwZW5kZW5jaWVzXG4gICAgYXdhaXQgc2hlbGwuc2hlbGwoWyducG0nLCAncnVuJywgJ3Rlc3QnXSk7XG5cbiAgICBhd2FpdCBzaGVsbC5zaGVsbChbJ2NkaycsICdzeW50aCddKTtcbiAgfSkpKTtcbn0pO1xuXG5pbnRlZ1Rlc3QoJ1Rlc3QgaW1wb3J0aW5nIENESyBmcm9tIEVTTScsIHdpdGhUZW1wb3JhcnlEaXJlY3Rvcnkod2l0aFBhY2thZ2VzKGFzeW5jIChjb250ZXh0KSA9PiB7XG4gIC8vIFVzZSAnY2RrIGluaXQgLWw9amF2YXNjcmlwdCcgdG8gZ2V0IHNldCB1cCwgYnV0IHVzZSBhIGRpZmZlcmVudCBmaWxlXG4gIGNvbnN0IHNoZWxsID0gU2hlbGxIZWxwZXIuZnJvbUNvbnRleHQoY29udGV4dCk7XG4gIGF3YWl0IGNvbnRleHQucGFja2FnZXMubWFrZUNsaUF2YWlsYWJsZSgpO1xuXG4gIGF3YWl0IHNoZWxsLnNoZWxsKFsnY2RrJywgJ2luaXQnLCAnLWwnLCAnamF2YXNjcmlwdCcsICdhcHAnXSk7XG5cbiAgLy8gUmV3cml0ZSBzb21lIGZpbGVzXG4gIGF3YWl0IGZzLndyaXRlRmlsZShwYXRoLmpvaW4oY29udGV4dC5pbnRlZ1Rlc3REaXIsICduZXctZW50cnlwb2ludC5tanMnKSwgYFxuLy8gVGVzdCBtdWx0aXBsZSBzdHlsZXMgb2YgaW1wb3J0c1xuaW1wb3J0IHsgU3RhY2ssIGF3c19zbnMgYXMgc25zIH0gZnJvbSAnYXdzLWNkay1saWInO1xuaW1wb3J0IHsgU3FzU3Vic2NyaXB0aW9uIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLXNucy1zdWJzY3JpcHRpb25zJztcbmltcG9ydCAqIGFzIHNxcyBmcm9tICdhd3MtY2RrLWxpYi9hd3Mtc3FzJztcbmltcG9ydCAqIGFzIGNkayBmcm9tICdhd3MtY2RrLWxpYic7XG5cbmNsYXNzIFRlc3Rqc1N0YWNrIGV4dGVuZHMgU3RhY2sge1xuICBjb25zdHJ1Y3RvcihzY29wZSwgaWQsIHByb3BzKSB7XG4gICAgc3VwZXIoc2NvcGUsIGlkLCBwcm9wcyk7XG5cbiAgICBjb25zdCBxdWV1ZSA9IG5ldyBzcXMuUXVldWUodGhpcywgJ1Rlc3Rqc1F1ZXVlJywge1xuICAgICAgdmlzaWJpbGl0eVRpbWVvdXQ6IGNkay5EdXJhdGlvbi5zZWNvbmRzKDMwMClcbiAgICB9KTtcblxuICAgIGNvbnN0IHRvcGljID0gbmV3IHNucy5Ub3BpYyh0aGlzLCAnVGVzdGpzVG9waWMnKTtcblxuICAgIHRvcGljLmFkZFN1YnNjcmlwdGlvbihuZXcgU3FzU3Vic2NyaXB0aW9uKHF1ZXVlKSk7XG4gIH1cbn1cblxuY29uc3QgYXBwID0gbmV3IGNkay5BcHAoKTtcbm5ldyBUZXN0anNTdGFjayhhcHAsICdUZXN0anNTdGFjaycpO1xuYCwgeyBlbmNvZGluZzogJ3V0Zi04JyB9KTtcblxuICAvLyBSZXdyaXRlICdjZGsuanNvbicgdG8gdXNlIG5ldyBlbnRyeXBvaW50XG4gIGNvbnN0IGNka0pzb24gPSBhd2FpdCBmcy5yZWFkSnNvbihwYXRoLmpvaW4oY29udGV4dC5pbnRlZ1Rlc3REaXIsICdjZGsuanNvbicpKTtcbiAgY2RrSnNvbi5hcHAgPSAnbm9kZSBuZXctZW50cnlwb2ludC5tanMnO1xuICBhd2FpdCBmcy53cml0ZUpzb24ocGF0aC5qb2luKGNvbnRleHQuaW50ZWdUZXN0RGlyLCAnY2RrLmpzb24nKSwgY2RrSnNvbik7XG5cbiAgYXdhaXQgc2hlbGwuc2hlbGwoWydjZGsnLCAnc3ludGgnXSk7XG5cbn0pKSk7XG4iXX0=
53
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5pdC1qYXZhc2NyaXB0LmludGVndGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImluaXQtamF2YXNjcmlwdC5pbnRlZ3Rlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSw2QkFBNkI7QUFDN0IsK0JBQStCO0FBQy9CLG1DQUF5RjtBQUV6RixDQUFDLEtBQUssRUFBRSxZQUFZLENBQUMsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLEVBQUU7SUFDdkMsSUFBQSxlQUFTLEVBQUMsbUJBQW1CLFFBQVEsRUFBRSxFQUFFLElBQUEsNEJBQXNCLEVBQUMsSUFBQSxrQkFBWSxFQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsRUFBRTtRQUM3RixNQUFNLEtBQUssR0FBRyxpQkFBVyxDQUFDLFdBQVcsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUMvQyxNQUFNLE9BQU8sQ0FBQyxRQUFRLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztRQUUxQyxNQUFNLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxZQUFZLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQztRQUNqRSxNQUFNLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLEVBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQztRQUNwQyxNQUFNLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLG9EQUFvRDtRQUN0RixNQUFNLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLEVBQUUsS0FBSyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7UUFFMUMsTUFBTSxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFDdEMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ1AsQ0FBQyxDQUFDLENBQUM7QUFFSCxJQUFBLGVBQVMsRUFBQyw2QkFBNkIsRUFBRSxJQUFBLDRCQUFzQixFQUFDLElBQUEsa0JBQVksRUFBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLEVBQUU7SUFDN0YsdUVBQXVFO0lBQ3ZFLE1BQU0sS0FBSyxHQUFHLGlCQUFXLENBQUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQy9DLE1BQU0sT0FBTyxDQUFDLFFBQVEsQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO0lBRTFDLE1BQU0sS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLEtBQUssRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLFlBQVksRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDO0lBRTlELHFCQUFxQjtJQUNyQixNQUFNLEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsWUFBWSxFQUFFLG9CQUFvQixDQUFDLEVBQUU7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0NBdUIzRSxFQUFFLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUM7SUFFeEIsMkNBQTJDO0lBQzNDLE1BQU0sT0FBTyxHQUFHLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLEVBQUUsVUFBVSxDQUFDLENBQUMsQ0FBQztJQUMvRSxPQUFPLENBQUMsR0FBRyxHQUFHLHlCQUF5QixDQUFDO0lBQ3hDLE1BQU0sRUFBRSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLEVBQUUsVUFBVSxDQUFDLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFFekUsTUFBTSxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUM7QUFDdEMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzLWV4dHJhJztcbmltcG9ydCB7IGludGVnVGVzdCwgd2l0aFRlbXBvcmFyeURpcmVjdG9yeSwgU2hlbGxIZWxwZXIsIHdpdGhQYWNrYWdlcyB9IGZyb20gJy4uLy4uL2xpYic7XG5cblsnYXBwJywgJ3NhbXBsZS1hcHAnXS5mb3JFYWNoKHRlbXBsYXRlID0+IHtcbiAgaW50ZWdUZXN0KGBpbml0IGphdmFzY3JpcHQgJHt0ZW1wbGF0ZX1gLCB3aXRoVGVtcG9yYXJ5RGlyZWN0b3J5KHdpdGhQYWNrYWdlcyhhc3luYyAoY29udGV4dCkgPT4ge1xuICAgIGNvbnN0IHNoZWxsID0gU2hlbGxIZWxwZXIuZnJvbUNvbnRleHQoY29udGV4dCk7XG4gICAgYXdhaXQgY29udGV4dC5wYWNrYWdlcy5tYWtlQ2xpQXZhaWxhYmxlKCk7XG5cbiAgICBhd2FpdCBzaGVsbC5zaGVsbChbJ2NkaycsICdpbml0JywgJy1sJywgJ2phdmFzY3JpcHQnLCB0ZW1wbGF0ZV0pO1xuICAgIGF3YWl0IHNoZWxsLnNoZWxsKFsnbnBtJywgJ3BydW5lJ10pO1xuICAgIGF3YWl0IHNoZWxsLnNoZWxsKFsnbnBtJywgJ2xzJ10pOyAvLyB0aGlzIHdpbGwgZmFpbCBpZiB3ZSBoYXZlIHVubWV0IHBlZXIgZGVwZW5kZW5jaWVzXG4gICAgYXdhaXQgc2hlbGwuc2hlbGwoWyducG0nLCAncnVuJywgJ3Rlc3QnXSk7XG5cbiAgICBhd2FpdCBzaGVsbC5zaGVsbChbJ2NkaycsICdzeW50aCddKTtcbiAgfSkpKTtcbn0pO1xuXG5pbnRlZ1Rlc3QoJ1Rlc3QgaW1wb3J0aW5nIENESyBmcm9tIEVTTScsIHdpdGhUZW1wb3JhcnlEaXJlY3Rvcnkod2l0aFBhY2thZ2VzKGFzeW5jIChjb250ZXh0KSA9PiB7XG4gIC8vIFVzZSAnY2RrIGluaXQgLWw9amF2YXNjcmlwdCcgdG8gZ2V0IHNldCB1cCwgYnV0IHVzZSBhIGRpZmZlcmVudCBmaWxlXG4gIGNvbnN0IHNoZWxsID0gU2hlbGxIZWxwZXIuZnJvbUNvbnRleHQoY29udGV4dCk7XG4gIGF3YWl0IGNvbnRleHQucGFja2FnZXMubWFrZUNsaUF2YWlsYWJsZSgpO1xuXG4gIGF3YWl0IHNoZWxsLnNoZWxsKFsnY2RrJywgJ2luaXQnLCAnLWwnLCAnamF2YXNjcmlwdCcsICdhcHAnXSk7XG5cbiAgLy8gUmV3cml0ZSBzb21lIGZpbGVzXG4gIGF3YWl0IGZzLndyaXRlRmlsZShwYXRoLmpvaW4oY29udGV4dC5pbnRlZ1Rlc3REaXIsICduZXctZW50cnlwb2ludC5tanMnKSwgYFxuLy8gVGVzdCBtdWx0aXBsZSBzdHlsZXMgb2YgaW1wb3J0c1xuaW1wb3J0IHsgU3RhY2ssIGF3c19zbnMgYXMgc25zIH0gZnJvbSAnYXdzLWNkay1saWInO1xuaW1wb3J0IHsgU3FzU3Vic2NyaXB0aW9uIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLXNucy1zdWJzY3JpcHRpb25zJztcbmltcG9ydCAqIGFzIHNxcyBmcm9tICdhd3MtY2RrLWxpYi9hd3Mtc3FzJztcbmltcG9ydCAqIGFzIGNkayBmcm9tICdhd3MtY2RrLWxpYic7XG5cbmNsYXNzIFRlc3Rqc1N0YWNrIGV4dGVuZHMgU3RhY2sge1xuICBjb25zdHJ1Y3RvcihzY29wZSwgaWQsIHByb3BzKSB7XG4gICAgc3VwZXIoc2NvcGUsIGlkLCBwcm9wcyk7XG5cbiAgICBjb25zdCBxdWV1ZSA9IG5ldyBzcXMuUXVldWUodGhpcywgJ1Rlc3Rqc1F1ZXVlJywge1xuICAgICAgdmlzaWJpbGl0eVRpbWVvdXQ6IGNkay5EdXJhdGlvbi5zZWNvbmRzKDMwMClcbiAgICB9KTtcblxuICAgIGNvbnN0IHRvcGljID0gbmV3IHNucy5Ub3BpYyh0aGlzLCAnVGVzdGpzVG9waWMnKTtcblxuICAgIHRvcGljLmFkZFN1YnNjcmlwdGlvbihuZXcgU3FzU3Vic2NyaXB0aW9uKHF1ZXVlKSk7XG4gIH1cbn1cblxuY29uc3QgYXBwID0gbmV3IGNkay5BcHAoKTtcbm5ldyBUZXN0anNTdGFjayhhcHAsICdUZXN0anNTdGFjaycpO1xuYCwgeyBlbmNvZGluZzogJ3V0Zi04JyB9KTtcblxuICAvLyBSZXdyaXRlICdjZGsuanNvbicgdG8gdXNlIG5ldyBlbnRyeXBvaW50XG4gIGNvbnN0IGNka0pzb24gPSBhd2FpdCBmcy5yZWFkSnNvbihwYXRoLmpvaW4oY29udGV4dC5pbnRlZ1Rlc3REaXIsICdjZGsuanNvbicpKTtcbiAgY2RrSnNvbi5hcHAgPSAnbm9kZSBuZXctZW50cnlwb2ludC5tanMnO1xuICBhd2FpdCBmcy53cml0ZUpzb24ocGF0aC5qb2luKGNvbnRleHQuaW50ZWdUZXN0RGlyLCAnY2RrLmpzb24nKSwgY2RrSnNvbik7XG5cbiAgYXdhaXQgc2hlbGwuc2hlbGwoWydjZGsnLCAnc3ludGgnXSk7XG59KSkpO1xuIl19
@@ -55,5 +55,4 @@ new TestjsStack(app, 'TestjsStack');
55
55
  await fs.writeJson(path.join(context.integTestDir, 'cdk.json'), cdkJson);
56
56
 
57
57
  await shell.shell(['cdk', 'synth']);
58
-
59
58
  })));
@@ -34,7 +34,7 @@ TYPESCRIPT_VERSIONS.forEach(tsVersion => {
34
34
  await removeDevDependencies(context);
35
35
  await shell.shell(['npm', 'install', '--save-dev', `typescript@${tsVersion}`]);
36
36
  // After we've removed devDependencies we need to re-install ts-node because it's necessary for `cdk synth`
37
- await shell.shell(['npm', 'install', '--save-dev', `ts-node@^10`]);
37
+ await shell.shell(['npm', 'install', '--save-dev', 'ts-node@^10']);
38
38
  await shell.shell(['npm', 'install']); // Older versions of npm require this to be a separate step from the one above
39
39
  await shell.shell(['npx', 'tsc', '--version']);
40
40
  await shell.shell(['npm', 'prune']);
@@ -51,4 +51,4 @@ async function removeDevDependencies(context) {
51
51
  delete pj.devDependencies;
52
52
  await fs_1.promises.writeFile(filename, JSON.stringify(pj, undefined, 2), { encoding: 'utf-8' });
53
53
  }
54
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5pdC10eXBlc2NyaXB0LWFwcC5pbnRlZ3Rlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbml0LXR5cGVzY3JpcHQtYXBwLmludGVndGVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLDJCQUFvQztBQUNwQyw2QkFBNkI7QUFDN0IsbUNBQW9IO0FBQ3BILHVDQUE4RjtBQUU5RixDQUFDLEtBQUssRUFBRSxZQUFZLENBQUMsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLEVBQUU7SUFDdkMsSUFBQSxlQUFTLEVBQUMsbUJBQW1CLFFBQVEsRUFBRSxFQUFFLElBQUEsNEJBQXNCLEVBQUMsSUFBQSxrQkFBWSxFQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsRUFBRTtRQUM3RixNQUFNLEtBQUssR0FBRyxpQkFBVyxDQUFDLFdBQVcsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUMvQyxNQUFNLE9BQU8sQ0FBQyxRQUFRLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztRQUUxQyxNQUFNLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxZQUFZLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQztRQUVqRSxNQUFNLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLEVBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQztRQUNwQyxNQUFNLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLG9EQUFvRDtRQUN0RixNQUFNLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLEVBQUUsS0FBSyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUM7UUFDM0MsTUFBTSxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsS0FBSyxFQUFFLEtBQUssRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO1FBRTFDLE1BQU0sS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLEtBQUssRUFBRSxP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MUIsTUFBTSxhQUFFLENBQUMsU0FBUyxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLEVBQUUsRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDLEVBQUUsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQztBQUN4RixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgcHJvbWlzZXMgYXMgZnMgfSBmcm9tICdmcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHsgaW50ZWdUZXN0LCB3aXRoVGVtcG9yYXJ5RGlyZWN0b3J5LCBTaGVsbEhlbHBlciwgd2l0aFBhY2thZ2VzLCBUZW1wb3JhcnlEaXJlY3RvcnlDb250ZXh0IH0gZnJvbSAnLi4vLi4vbGliJztcbmltcG9ydCB7IHR5cGVzY3JpcHRWZXJzaW9uc1N5bmMsIHR5cGVzY3JpcHRWZXJzaW9uc1lvdW5nZXJUaGFuRGF5c1N5bmMgfSBmcm9tICcuLi8uLi9saWIvbnBtJztcblxuWydhcHAnLCAnc2FtcGxlLWFwcCddLmZvckVhY2godGVtcGxhdGUgPT4ge1xuICBpbnRlZ1Rlc3QoYHR5cGVzY3JpcHQgaW5pdCAke3RlbXBsYXRlfWAsIHdpdGhUZW1wb3JhcnlEaXJlY3Rvcnkod2l0aFBhY2thZ2VzKGFzeW5jIChjb250ZXh0KSA9PiB7XG4gICAgY29uc3Qgc2hlbGwgPSBTaGVsbEhlbHBlci5mcm9tQ29udGV4dChjb250ZXh0KTtcbiAgICBhd2FpdCBjb250ZXh0LnBhY2thZ2VzLm1ha2VDbGlBdmFpbGFibGUoKTtcblxuICAgIGF3YWl0IHNoZWxsLnNoZWxsKFsnY2RrJywgJ2luaXQnLCAnLWwnLCAndHlwZXNjcmlwdCcsIHRlbXBsYXRlXSk7XG5cbiAgICBhd2FpdCBzaGVsbC5zaGVsbChbJ25wbScsICdwcnVuZSddKTtcbiAgICBhd2FpdCBzaGVsbC5zaGVsbChbJ25wbScsICdscyddKTsgLy8gdGhpcyB3aWxsIGZhaWwgaWYgd2UgaGF2ZSB1bm1ldCBwZWVyIGRlcGVuZGVuY2llc1xuICAgIGF3YWl0IHNoZWxsLnNoZWxsKFsnbnBtJywgJ3J1bicsICdidWlsZCddKTtcbiAgICBhd2FpdCBzaGVsbC5zaGVsbChbJ25wbScsICdydW4nLCAndGVzdCddKTtcblxuICAgIGF3YWl0IHNoZWxsLnNoZWxsKFsnY2RrJywgJ3N5bnRoJ10pO1xuICB9KSkpO1xufSk7XG5cbi8vIFNhbWUgYXMgaHR0cHM6Ly9naXRodWIuY29tL0RlZmluaXRlbHlUeXBlZC9EZWZpbml0ZWx5VHlwZWQ/dGFiPXJlYWRtZS1vdi1maWxlI3N1cHBvcnQtd2luZG93XG5jb25zdCBUWVBFU0NSSVBUX1ZFUlNJT05fQUdFX0RBWVMgPSAyICogMzY1O1xuXG5jb25zdCBUWVBFU0NSSVBUX1ZFUlNJT05TID0gdHlwZXNjcmlwdFZlcnNpb25zWW91bmdlclRoYW5EYXlzU3luYyhUWVBFU0NSSVBUX1ZFUlNJT05fQUdFX0RBWVMsIHR5cGVzY3JpcHRWZXJzaW9uc1N5bmMoKSk7XG5cbi8qKlxuICogVGVzdCBvdXIgZ2VuZXJhdGVkIGNvZGUgd2l0aCB2YXJpb3VzIHZlcnNpb25zIG9mIFR5cGVTY3JpcHRcbiAqL1xuVFlQRVNDUklQVF9WRVJTSU9OUy5mb3JFYWNoKHRzVmVyc2lvbiA9PiB7XG4gIGludGVnVGVzdChgdHlwZXNjcmlwdCAke3RzVmVyc2lvbn0gaW5pdCBhcHBgLCB3aXRoVGVtcG9yYXJ5RGlyZWN0b3J5KHdpdGhQYWNrYWdlcyhhc3luYyAoY29udGV4dCkgPT4ge1xuICAgIGNvbnN0IHNoZWxsID0gU2hlbGxIZWxwZXIuZnJvbUNvbnRleHQoY29udGV4dCk7XG4gICAgYXdhaXQgY29udGV4dC5wYWNrYWdlcy5tYWtlQ2xpQXZhaWxhYmxlKCk7XG5cbiAgICBhd2FpdCBzaGVsbC5zaGVsbChbJ25vZGUnLCAnLS12ZXJzaW9uJ10pO1xuICAgIGF3YWl0IHNoZWxsLnNoZWxsKFsnbnBtJywgJy0tdmVyc2lvbiddKTtcblxuICAgIGF3YWl0IHNoZWxsLnNoZWxsKFsnY2RrJywgJ2luaXQnLCAnLWwnLCAndHlwZXNjcmlwdCcsICdhcHAnLCAnLS1nZW5lcmF0ZS1vbmx5J10pO1xuXG4gICAgLy8gTmVjZXNzYXJ5IGJlY2F1c2UgcmVjZW50IHZlcnNpb25zIG9mIHRzLWplc3QgcmVxdWlyZSBUeXBlU2NyaXB0Pj00LjMgYnV0IHdlXG4gICAgLy8gc3RpbGwgd2FudCB0byB0ZXN0IHdpdGggb2xkZXIgdmVyc2lvbnMgYXMgd2VsbC5cbiAgICBhd2FpdCByZW1vdmVEZXZEZXBlbmRlbmNpZXMoY29udGV4dCk7XG5cbiAgICBhd2FpdCBzaGVsbC5zaGVsbChbJ25wbScsICdpbnN0YWxsJywgJy0tc2F2ZS1kZXYnLCBgdHlwZXNjcmlwdEAke3RzVmVyc2lvbn1gXSk7XG5cbiAgICAvLyBBZnRlciB3ZSd2ZSByZW1vdmVkIGRldkRlcGVuZGVuY2llcyB3ZSBuZWVkIHRvIHJlLWluc3RhbGwgdHMtbm9kZSBiZWNhdXNlIGl0J3MgbmVjZXNzYXJ5IGZvciBgY2RrIHN5bnRoYFxuICAgIGF3YWl0IHNoZWxsLnNoZWxsKFsnbnBtJywgJ2luc3RhbGwnLCAnLS1zYXZlLWRldicsIGB0cy1ub2RlQF4xMGBdKTtcblxuICAgIGF3YWl0IHNoZWxsLnNoZWxsKFsnbnBtJywgJ2luc3RhbGwnXSk7IC8vIE9sZGVyIHZlcnNpb25zIG9mIG5wbSByZXF1aXJlIHRoaXMgdG8gYmUgYSBzZXBhcmF0ZSBzdGVwIGZyb20gdGhlIG9uZSBhYm92ZVxuICAgIGF3YWl0IHNoZWxsLnNoZWxsKFsnbnB4JywgJ3RzYycsICctLXZlcnNpb24nXSk7XG4gICAgYXdhaXQgc2hlbGwuc2hlbGwoWyducG0nLCAncHJ1bmUnXSk7XG4gICAgYXdhaXQgc2hlbGwuc2hlbGwoWyducG0nLCAnbHMnXSk7IC8vIHRoaXMgd2lsbCBmYWlsIGlmIHdlIGhhdmUgdW5tZXQgcGVlciBkZXBlbmRlbmNpZXNcblxuICAgIC8vIFdlIGp1c3QgcmVtb3ZlZCB0aGUgJ2plc3QnIGRlcGVuZGVuY3kgc28gcmVtb3ZlIHRoZSB0ZXN0cyBhcyB3ZWxsIGJlY2F1c2UgdGhleSB3b24ndCBjb21waWxlXG4gICAgYXdhaXQgc2hlbGwuc2hlbGwoWydybScsICctcmYnLCAndGVzdC8nXSk7XG5cbiAgICBhd2FpdCBzaGVsbC5zaGVsbChbJ25wbScsICdydW4nLCAnYnVpbGQnXSk7XG4gICAgYXdhaXQgc2hlbGwuc2hlbGwoWydjZGsnLCAnc3ludGgnXSk7XG4gIH0pKSk7XG59KTtcblxuYXN5bmMgZnVuY3Rpb24gcmVtb3ZlRGV2RGVwZW5kZW5jaWVzKGNvbnRleHQ6IFRlbXBvcmFyeURpcmVjdG9yeUNvbnRleHQpIHtcbiAgY29uc3QgZmlsZW5hbWUgPSBwYXRoLmpvaW4oY29udGV4dC5pbnRlZ1Rlc3REaXIsICdwYWNrYWdlLmpzb24nKTtcbiAgY29uc3QgcGogPSBKU09OLnBhcnNlKGF3YWl0IGZzLnJlYWRGaWxlKGZpbGVuYW1lLCB7IGVuY29kaW5nOiAndXRmLTgnIH0pKTtcbiAgZGVsZXRlIHBqLmRldkRlcGVuZGVuY2llcztcbiAgYXdhaXQgZnMud3JpdGVGaWxlKGZpbGVuYW1lLCBKU09OLnN0cmluZ2lmeShwaiwgdW5kZWZpbmVkLCAyKSwgeyBlbmNvZGluZzogJ3V0Zi04JyB9KTtcbn1cbiJdfQ==
54
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5pdC10eXBlc2NyaXB0LWFwcC5pbnRlZ3Rlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbml0LXR5cGVzY3JpcHQtYXBwLmludGVndGVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLDJCQUFvQztBQUNwQyw2QkFBNkI7QUFDN0IsbUNBQW9IO0FBQ3BILHVDQUE4RjtBQUU5RixDQUFDLEtBQUssRUFBRSxZQUFZLENBQUMsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLEVBQUU7SUFDdkMsSUFBQSxlQUFTLEVBQUMsbUJBQW1CLFFBQVEsRUFBRSxFQUFFLElBQUEsNEJBQXNCLEVBQUMsSUFBQSxrQkFBWSxFQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsRUFBRTtRQUM3RixNQUFNLEtBQUssR0FBRyxpQkFBVyxDQUFDLFdBQVcsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUMvQyxNQUFNLE9BQU8sQ0FBQyxRQUFRLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztRQUUxQyxNQUFNLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxZQUFZLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQztRQUVqRSxNQUFNLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLEVBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQztRQUNwQyxNQUFNLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLG9EQUFvRDtRQUN0RixNQUFNLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLEVBQUUsS0FBSyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUM7UUFDM0MsTUFBTSxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsS0FBSyxFQUFFLEtBQUssRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO1FBRTFDLE1BQU0sS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLEtBQUssRUFBRSxP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MUIsTUFBTSxhQUFFLENBQUMsU0FBUyxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLEVBQUUsRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDLEVBQUUsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQztBQUN4RixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgcHJvbWlzZXMgYXMgZnMgfSBmcm9tICdmcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHsgaW50ZWdUZXN0LCB3aXRoVGVtcG9yYXJ5RGlyZWN0b3J5LCBTaGVsbEhlbHBlciwgd2l0aFBhY2thZ2VzLCBUZW1wb3JhcnlEaXJlY3RvcnlDb250ZXh0IH0gZnJvbSAnLi4vLi4vbGliJztcbmltcG9ydCB7IHR5cGVzY3JpcHRWZXJzaW9uc1N5bmMsIHR5cGVzY3JpcHRWZXJzaW9uc1lvdW5nZXJUaGFuRGF5c1N5bmMgfSBmcm9tICcuLi8uLi9saWIvbnBtJztcblxuWydhcHAnLCAnc2FtcGxlLWFwcCddLmZvckVhY2godGVtcGxhdGUgPT4ge1xuICBpbnRlZ1Rlc3QoYHR5cGVzY3JpcHQgaW5pdCAke3RlbXBsYXRlfWAsIHdpdGhUZW1wb3JhcnlEaXJlY3Rvcnkod2l0aFBhY2thZ2VzKGFzeW5jIChjb250ZXh0KSA9PiB7XG4gICAgY29uc3Qgc2hlbGwgPSBTaGVsbEhlbHBlci5mcm9tQ29udGV4dChjb250ZXh0KTtcbiAgICBhd2FpdCBjb250ZXh0LnBhY2thZ2VzLm1ha2VDbGlBdmFpbGFibGUoKTtcblxuICAgIGF3YWl0IHNoZWxsLnNoZWxsKFsnY2RrJywgJ2luaXQnLCAnLWwnLCAndHlwZXNjcmlwdCcsIHRlbXBsYXRlXSk7XG5cbiAgICBhd2FpdCBzaGVsbC5zaGVsbChbJ25wbScsICdwcnVuZSddKTtcbiAgICBhd2FpdCBzaGVsbC5zaGVsbChbJ25wbScsICdscyddKTsgLy8gdGhpcyB3aWxsIGZhaWwgaWYgd2UgaGF2ZSB1bm1ldCBwZWVyIGRlcGVuZGVuY2llc1xuICAgIGF3YWl0IHNoZWxsLnNoZWxsKFsnbnBtJywgJ3J1bicsICdidWlsZCddKTtcbiAgICBhd2FpdCBzaGVsbC5zaGVsbChbJ25wbScsICdydW4nLCAndGVzdCddKTtcblxuICAgIGF3YWl0IHNoZWxsLnNoZWxsKFsnY2RrJywgJ3N5bnRoJ10pO1xuICB9KSkpO1xufSk7XG5cbi8vIFNhbWUgYXMgaHR0cHM6Ly9naXRodWIuY29tL0RlZmluaXRlbHlUeXBlZC9EZWZpbml0ZWx5VHlwZWQ/dGFiPXJlYWRtZS1vdi1maWxlI3N1cHBvcnQtd2luZG93XG5jb25zdCBUWVBFU0NSSVBUX1ZFUlNJT05fQUdFX0RBWVMgPSAyICogMzY1O1xuXG5jb25zdCBUWVBFU0NSSVBUX1ZFUlNJT05TID0gdHlwZXNjcmlwdFZlcnNpb25zWW91bmdlclRoYW5EYXlzU3luYyhUWVBFU0NSSVBUX1ZFUlNJT05fQUdFX0RBWVMsIHR5cGVzY3JpcHRWZXJzaW9uc1N5bmMoKSk7XG5cbi8qKlxuICogVGVzdCBvdXIgZ2VuZXJhdGVkIGNvZGUgd2l0aCB2YXJpb3VzIHZlcnNpb25zIG9mIFR5cGVTY3JpcHRcbiAqL1xuVFlQRVNDUklQVF9WRVJTSU9OUy5mb3JFYWNoKHRzVmVyc2lvbiA9PiB7XG4gIGludGVnVGVzdChgdHlwZXNjcmlwdCAke3RzVmVyc2lvbn0gaW5pdCBhcHBgLCB3aXRoVGVtcG9yYXJ5RGlyZWN0b3J5KHdpdGhQYWNrYWdlcyhhc3luYyAoY29udGV4dCkgPT4ge1xuICAgIGNvbnN0IHNoZWxsID0gU2hlbGxIZWxwZXIuZnJvbUNvbnRleHQoY29udGV4dCk7XG4gICAgYXdhaXQgY29udGV4dC5wYWNrYWdlcy5tYWtlQ2xpQXZhaWxhYmxlKCk7XG5cbiAgICBhd2FpdCBzaGVsbC5zaGVsbChbJ25vZGUnLCAnLS12ZXJzaW9uJ10pO1xuICAgIGF3YWl0IHNoZWxsLnNoZWxsKFsnbnBtJywgJy0tdmVyc2lvbiddKTtcblxuICAgIGF3YWl0IHNoZWxsLnNoZWxsKFsnY2RrJywgJ2luaXQnLCAnLWwnLCAndHlwZXNjcmlwdCcsICdhcHAnLCAnLS1nZW5lcmF0ZS1vbmx5J10pO1xuXG4gICAgLy8gTmVjZXNzYXJ5IGJlY2F1c2UgcmVjZW50IHZlcnNpb25zIG9mIHRzLWplc3QgcmVxdWlyZSBUeXBlU2NyaXB0Pj00LjMgYnV0IHdlXG4gICAgLy8gc3RpbGwgd2FudCB0byB0ZXN0IHdpdGggb2xkZXIgdmVyc2lvbnMgYXMgd2VsbC5cbiAgICBhd2FpdCByZW1vdmVEZXZEZXBlbmRlbmNpZXMoY29udGV4dCk7XG5cbiAgICBhd2FpdCBzaGVsbC5zaGVsbChbJ25wbScsICdpbnN0YWxsJywgJy0tc2F2ZS1kZXYnLCBgdHlwZXNjcmlwdEAke3RzVmVyc2lvbn1gXSk7XG5cbiAgICAvLyBBZnRlciB3ZSd2ZSByZW1vdmVkIGRldkRlcGVuZGVuY2llcyB3ZSBuZWVkIHRvIHJlLWluc3RhbGwgdHMtbm9kZSBiZWNhdXNlIGl0J3MgbmVjZXNzYXJ5IGZvciBgY2RrIHN5bnRoYFxuICAgIGF3YWl0IHNoZWxsLnNoZWxsKFsnbnBtJywgJ2luc3RhbGwnLCAnLS1zYXZlLWRldicsICd0cy1ub2RlQF4xMCddKTtcblxuICAgIGF3YWl0IHNoZWxsLnNoZWxsKFsnbnBtJywgJ2luc3RhbGwnXSk7IC8vIE9sZGVyIHZlcnNpb25zIG9mIG5wbSByZXF1aXJlIHRoaXMgdG8gYmUgYSBzZXBhcmF0ZSBzdGVwIGZyb20gdGhlIG9uZSBhYm92ZVxuICAgIGF3YWl0IHNoZWxsLnNoZWxsKFsnbnB4JywgJ3RzYycsICctLXZlcnNpb24nXSk7XG4gICAgYXdhaXQgc2hlbGwuc2hlbGwoWyducG0nLCAncHJ1bmUnXSk7XG4gICAgYXdhaXQgc2hlbGwuc2hlbGwoWyducG0nLCAnbHMnXSk7IC8vIHRoaXMgd2lsbCBmYWlsIGlmIHdlIGhhdmUgdW5tZXQgcGVlciBkZXBlbmRlbmNpZXNcblxuICAgIC8vIFdlIGp1c3QgcmVtb3ZlZCB0aGUgJ2plc3QnIGRlcGVuZGVuY3kgc28gcmVtb3ZlIHRoZSB0ZXN0cyBhcyB3ZWxsIGJlY2F1c2UgdGhleSB3b24ndCBjb21waWxlXG4gICAgYXdhaXQgc2hlbGwuc2hlbGwoWydybScsICctcmYnLCAndGVzdC8nXSk7XG5cbiAgICBhd2FpdCBzaGVsbC5zaGVsbChbJ25wbScsICdydW4nLCAnYnVpbGQnXSk7XG4gICAgYXdhaXQgc2hlbGwuc2hlbGwoWydjZGsnLCAnc3ludGgnXSk7XG4gIH0pKSk7XG59KTtcblxuYXN5bmMgZnVuY3Rpb24gcmVtb3ZlRGV2RGVwZW5kZW5jaWVzKGNvbnRleHQ6IFRlbXBvcmFyeURpcmVjdG9yeUNvbnRleHQpIHtcbiAgY29uc3QgZmlsZW5hbWUgPSBwYXRoLmpvaW4oY29udGV4dC5pbnRlZ1Rlc3REaXIsICdwYWNrYWdlLmpzb24nKTtcbiAgY29uc3QgcGogPSBKU09OLnBhcnNlKGF3YWl0IGZzLnJlYWRGaWxlKGZpbGVuYW1lLCB7IGVuY29kaW5nOiAndXRmLTgnIH0pKTtcbiAgZGVsZXRlIHBqLmRldkRlcGVuZGVuY2llcztcbiAgYXdhaXQgZnMud3JpdGVGaWxlKGZpbGVuYW1lLCBKU09OLnN0cmluZ2lmeShwaiwgdW5kZWZpbmVkLCAyKSwgeyBlbmNvZGluZzogJ3V0Zi04JyB9KTtcbn1cbiJdfQ==
@@ -44,7 +44,7 @@ TYPESCRIPT_VERSIONS.forEach(tsVersion => {
44
44
  await shell.shell(['npm', 'install', '--save-dev', `typescript@${tsVersion}`]);
45
45
 
46
46
  // After we've removed devDependencies we need to re-install ts-node because it's necessary for `cdk synth`
47
- await shell.shell(['npm', 'install', '--save-dev', `ts-node@^10`]);
47
+ await shell.shell(['npm', 'install', '--save-dev', 'ts-node@^10']);
48
48
 
49
49
  await shell.shell(['npm', 'install']); // Older versions of npm require this to be a separate step from the one above
50
50
  await shell.shell(['npx', 'tsc', '--version']);