@aws-cdk-testing/cli-integ 2.173.4 → 3.0.0

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 (81) hide show
  1. package/.eslintrc.js +9 -0
  2. package/LICENSE +2 -1
  3. package/bin/query-github.js +3 -3
  4. package/bin/query-github.ts +56 -0
  5. package/bin/run-suite.js +3 -3
  6. package/bin/run-suite.ts +140 -0
  7. package/bin/stage-distribution.js +3 -2
  8. package/bin/stage-distribution.ts +267 -0
  9. package/bin/test-root.ts +3 -0
  10. package/lib/aws.js +9 -6
  11. package/lib/aws.ts +263 -0
  12. package/lib/corking.ts +33 -0
  13. package/lib/eventually.js +3 -3
  14. package/lib/eventually.ts +42 -0
  15. package/lib/files.js +3 -2
  16. package/lib/files.ts +80 -0
  17. package/lib/github.js +6 -5
  18. package/lib/github.ts +43 -0
  19. package/lib/index.ts +13 -0
  20. package/lib/integ-test.ts +81 -0
  21. package/lib/lists.ts +9 -0
  22. package/lib/memoize.ts +14 -0
  23. package/lib/npm.ts +41 -0
  24. package/lib/package-sources/release-source.js +3 -2
  25. package/lib/package-sources/release-source.ts +81 -0
  26. package/lib/package-sources/repo-source.ts +111 -0
  27. package/lib/package-sources/repo-tools/npm.js +5 -4
  28. package/lib/package-sources/repo-tools/npm.ts +48 -0
  29. package/lib/package-sources/source.ts +35 -0
  30. package/lib/package-sources/subprocess.ts +15 -0
  31. package/lib/resource-pool.js +2 -2
  32. package/lib/resource-pool.ts +140 -0
  33. package/lib/resources.ts +4 -0
  34. package/lib/shell.js +8 -5
  35. package/lib/shell.ts +168 -0
  36. package/lib/staging/codeartifact.js +11 -8
  37. package/lib/staging/codeartifact.ts +387 -0
  38. package/lib/staging/maven.js +5 -3
  39. package/lib/staging/maven.ts +95 -0
  40. package/lib/staging/npm.ts +62 -0
  41. package/lib/staging/nuget.ts +75 -0
  42. package/lib/staging/parallel-shell.js +2 -2
  43. package/lib/staging/parallel-shell.ts +51 -0
  44. package/lib/staging/pypi.ts +50 -0
  45. package/lib/staging/usage-dir.ts +99 -0
  46. package/lib/with-aws.js +3 -2
  47. package/lib/with-aws.ts +67 -0
  48. package/lib/with-cdk-app.js +23 -14
  49. package/lib/with-cdk-app.ts +742 -0
  50. package/lib/with-cli-lib.ts +134 -0
  51. package/lib/with-packages.ts +15 -0
  52. package/lib/with-sam.js +7 -4
  53. package/lib/with-sam.ts +288 -0
  54. package/lib/with-temporary-directory.ts +35 -0
  55. package/lib/with-timeout.ts +33 -0
  56. package/lib/xpmutex.js +2 -2
  57. package/lib/xpmutex.ts +218 -0
  58. package/package.json +84 -62
  59. package/resources/cloud-assemblies/0.36.0/cdk.out +1 -0
  60. package/resources/cloud-assemblies/1.10.0-lookup-default-vpc/cdk.out +1 -0
  61. package/resources/cloud-assemblies/1.10.0-request-azs/cdk.out +1 -0
  62. package/tests/cli-integ-tests/bootstrapping.integtest.js +22 -13
  63. package/tests/cli-integ-tests/bootstrapping.integtest.ts +493 -0
  64. package/tests/cli-integ-tests/cli-lib.integtest.js +3 -2
  65. package/tests/cli-integ-tests/cli-lib.integtest.ts +90 -0
  66. package/tests/cli-integ-tests/cli.integtest.js +76 -49
  67. package/tests/cli-integ-tests/cli.integtest.ts +2874 -0
  68. package/tests/cli-integ-tests/garbage-collection.integtest.js +2 -2
  69. package/tests/cli-integ-tests/garbage-collection.integtest.ts +392 -0
  70. package/tests/init-csharp/init-csharp.integtest.ts +15 -0
  71. package/tests/init-fsharp/init-fsharp.integtest.ts +15 -0
  72. package/tests/init-go/init-go.integtest.ts +23 -0
  73. package/tests/init-java/init-java.integtest.ts +14 -0
  74. package/tests/init-javascript/init-javascript.integtest.ts +59 -0
  75. package/tests/init-python/init-python.integtest.ts +20 -0
  76. package/tests/init-typescript-app/init-typescript-app.integtest.ts +66 -0
  77. package/tests/init-typescript-lib/init-typescript-lib.integtest.ts +13 -0
  78. package/tests/tool-integrations/amplify.integtest.ts +43 -0
  79. package/tests/tool-integrations/with-tool-context.ts +14 -0
  80. package/tests/uberpackage/uberpackage.integtest.ts +11 -0
  81. package/resources/cdk-apps/cfn-include-app/.gitignore +0 -1
@@ -0,0 +1,493 @@
1
+ /* eslint-disable @cdklabs/no-literal-partition */
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import { DescribeStackResourcesCommand, DescribeStacksCommand } from '@aws-sdk/client-cloudformation';
5
+ import { DescribeRepositoriesCommand } from '@aws-sdk/client-ecr';
6
+ import { CreatePolicyCommand, DeletePolicyCommand, GetRoleCommand } from '@aws-sdk/client-iam';
7
+ import * as yaml from 'yaml';
8
+ import { integTest, randomString, withoutBootstrap } from '../../lib';
9
+ import eventually from '../../lib/eventually';
10
+
11
+ jest.setTimeout(2 * 60 * 60_000); // Includes the time to acquire locks, worst-case single-threaded runtime
12
+
13
+ integTest('can bootstrap without execution', withoutBootstrap(async (fixture) => {
14
+ const bootstrapStackName = fixture.bootstrapStackName;
15
+
16
+ await fixture.cdkBootstrapLegacy({
17
+ toolkitStackName: bootstrapStackName,
18
+ noExecute: true,
19
+ });
20
+
21
+ const resp = await fixture.aws.cloudFormation.send(
22
+ new DescribeStacksCommand({
23
+ StackName: bootstrapStackName,
24
+ }),
25
+ );
26
+
27
+ expect(resp.Stacks?.[0].StackStatus).toEqual('REVIEW_IN_PROGRESS');
28
+ }));
29
+
30
+ integTest('upgrade legacy bootstrap stack to new bootstrap stack while in use', withoutBootstrap(async (fixture) => {
31
+ const bootstrapStackName = fixture.bootstrapStackName;
32
+
33
+ const legacyBootstrapBucketName = `aws-cdk-bootstrap-integ-test-legacy-bckt-${randomString()}`;
34
+ const newBootstrapBucketName = `aws-cdk-bootstrap-integ-test-v2-bckt-${randomString()}`;
35
+ fixture.rememberToDeleteBucket(legacyBootstrapBucketName); // This one will leak
36
+ fixture.rememberToDeleteBucket(newBootstrapBucketName); // This one shouldn't leak if the test succeeds, but let's be safe in case it doesn't
37
+
38
+ // Legacy bootstrap
39
+ await fixture.cdkBootstrapLegacy({
40
+ toolkitStackName: bootstrapStackName,
41
+ bootstrapBucketName: legacyBootstrapBucketName,
42
+ });
43
+
44
+ // Deploy stack that uses file assets
45
+ await fixture.cdkDeploy('lambda', {
46
+ options: [
47
+ '--context', `bootstrapBucket=${legacyBootstrapBucketName}`,
48
+ '--context', 'legacySynth=true',
49
+ '--context', `@aws-cdk/core:bootstrapQualifier=${fixture.qualifier}`,
50
+ '--toolkit-stack-name', bootstrapStackName,
51
+ ],
52
+ });
53
+
54
+ // Upgrade bootstrap stack to "new" style
55
+ await fixture.cdkBootstrapModern({
56
+ toolkitStackName: bootstrapStackName,
57
+ bootstrapBucketName: newBootstrapBucketName,
58
+ cfnExecutionPolicy: 'arn:aws:iam::aws:policy/AdministratorAccess',
59
+ });
60
+
61
+ // (Force) deploy stack again
62
+ // --force to bypass the check which says that the template hasn't changed.
63
+ await fixture.cdkDeploy('lambda', {
64
+ options: [
65
+ '--context', `bootstrapBucket=${newBootstrapBucketName}`,
66
+ '--context', `@aws-cdk/core:bootstrapQualifier=${fixture.qualifier}`,
67
+ '--toolkit-stack-name', bootstrapStackName,
68
+ '--force',
69
+ ],
70
+ });
71
+ }));
72
+
73
+ integTest('can and deploy if omitting execution policies', withoutBootstrap(async (fixture) => {
74
+ const bootstrapStackName = fixture.bootstrapStackName;
75
+
76
+ await fixture.cdkBootstrapModern({
77
+ toolkitStackName: bootstrapStackName,
78
+ });
79
+
80
+ // Deploy stack that uses file assets
81
+ await fixture.cdkDeploy('lambda', {
82
+ options: [
83
+ '--toolkit-stack-name', bootstrapStackName,
84
+ '--context', `@aws-cdk/core:bootstrapQualifier=${fixture.qualifier}`,
85
+ '--context', '@aws-cdk/core:newStyleStackSynthesis=1',
86
+ ],
87
+ });
88
+ }));
89
+
90
+ integTest('can deploy with session tags on the deploy, lookup, file asset, and image asset publishing roles', withoutBootstrap(async (fixture) => {
91
+ const bootstrapStackName = fixture.bootstrapStackName;
92
+
93
+ await fixture.cdkBootstrapModern({
94
+ toolkitStackName: bootstrapStackName,
95
+ bootstrapTemplate: path.join(__dirname, '..', '..', 'resources', 'bootstrap-templates', 'session-tags.all-roles-deny-all.yaml'),
96
+ });
97
+
98
+ await fixture.cdkDeploy('session-tags', {
99
+ options: [
100
+ '--toolkit-stack-name', bootstrapStackName,
101
+ '--context', `@aws-cdk/core:bootstrapQualifier=${fixture.qualifier}`,
102
+ '--context', '@aws-cdk/core:newStyleStackSynthesis=1',
103
+ ],
104
+ modEnv: {
105
+ ENABLE_VPC_TESTING: 'IMPORT',
106
+ },
107
+ });
108
+ }));
109
+
110
+ integTest('can deploy without execution role and with session tags on deploy role', withoutBootstrap(async (fixture) => {
111
+ const bootstrapStackName = fixture.bootstrapStackName;
112
+
113
+ await fixture.cdkBootstrapModern({
114
+ toolkitStackName: bootstrapStackName,
115
+ bootstrapTemplate: path.join(__dirname, '..', '..', 'resources', 'bootstrap-templates', 'session-tags.deploy-role-deny-sqs.yaml'),
116
+ });
117
+
118
+ await fixture.cdkDeploy('session-tags-with-custom-synthesizer', {
119
+ options: [
120
+ '--toolkit-stack-name', bootstrapStackName,
121
+ '--context', `@aws-cdk/core:bootstrapQualifier=${fixture.qualifier}`,
122
+ '--context', '@aws-cdk/core:newStyleStackSynthesis=1',
123
+ ],
124
+ });
125
+ }));
126
+
127
+ integTest('deploy new style synthesis to new style bootstrap', withoutBootstrap(async (fixture) => {
128
+ const bootstrapStackName = fixture.bootstrapStackName;
129
+
130
+ await fixture.cdkBootstrapModern({
131
+ toolkitStackName: bootstrapStackName,
132
+ cfnExecutionPolicy: 'arn:aws:iam::aws:policy/AdministratorAccess',
133
+ });
134
+
135
+ // Deploy stack that uses file assets
136
+ await fixture.cdkDeploy('lambda', {
137
+ options: [
138
+ '--toolkit-stack-name', bootstrapStackName,
139
+ '--context', `@aws-cdk/core:bootstrapQualifier=${fixture.qualifier}`,
140
+ '--context', '@aws-cdk/core:newStyleStackSynthesis=1',
141
+ ],
142
+ });
143
+ }));
144
+
145
+ integTest('deploy new style synthesis to new style bootstrap (with docker image)', withoutBootstrap(async (fixture) => {
146
+ const bootstrapStackName = fixture.bootstrapStackName;
147
+
148
+ await fixture.cdkBootstrapModern({
149
+ toolkitStackName: bootstrapStackName,
150
+ cfnExecutionPolicy: 'arn:aws:iam::aws:policy/AdministratorAccess',
151
+ });
152
+
153
+ // Deploy stack that uses file assets
154
+ await fixture.cdkDeploy('docker', {
155
+ options: [
156
+ '--toolkit-stack-name', bootstrapStackName,
157
+ '--context', `@aws-cdk/core:bootstrapQualifier=${fixture.qualifier}`,
158
+ '--context', '@aws-cdk/core:newStyleStackSynthesis=1',
159
+ ],
160
+ });
161
+ }));
162
+
163
+ integTest('deploy old style synthesis to new style bootstrap', withoutBootstrap(async (fixture) => {
164
+ const bootstrapStackName = fixture.bootstrapStackName;
165
+
166
+ await fixture.cdkBootstrapModern({
167
+ toolkitStackName: bootstrapStackName,
168
+ cfnExecutionPolicy: 'arn:aws:iam::aws:policy/AdministratorAccess',
169
+ });
170
+
171
+ // Deploy stack that uses file assets
172
+ await fixture.cdkDeploy('lambda', {
173
+ options: [
174
+ '--context', `@aws-cdk/core:bootstrapQualifier=${fixture.qualifier}`,
175
+ '--toolkit-stack-name', bootstrapStackName,
176
+ ],
177
+ });
178
+ }));
179
+
180
+ integTest('can create a legacy bootstrap stack with --public-access-block-configuration=false', withoutBootstrap(async (fixture) => {
181
+ const bootstrapStackName = fixture.bootstrapStackName;
182
+
183
+ await fixture.cdkBootstrapLegacy({
184
+ verbose: true,
185
+ toolkitStackName: bootstrapStackName,
186
+ publicAccessBlockConfiguration: false,
187
+ tags: 'Foo=Bar',
188
+ });
189
+
190
+ const response = await fixture.aws.cloudFormation.send(new DescribeStacksCommand({ StackName: bootstrapStackName }));
191
+ expect(response.Stacks?.[0].Tags).toEqual([
192
+ { Key: 'Foo', Value: 'Bar' },
193
+ ]);
194
+ }));
195
+
196
+ integTest('can create multiple legacy bootstrap stacks', withoutBootstrap(async (fixture) => {
197
+ const bootstrapStackName1 = `${fixture.bootstrapStackName}-1`;
198
+ const bootstrapStackName2 = `${fixture.bootstrapStackName}-2`;
199
+
200
+ // deploy two toolkit stacks into the same environment (see #1416)
201
+ // one with tags
202
+ await fixture.cdkBootstrapLegacy({
203
+ verbose: true,
204
+ toolkitStackName: bootstrapStackName1,
205
+ tags: 'Foo=Bar',
206
+ });
207
+ await fixture.cdkBootstrapLegacy({
208
+ verbose: true,
209
+ toolkitStackName: bootstrapStackName2,
210
+ });
211
+
212
+ const response = await fixture.aws.cloudFormation.send(new DescribeStacksCommand({ StackName: bootstrapStackName1 }));
213
+ expect(response.Stacks?.[0].Tags).toEqual([
214
+ { Key: 'Foo', Value: 'Bar' },
215
+ ]);
216
+ }));
217
+
218
+ integTest('can dump the template, modify and use it to deploy a custom bootstrap stack', withoutBootstrap(async (fixture) => {
219
+ let template = await fixture.cdkBootstrapModern({
220
+ // toolkitStackName doesn't matter for this particular invocation
221
+ toolkitStackName: fixture.bootstrapStackName,
222
+ showTemplate: true,
223
+ cliOptions: {
224
+ captureStderr: false,
225
+ },
226
+ });
227
+
228
+ expect(template).toContain('BootstrapVersion:');
229
+
230
+ template += '\n' + [
231
+ ' TwiddleDee:',
232
+ ' Value: Template got twiddled',
233
+ ].join('\n');
234
+
235
+ const filename = path.join(fixture.integTestDir, `${fixture.qualifier}-template.yaml`);
236
+ fs.writeFileSync(filename, template, { encoding: 'utf-8' });
237
+ await fixture.cdkBootstrapModern({
238
+ toolkitStackName: fixture.bootstrapStackName,
239
+ template: filename,
240
+ cfnExecutionPolicy: 'arn:aws:iam::aws:policy/AdministratorAccess',
241
+ });
242
+ }));
243
+
244
+ integTest('a customized template vendor will not overwrite the default template', withoutBootstrap(async (fixture) => {
245
+ // Initial bootstrap
246
+ const toolkitStackName = fixture.bootstrapStackName;
247
+ await fixture.cdkBootstrapModern({
248
+ toolkitStackName,
249
+ cfnExecutionPolicy: 'arn:aws:iam::aws:policy/AdministratorAccess',
250
+ });
251
+
252
+ // Customize template
253
+ const templateStr = await fixture.cdkBootstrapModern({
254
+ // toolkitStackName doesn't matter for this particular invocation
255
+ toolkitStackName,
256
+ showTemplate: true,
257
+ cliOptions: {
258
+ captureStderr: false,
259
+ },
260
+ });
261
+
262
+ const template = yaml.parse(templateStr, { schema: 'core' });
263
+ template.Parameters.BootstrapVariant.Default = 'CustomizedVendor';
264
+ const filename = path.join(fixture.integTestDir, `${fixture.qualifier}-template.yaml`);
265
+ fs.writeFileSync(filename, yaml.stringify(template, { schema: 'yaml-1.1' }), { encoding: 'utf-8' });
266
+
267
+ // Rebootstrap. For some reason, this doesn't cause a failure, it's a successful no-op.
268
+ const output = await fixture.cdkBootstrapModern({
269
+ toolkitStackName,
270
+ template: filename,
271
+ cfnExecutionPolicy: 'arn:aws:iam::aws:policy/AdministratorAccess',
272
+ cliOptions: {
273
+ captureStderr: true,
274
+ },
275
+ });
276
+ expect(output).toContain('Not overwriting it with a template containing');
277
+ }));
278
+
279
+ integTest('can use the default permissions boundary to bootstrap', withoutBootstrap(async (fixture) => {
280
+ let template = await fixture.cdkBootstrapModern({
281
+ // toolkitStackName doesn't matter for this particular invocation
282
+ toolkitStackName: fixture.bootstrapStackName,
283
+ showTemplate: true,
284
+ examplePermissionsBoundary: true,
285
+ });
286
+
287
+ expect(template).toContain('PermissionsBoundary');
288
+ }));
289
+
290
+ integTest('can use the custom permissions boundary to bootstrap', withoutBootstrap(async (fixture) => {
291
+ let template = await fixture.cdkBootstrapModern({
292
+ // toolkitStackName doesn't matter for this particular invocation
293
+ toolkitStackName: fixture.bootstrapStackName,
294
+ showTemplate: true,
295
+ customPermissionsBoundary: 'permission-boundary-name',
296
+ });
297
+
298
+ expect(template).toContain('permission-boundary-name');
299
+ }));
300
+
301
+ integTest('can use the custom permissions boundary (with slashes) to bootstrap', withoutBootstrap(async (fixture) => {
302
+ let template = await fixture.cdkBootstrapModern({
303
+ // toolkitStackName doesn't matter for this particular invocation
304
+ toolkitStackName: fixture.bootstrapStackName,
305
+ showTemplate: true,
306
+ customPermissionsBoundary: 'permission-boundary-name/with/path',
307
+ });
308
+
309
+ expect(template).toContain('permission-boundary-name/with/path');
310
+ }));
311
+
312
+ integTest('can remove customPermissionsBoundary', withoutBootstrap(async (fixture) => {
313
+ const bootstrapStackName = fixture.bootstrapStackName;
314
+ const policyName = `${bootstrapStackName}-pb`;
315
+ let policyArn;
316
+ try {
317
+ const policy = await fixture.aws.iam.send(
318
+ new CreatePolicyCommand({
319
+ PolicyName: policyName,
320
+ PolicyDocument: JSON.stringify({
321
+ Version: '2012-10-17',
322
+ Statement: {
323
+ Action: ['*'],
324
+ Resource: ['*'],
325
+ Effect: 'Allow',
326
+ },
327
+ }),
328
+ }),
329
+ );
330
+ policyArn = policy.Policy?.Arn;
331
+
332
+ // Policy creation and consistency across regions is "almost immediate"
333
+ // See: https://docs.aws.amazon.com/IAM/latest/UserGuide/troubleshoot_general.html#troubleshoot_general_eventual-consistency
334
+ // We will put this in an `eventually` block to retry stack creation with a reasonable timeout
335
+ const createStackWithPermissionBoundary = async (): Promise<void> => {
336
+ await fixture.cdkBootstrapModern({
337
+ // toolkitStackName doesn't matter for this particular invocation
338
+ toolkitStackName: bootstrapStackName,
339
+ customPermissionsBoundary: policyName,
340
+ });
341
+
342
+ const response = await fixture.aws.cloudFormation.send(
343
+ new DescribeStacksCommand({ StackName: bootstrapStackName }),
344
+ );
345
+ expect(
346
+ response.Stacks?.[0].Parameters?.some(
347
+ param => (param.ParameterKey === 'InputPermissionsBoundary' && param.ParameterValue === policyName),
348
+ )).toEqual(true);
349
+ };
350
+
351
+ await eventually(createStackWithPermissionBoundary, { maxAttempts: 3 });
352
+
353
+ await fixture.cdkBootstrapModern({
354
+ // toolkitStackName doesn't matter for this particular invocation
355
+ toolkitStackName: bootstrapStackName,
356
+ usePreviousParameters: false,
357
+ });
358
+ const response2 = await fixture.aws.cloudFormation.send(
359
+ new DescribeStacksCommand({ StackName: bootstrapStackName }),
360
+ );
361
+ expect(
362
+ response2.Stacks?.[0].Parameters?.some(
363
+ param => (param.ParameterKey === 'InputPermissionsBoundary' && !param.ParameterValue),
364
+ )).toEqual(true);
365
+
366
+ const region = fixture.aws.region;
367
+ const account = await fixture.aws.account();
368
+ const role = await fixture.aws.iam.send(
369
+ new GetRoleCommand({ RoleName: `cdk-${fixture.qualifier}-cfn-exec-role-${account}-${region}` }),
370
+ );
371
+ if (!role.Role) {
372
+ throw new Error('Role not found');
373
+ }
374
+ expect(role.Role.PermissionsBoundary).toBeUndefined();
375
+
376
+ } finally {
377
+ if (policyArn) {
378
+ await fixture.aws.iam.send(new DeletePolicyCommand({ PolicyArn: policyArn }));
379
+ }
380
+ }
381
+ }));
382
+
383
+ integTest('switch on termination protection, switch is left alone on re-bootstrap', withoutBootstrap(async (fixture) => {
384
+ const bootstrapStackName = fixture.bootstrapStackName;
385
+
386
+ await fixture.cdkBootstrapModern({
387
+ verbose: true,
388
+ toolkitStackName: bootstrapStackName,
389
+ terminationProtection: true,
390
+ cfnExecutionPolicy: 'arn:aws:iam::aws:policy/AdministratorAccess',
391
+ });
392
+ await fixture.cdkBootstrapModern({
393
+ verbose: true,
394
+ toolkitStackName: bootstrapStackName,
395
+ force: true,
396
+ });
397
+
398
+ const response = await fixture.aws.cloudFormation.send(new DescribeStacksCommand({ StackName: bootstrapStackName }));
399
+ expect(response.Stacks?.[0].EnableTerminationProtection).toEqual(true);
400
+ }));
401
+
402
+ integTest('add tags, left alone on re-bootstrap', withoutBootstrap(async (fixture) => {
403
+ const bootstrapStackName = fixture.bootstrapStackName;
404
+
405
+ await fixture.cdkBootstrapModern({
406
+ verbose: true,
407
+ toolkitStackName: bootstrapStackName,
408
+ tags: 'Foo=Bar',
409
+ cfnExecutionPolicy: 'arn:aws:iam::aws:policy/AdministratorAccess',
410
+ });
411
+ await fixture.cdkBootstrapModern({
412
+ verbose: true,
413
+ toolkitStackName: bootstrapStackName,
414
+ force: true,
415
+ });
416
+
417
+ const response = await fixture.aws.cloudFormation.send(new DescribeStacksCommand({ StackName: bootstrapStackName }));
418
+ expect(response.Stacks?.[0].Tags).toEqual([
419
+ { Key: 'Foo', Value: 'Bar' },
420
+ ]);
421
+ }));
422
+
423
+ integTest('can add tags then update tags during re-bootstrap', withoutBootstrap(async (fixture) => {
424
+ const bootstrapStackName = fixture.bootstrapStackName;
425
+
426
+ await fixture.cdkBootstrapModern({
427
+ verbose: true,
428
+ toolkitStackName: bootstrapStackName,
429
+ tags: 'Foo=Bar',
430
+ cfnExecutionPolicy: 'arn:aws:iam::aws:policy/AdministratorAccess',
431
+ });
432
+ await fixture.cdkBootstrapModern({
433
+ verbose: true,
434
+ toolkitStackName: bootstrapStackName,
435
+ tags: 'Foo=BarBaz',
436
+ cfnExecutionPolicy: 'arn:aws:iam::aws:policy/AdministratorAccess',
437
+ force: true,
438
+ });
439
+
440
+ const response = await fixture.aws.cloudFormation.send(new DescribeStacksCommand({ StackName: bootstrapStackName }));
441
+ expect(response.Stacks?.[0].Tags).toEqual([
442
+ { Key: 'Foo', Value: 'BarBaz' },
443
+ ]);
444
+ }));
445
+
446
+ integTest('can deploy modern-synthesized stack even if bootstrap stack name is unknown', withoutBootstrap(async (fixture) => {
447
+ const bootstrapStackName = fixture.bootstrapStackName;
448
+
449
+ await fixture.cdkBootstrapModern({
450
+ toolkitStackName: bootstrapStackName,
451
+ cfnExecutionPolicy: 'arn:aws:iam::aws:policy/AdministratorAccess',
452
+ });
453
+
454
+ // Deploy stack that uses file assets
455
+ await fixture.cdkDeploy('lambda', {
456
+ options: [
457
+ // Explicity pass a name that's sure to not exist, otherwise the CLI might accidentally find a
458
+ // default bootstracp stack if that happens to be in the account already.
459
+ '--toolkit-stack-name', 'DefinitelyDoesNotExist',
460
+ '--context', `@aws-cdk/core:bootstrapQualifier=${fixture.qualifier}`,
461
+ '--context', '@aws-cdk/core:newStyleStackSynthesis=1',
462
+ ],
463
+ });
464
+ }));
465
+
466
+ integTest('create ECR with tag IMMUTABILITY to set on', withoutBootstrap(async (fixture) => {
467
+ const bootstrapStackName = fixture.bootstrapStackName;
468
+
469
+ await fixture.cdkBootstrapModern({
470
+ verbose: true,
471
+ toolkitStackName: bootstrapStackName,
472
+ });
473
+
474
+ const response = await fixture.aws.cloudFormation.send(
475
+ new DescribeStackResourcesCommand({
476
+ StackName: bootstrapStackName,
477
+ }),
478
+ );
479
+ const ecrResource = response.StackResources?.find(resource => resource.LogicalResourceId === 'ContainerAssetsRepository');
480
+ expect(ecrResource).toBeDefined();
481
+
482
+ const ecrResponse = await fixture.aws.ecr.send(
483
+ new DescribeRepositoriesCommand({
484
+ repositoryNames: [
485
+ // This is set, as otherwise we don't end up here
486
+ ecrResource?.PhysicalResourceId ?? '',
487
+ ],
488
+ }),
489
+ );
490
+
491
+ expect(ecrResponse.repositories?.[0].imageTagMutability).toEqual('IMMUTABLE');
492
+ }));
493
+
@@ -25,6 +25,7 @@ jest.setTimeout(2 * 60 * 60000); // Includes the time to acquire locks, worst-ca
25
25
  expect(listing).toContain(fixture.fullStackName('simple-1'));
26
26
  }));
27
27
  (0, lib_1.integTest)('cli-lib deploy', (0, lib_1.withCliLibFixture)(async (fixture) => {
28
+ var _a;
28
29
  const stackName = fixture.fullStackName('simple-1');
29
30
  try {
30
31
  // deploy the stack
@@ -35,7 +36,7 @@ jest.setTimeout(2 * 60 * 60000); // Includes the time to acquire locks, worst-ca
35
36
  const expectedStack = await fixture.aws.cloudFormation.send(new client_cloudformation_1.DescribeStackResourcesCommand({
36
37
  StackName: stackName,
37
38
  }));
38
- expect(expectedStack.StackResources?.length).toEqual(3);
39
+ expect((_a = expectedStack.StackResources) === null || _a === void 0 ? void 0 : _a.length).toEqual(3);
39
40
  }
40
41
  finally {
41
42
  // delete the stack
@@ -58,4 +59,4 @@ jest.setTimeout(2 * 60 * 60000); // Includes the time to acquire locks, worst-ca
58
59
  StackName: fixture.fullStackName('simple-1'),
59
60
  }))).rejects.toThrow('does not exist');
60
61
  }));
61
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLWxpYi5pbnRlZ3Rlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjbGktbGliLmludGVndGVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLDBFQUFzRztBQUN0RyxtQ0FBeUQ7QUFFekQsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDLEdBQUcsRUFBRSxHQUFHLEtBQU0sQ0FBQyxDQUFDLENBQUMseUVBQXlFO0FBRTNHLElBQUEsZUFBUyxFQUNQLGVBQWUsRUFDZixJQUFBLHVCQUFpQixFQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsRUFBRTtJQUNsQyxNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDaEUsTUFBTSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQzFDLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQztRQUN0QixtRUFBbUU7UUFDbkUsU0FBUyxFQUFFLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQztZQUNqQyxhQUFhLEVBQUUsTUFBTSxDQUFDLGdCQUFnQixDQUFDO2dCQUNyQyxJQUFJLEVBQUUsaUJBQWlCO2dCQUN2QixVQUFVLEVBQUU7b0JBQ1YsaUJBQWlCLEVBQUUsR0FBRztpQkFDdkI7Z0JBQ0QsUUFBUSxFQUFFO29CQUNSLGNBQWMsRUFBRSxHQUFHLE9BQU8sQ0FBQyxlQUFlLDBCQUEwQjtpQkFDckU7YUFDRixDQUFDO1NBQ0gsQ0FBQztLQUNILENBQUMsQ0FDSCxDQUFDO0FBQ0osQ0FBQyxDQUFDLENBQ0gsQ0FBQztBQUVGLElBQUEsZUFBUyxFQUNQLGNBQWMsRUFDZCxJQUFBLHVCQUFpQixFQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsRUFBRTtJQUNsQyxNQUFNLE9BQU8sR0FBRyxNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxNQUFNLENBQUMsRUFBRSxFQUFFLGFBQWEsRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDO0lBQ3RFLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDO0FBQy9ELENBQUMsQ0FBQyxDQUNILENBQUM7QUFFRixJQUFBLGVBQVMsRUFDUCxnQkFBZ0IsRUFDaEIsSUFBQSx1QkFBaUIsRUFBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLEVBQUU7SUFDbEMsTUFBTSxTQUFTLEdBQUcsT0FBTyxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUVwRCxJQUFJLENBQUM7UUFDSCxtQkFBbUI7UUFDbkIsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsUUFBUSxFQUFFLFNBQVMsQ0FBQyxFQUFFO1lBQ3ZDLG9CQUFvQixFQUFFLElBQUk7U0FDM0IsQ0FBQyxDQUFDO1FBRUgsOENBQThDO1FBQzlDLE1BQU0sYUFBYSxHQUFHLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUN6RCxJQUFJLHFEQUE2QixDQUFDO1lBQ2hDLFNBQVMsRUFBRSxTQUFTO1NBQ3JCLENBQUMsQ0FDSCxDQUFDO1FBQ0YsTUFBTSxDQUFDLGFBQWEsQ0FBQyxjQUFjLEVBQUUsTUFBTSxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzFELENBQUM7WUFBUyxDQUFDO1FBQ1QsbUJBQW1CO1FBQ25CLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLFNBQVMsRUFBRSxTQUFTLENBQUMsRUFBRTtZQUN4QyxhQUFhLEVBQUUsS0FBSztTQUNyQixDQUFDLENBQUM7SUFDTCxDQUFDO0FBQ0gsQ0FBQyxDQUFDLENBQ0gsQ0FBQztBQUVGLElBQUEsZUFBUyxFQUNQLHVGQUF1RixFQUN2RixJQUFBLHVCQUFpQixFQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsRUFBRTtJQUNsQyxNQUFNLE1BQU0sR0FBRyxNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxRQUFRLEVBQUUsT0FBTyxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsQ0FBQyxFQUFFO1FBQzlFLFVBQVUsRUFBRSxJQUFJO1FBQ2hCLGFBQWEsRUFBRSxJQUFJO1FBQ25CLFlBQVksRUFBRSxJQUFJO1FBQ2xCLG9CQUFvQixFQUFFLEtBQUs7S0FDNUIsQ0FBQyxDQUFDO0lBRUgsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLFNBQVMsQ0FDdEIsMkdBQTJHLENBQzVHLENBQUM7SUFDRixNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsU0FBUyxDQUN0QiwrRUFBK0UsQ0FDaEYsQ0FBQztJQUVGLGdDQUFnQztJQUNoQyxNQUFNLE1BQU0sQ0FDVixPQUFPLENBQUMsR0FBRyxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQzdCLElBQUksNkNBQXFCLENBQUM7UUFDeEIsU0FBUyxFQUFFLE9BQU8sQ0FBQyxhQUFhLENBQUMsVUFBVSxDQUFDO0tBQzdDLENBQUMsQ0FDSCxDQUNGLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0FBQ3RDLENBQUMsQ0FBQyxDQUNILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBEZXNjcmliZVN0YWNrUmVzb3VyY2VzQ29tbWFuZCwgRGVzY3JpYmVTdGFja3NDb21tYW5kIH0gZnJvbSAnQGF3cy1zZGsvY2xpZW50LWNsb3VkZm9ybWF0aW9uJztcbmltcG9ydCB7IGludGVnVGVzdCwgd2l0aENsaUxpYkZpeHR1cmUgfSBmcm9tICcuLi8uLi9saWInO1xuXG5qZXN0LnNldFRpbWVvdXQoMiAqIDYwICogNjBfMDAwKTsgLy8gSW5jbHVkZXMgdGhlIHRpbWUgdG8gYWNxdWlyZSBsb2Nrcywgd29yc3QtY2FzZSBzaW5nbGUtdGhyZWFkZWQgcnVudGltZVxuXG5pbnRlZ1Rlc3QoXG4gICdjbGktbGliIHN5bnRoJyxcbiAgd2l0aENsaUxpYkZpeHR1cmUoYXN5bmMgKGZpeHR1cmUpID0+IHtcbiAgICBhd2FpdCBmaXh0dXJlLmNkayhbJ3N5bnRoJywgZml4dHVyZS5mdWxsU3RhY2tOYW1lKCdzaW1wbGUtMScpXSk7XG4gICAgZXhwZWN0KGZpeHR1cmUudGVtcGxhdGUoJ3NpbXBsZS0xJykpLnRvRXF1YWwoXG4gICAgICBleHBlY3Qub2JqZWN0Q29udGFpbmluZyh7XG4gICAgICAgIC8vIENoZWNraW5nIGZvciBhIHNtYWxsIHN1YnNldCBpcyBlbm91Z2ggYXMgcHJvb2YgdGhhdCBzeW50aCB3b3JrZWRcbiAgICAgICAgUmVzb3VyY2VzOiBleHBlY3Qub2JqZWN0Q29udGFpbmluZyh7XG4gICAgICAgICAgcXVldWUyNzZGNzI5NzogZXhwZWN0Lm9iamVjdENvbnRhaW5pbmcoe1xuICAgICAgICAgICAgVHlwZTogJ0FXUzo6U1FTOjpRdWV1ZScsXG4gICAgICAgICAgICBQcm9wZXJ0aWVzOiB7XG4gICAgICAgICAgICAgIFZpc2liaWxpdHlUaW1lb3V0OiAzMDAsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgTWV0YWRhdGE6IHtcbiAgICAgICAgICAgICAgJ2F3czpjZGs6cGF0aCc6IGAke2ZpeHR1cmUuc3RhY2tOYW1lUHJlZml4fS1zaW1wbGUtMS9xdWV1ZS9SZXNvdXJjZWAsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIH0pLFxuICAgICAgICB9KSxcbiAgICAgIH0pLFxuICAgICk7XG4gIH0pLFxuKTtcblxuaW50ZWdUZXN0KFxuICAnY2xpLWxpYiBsaXN0JyxcbiAgd2l0aENsaUxpYkZpeHR1cmUoYXN5bmMgKGZpeHR1cmUpID0+IHtcbiAgICBjb25zdCBsaXN0aW5nID0gYXdhaXQgZml4dHVyZS5jZGsoWydsaXN0J10sIHsgY2FwdHVyZVN0ZGVycjogZmFsc2UgfSk7XG4gICAgZXhwZWN0KGxpc3RpbmcpLnRvQ29udGFpbihmaXh0dXJlLmZ1bGxTdGFja05hbWUoJ3NpbXBsZS0xJykpO1xuICB9KSxcbik7XG5cbmludGVnVGVzdChcbiAgJ2NsaS1saWIgZGVwbG95JyxcbiAgd2l0aENsaUxpYkZpeHR1cmUoYXN5bmMgKGZpeHR1cmUpID0+IHtcbiAgICBjb25zdCBzdGFja05hbWUgPSBmaXh0dXJlLmZ1bGxTdGFja05hbWUoJ3NpbXBsZS0xJyk7XG5cbiAgICB0cnkge1xuICAgICAgLy8gZGVwbG95IHRoZSBzdGFja1xuICAgICAgYXdhaXQgZml4dHVyZS5jZGsoWydkZXBsb3knLCBzdGFja05hbWVdLCB7XG4gICAgICAgIG5ldmVyUmVxdWlyZUFwcHJvdmFsOiB0cnVlLFxuICAgICAgfSk7XG5cbiAgICAgIC8vIHZlcmlmeSB0aGUgbnVtYmVyIG9mIHJlc291cmNlcyBpbiB0aGUgc3RhY2tcbiAgICAgIGNvbnN0IGV4cGVjdGVkU3RhY2sgPSBhd2FpdCBmaXh0dXJlLmF3cy5jbG91ZEZvcm1hdGlvbi5zZW5kKFxuICAgICAgICBuZXcgRGVzY3JpYmVTdGFja1Jlc291cmNlc0NvbW1hbmQoe1xuICAgICAgICAgIFN0YWNrTmFtZTogc3RhY2tOYW1lLFxuICAgICAgICB9KSxcbiAgICAgICk7XG4gICAgICBleHBlY3QoZXhwZWN0ZWRTdGFjay5TdGFja1Jlc291cmNlcz8ubGVuZ3RoKS50b0VxdWFsKDMpO1xuICAgIH0gZmluYWxseSB7XG4gICAgICAvLyBkZWxldGUgdGhlIHN0YWNrXG4gICAgICBhd2FpdCBmaXh0dXJlLmNkayhbJ2Rlc3Ryb3knLCBzdGFja05hbWVdLCB7XG4gICAgICAgIGNhcHR1cmVTdGRlcnI6IGZhbHNlLFxuICAgICAgfSk7XG4gICAgfVxuICB9KSxcbik7XG5cbmludGVnVGVzdChcbiAgJ3NlY3VyaXR5IHJlbGF0ZWQgY2hhbmdlcyB3aXRob3V0IGEgQ0xJIGFyZSBleHBlY3RlZCB0byBmYWlsIHdoZW4gYXBwcm92YWwgaXMgcmVxdWlyZWQnLFxuICB3aXRoQ2xpTGliRml4dHVyZShhc3luYyAoZml4dHVyZSkgPT4ge1xuICAgIGNvbnN0IHN0ZEVyciA9IGF3YWl0IGZpeHR1cmUuY2RrKFsnZGVwbG95JywgZml4dHVyZS5mdWxsU3RhY2tOYW1lKCdzaW1wbGUtMScpXSwge1xuICAgICAgb25seVN0ZGVycjogdHJ1ZSxcbiAgICAgIGNhcHR1cmVTdGRlcnI6IHRydWUsXG4gICAgICBhbGxvd0VyckV4aXQ6IHRydWUsXG4gICAgICBuZXZlclJlcXVpcmVBcHByb3ZhbDogZmFsc2UsXG4gICAgfSk7XG5cbiAgICBleHBlY3Qoc3RkRXJyKS50b0NvbnRhaW4oXG4gICAgICAnVGhpcyBkZXBsb3ltZW50IHdpbGwgbWFrZSBwb3RlbnRpYWxseSBzZW5zaXRpdmUgY2hhbmdlcyBhY2NvcmRpbmcgdG8geW91ciBjdXJyZW50IHNlY3VyaXR5IGFwcHJvdmFsIGxldmVsJyxcbiAgICApO1xuICAgIGV4cGVjdChzdGRFcnIpLnRvQ29udGFpbihcbiAgICAgICdcIi0tcmVxdWlyZS1hcHByb3ZhbFwiIGlzIGVuYWJsZWQgYW5kIHN0YWNrIGluY2x1ZGVzIHNlY3VyaXR5LXNlbnNpdGl2ZSB1cGRhdGVzJyxcbiAgICApO1xuXG4gICAgLy8gRW5zdXJlIHN0YWNrIHdhcyBub3QgZGVwbG95ZWRcbiAgICBhd2FpdCBleHBlY3QoXG4gICAgICBmaXh0dXJlLmF3cy5jbG91ZEZvcm1hdGlvbi5zZW5kKFxuICAgICAgICBuZXcgRGVzY3JpYmVTdGFja3NDb21tYW5kKHtcbiAgICAgICAgICBTdGFja05hbWU6IGZpeHR1cmUuZnVsbFN0YWNrTmFtZSgnc2ltcGxlLTEnKSxcbiAgICAgICAgfSksXG4gICAgICApLFxuICAgICkucmVqZWN0cy50b1Rocm93KCdkb2VzIG5vdCBleGlzdCcpO1xuICB9KSxcbik7XG4iXX0=
62
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLWxpYi5pbnRlZ3Rlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjbGktbGliLmludGVndGVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLDBFQUFzRztBQUN0RyxtQ0FBeUQ7QUFFekQsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDLEdBQUcsRUFBRSxHQUFHLEtBQU0sQ0FBQyxDQUFDLENBQUMseUVBQXlFO0FBRTNHLElBQUEsZUFBUyxFQUNQLGVBQWUsRUFDZixJQUFBLHVCQUFpQixFQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsRUFBRTtJQUNsQyxNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDaEUsTUFBTSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQzFDLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQztRQUN0QixtRUFBbUU7UUFDbkUsU0FBUyxFQUFFLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQztZQUNqQyxhQUFhLEVBQUUsTUFBTSxDQUFDLGdCQUFnQixDQUFDO2dCQUNyQyxJQUFJLEVBQUUsaUJBQWlCO2dCQUN2QixVQUFVLEVBQUU7b0JBQ1YsaUJBQWlCLEVBQUUsR0FBRztpQkFDdkI7Z0JBQ0QsUUFBUSxFQUFFO29CQUNSLGNBQWMsRUFBRSxHQUFHLE9BQU8sQ0FBQyxlQUFlLDBCQUEwQjtpQkFDckU7YUFDRixDQUFDO1NBQ0gsQ0FBQztLQUNILENBQUMsQ0FDSCxDQUFDO0FBQ0osQ0FBQyxDQUFDLENBQ0gsQ0FBQztBQUVGLElBQUEsZUFBUyxFQUNQLGNBQWMsRUFDZCxJQUFBLHVCQUFpQixFQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsRUFBRTtJQUNsQyxNQUFNLE9BQU8sR0FBRyxNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxNQUFNLENBQUMsRUFBRSxFQUFFLGFBQWEsRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDO0lBQ3RFLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDO0FBQy9ELENBQUMsQ0FBQyxDQUNILENBQUM7QUFFRixJQUFBLGVBQVMsRUFDUCxnQkFBZ0IsRUFDaEIsSUFBQSx1QkFBaUIsRUFBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLEVBQUU7O0lBQ2xDLE1BQU0sU0FBUyxHQUFHLE9BQU8sQ0FBQyxhQUFhLENBQUMsVUFBVSxDQUFDLENBQUM7SUFFcEQsSUFBSSxDQUFDO1FBQ0gsbUJBQW1CO1FBQ25CLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLFFBQVEsRUFBRSxTQUFTLENBQUMsRUFBRTtZQUN2QyxvQkFBb0IsRUFBRSxJQUFJO1NBQzNCLENBQUMsQ0FBQztRQUVILDhDQUE4QztRQUM5QyxNQUFNLGFBQWEsR0FBRyxNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsY0FBYyxDQUFDLElBQUksQ0FDekQsSUFBSSxxREFBNkIsQ0FBQztZQUNoQyxTQUFTLEVBQUUsU0FBUztTQUNyQixDQUFDLENBQ0gsQ0FBQztRQUNGLE1BQU0sQ0FBQyxNQUFBLGFBQWEsQ0FBQyxjQUFjLDBDQUFFLE1BQU0sQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUMxRCxDQUFDO1lBQVMsQ0FBQztRQUNULG1CQUFtQjtRQUNuQixNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxTQUFTLEVBQUUsU0FBUyxDQUFDLEVBQUU7WUFDeEMsYUFBYSxFQUFFLEtBQUs7U0FDckIsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztBQUNILENBQUMsQ0FBQyxDQUNILENBQUM7QUFFRixJQUFBLGVBQVMsRUFDUCx1RkFBdUYsRUFDdkYsSUFBQSx1QkFBaUIsRUFBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLEVBQUU7SUFDbEMsTUFBTSxNQUFNLEdBQUcsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsUUFBUSxFQUFFLE9BQU8sQ0FBQyxhQUFhLENBQUMsVUFBVSxDQUFDLENBQUMsRUFBRTtRQUM5RSxVQUFVLEVBQUUsSUFBSTtRQUNoQixhQUFhLEVBQUUsSUFBSTtRQUNuQixZQUFZLEVBQUUsSUFBSTtRQUNsQixvQkFBb0IsRUFBRSxLQUFLO0tBQzVCLENBQUMsQ0FBQztJQUVILE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxTQUFTLENBQ3RCLDJHQUEyRyxDQUM1RyxDQUFDO0lBQ0YsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLFNBQVMsQ0FDdEIsK0VBQStFLENBQ2hGLENBQUM7SUFFRixnQ0FBZ0M7SUFDaEMsTUFBTSxNQUFNLENBQ1YsT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUM3QixJQUFJLDZDQUFxQixDQUFDO1FBQ3hCLFNBQVMsRUFBRSxPQUFPLENBQUMsYUFBYSxDQUFDLFVBQVUsQ0FBQztLQUM3QyxDQUFDLENBQ0gsQ0FDRixDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztBQUN0QyxDQUFDLENBQUMsQ0FDSCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRGVzY3JpYmVTdGFja1Jlc291cmNlc0NvbW1hbmQsIERlc2NyaWJlU3RhY2tzQ29tbWFuZCB9IGZyb20gJ0Bhd3Mtc2RrL2NsaWVudC1jbG91ZGZvcm1hdGlvbic7XG5pbXBvcnQgeyBpbnRlZ1Rlc3QsIHdpdGhDbGlMaWJGaXh0dXJlIH0gZnJvbSAnLi4vLi4vbGliJztcblxuamVzdC5zZXRUaW1lb3V0KDIgKiA2MCAqIDYwXzAwMCk7IC8vIEluY2x1ZGVzIHRoZSB0aW1lIHRvIGFjcXVpcmUgbG9ja3MsIHdvcnN0LWNhc2Ugc2luZ2xlLXRocmVhZGVkIHJ1bnRpbWVcblxuaW50ZWdUZXN0KFxuICAnY2xpLWxpYiBzeW50aCcsXG4gIHdpdGhDbGlMaWJGaXh0dXJlKGFzeW5jIChmaXh0dXJlKSA9PiB7XG4gICAgYXdhaXQgZml4dHVyZS5jZGsoWydzeW50aCcsIGZpeHR1cmUuZnVsbFN0YWNrTmFtZSgnc2ltcGxlLTEnKV0pO1xuICAgIGV4cGVjdChmaXh0dXJlLnRlbXBsYXRlKCdzaW1wbGUtMScpKS50b0VxdWFsKFxuICAgICAgZXhwZWN0Lm9iamVjdENvbnRhaW5pbmcoe1xuICAgICAgICAvLyBDaGVja2luZyBmb3IgYSBzbWFsbCBzdWJzZXQgaXMgZW5vdWdoIGFzIHByb29mIHRoYXQgc3ludGggd29ya2VkXG4gICAgICAgIFJlc291cmNlczogZXhwZWN0Lm9iamVjdENvbnRhaW5pbmcoe1xuICAgICAgICAgIHF1ZXVlMjc2RjcyOTc6IGV4cGVjdC5vYmplY3RDb250YWluaW5nKHtcbiAgICAgICAgICAgIFR5cGU6ICdBV1M6OlNRUzo6UXVldWUnLFxuICAgICAgICAgICAgUHJvcGVydGllczoge1xuICAgICAgICAgICAgICBWaXNpYmlsaXR5VGltZW91dDogMzAwLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIE1ldGFkYXRhOiB7XG4gICAgICAgICAgICAgICdhd3M6Y2RrOnBhdGgnOiBgJHtmaXh0dXJlLnN0YWNrTmFtZVByZWZpeH0tc2ltcGxlLTEvcXVldWUvUmVzb3VyY2VgLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9KSxcbiAgICAgICAgfSksXG4gICAgICB9KSxcbiAgICApO1xuICB9KSxcbik7XG5cbmludGVnVGVzdChcbiAgJ2NsaS1saWIgbGlzdCcsXG4gIHdpdGhDbGlMaWJGaXh0dXJlKGFzeW5jIChmaXh0dXJlKSA9PiB7XG4gICAgY29uc3QgbGlzdGluZyA9IGF3YWl0IGZpeHR1cmUuY2RrKFsnbGlzdCddLCB7IGNhcHR1cmVTdGRlcnI6IGZhbHNlIH0pO1xuICAgIGV4cGVjdChsaXN0aW5nKS50b0NvbnRhaW4oZml4dHVyZS5mdWxsU3RhY2tOYW1lKCdzaW1wbGUtMScpKTtcbiAgfSksXG4pO1xuXG5pbnRlZ1Rlc3QoXG4gICdjbGktbGliIGRlcGxveScsXG4gIHdpdGhDbGlMaWJGaXh0dXJlKGFzeW5jIChmaXh0dXJlKSA9PiB7XG4gICAgY29uc3Qgc3RhY2tOYW1lID0gZml4dHVyZS5mdWxsU3RhY2tOYW1lKCdzaW1wbGUtMScpO1xuXG4gICAgdHJ5IHtcbiAgICAgIC8vIGRlcGxveSB0aGUgc3RhY2tcbiAgICAgIGF3YWl0IGZpeHR1cmUuY2RrKFsnZGVwbG95Jywgc3RhY2tOYW1lXSwge1xuICAgICAgICBuZXZlclJlcXVpcmVBcHByb3ZhbDogdHJ1ZSxcbiAgICAgIH0pO1xuXG4gICAgICAvLyB2ZXJpZnkgdGhlIG51bWJlciBvZiByZXNvdXJjZXMgaW4gdGhlIHN0YWNrXG4gICAgICBjb25zdCBleHBlY3RlZFN0YWNrID0gYXdhaXQgZml4dHVyZS5hd3MuY2xvdWRGb3JtYXRpb24uc2VuZChcbiAgICAgICAgbmV3IERlc2NyaWJlU3RhY2tSZXNvdXJjZXNDb21tYW5kKHtcbiAgICAgICAgICBTdGFja05hbWU6IHN0YWNrTmFtZSxcbiAgICAgICAgfSksXG4gICAgICApO1xuICAgICAgZXhwZWN0KGV4cGVjdGVkU3RhY2suU3RhY2tSZXNvdXJjZXM/Lmxlbmd0aCkudG9FcXVhbCgzKTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgLy8gZGVsZXRlIHRoZSBzdGFja1xuICAgICAgYXdhaXQgZml4dHVyZS5jZGsoWydkZXN0cm95Jywgc3RhY2tOYW1lXSwge1xuICAgICAgICBjYXB0dXJlU3RkZXJyOiBmYWxzZSxcbiAgICAgIH0pO1xuICAgIH1cbiAgfSksXG4pO1xuXG5pbnRlZ1Rlc3QoXG4gICdzZWN1cml0eSByZWxhdGVkIGNoYW5nZXMgd2l0aG91dCBhIENMSSBhcmUgZXhwZWN0ZWQgdG8gZmFpbCB3aGVuIGFwcHJvdmFsIGlzIHJlcXVpcmVkJyxcbiAgd2l0aENsaUxpYkZpeHR1cmUoYXN5bmMgKGZpeHR1cmUpID0+IHtcbiAgICBjb25zdCBzdGRFcnIgPSBhd2FpdCBmaXh0dXJlLmNkayhbJ2RlcGxveScsIGZpeHR1cmUuZnVsbFN0YWNrTmFtZSgnc2ltcGxlLTEnKV0sIHtcbiAgICAgIG9ubHlTdGRlcnI6IHRydWUsXG4gICAgICBjYXB0dXJlU3RkZXJyOiB0cnVlLFxuICAgICAgYWxsb3dFcnJFeGl0OiB0cnVlLFxuICAgICAgbmV2ZXJSZXF1aXJlQXBwcm92YWw6IGZhbHNlLFxuICAgIH0pO1xuXG4gICAgZXhwZWN0KHN0ZEVycikudG9Db250YWluKFxuICAgICAgJ1RoaXMgZGVwbG95bWVudCB3aWxsIG1ha2UgcG90ZW50aWFsbHkgc2Vuc2l0aXZlIGNoYW5nZXMgYWNjb3JkaW5nIHRvIHlvdXIgY3VycmVudCBzZWN1cml0eSBhcHByb3ZhbCBsZXZlbCcsXG4gICAgKTtcbiAgICBleHBlY3Qoc3RkRXJyKS50b0NvbnRhaW4oXG4gICAgICAnXCItLXJlcXVpcmUtYXBwcm92YWxcIiBpcyBlbmFibGVkIGFuZCBzdGFjayBpbmNsdWRlcyBzZWN1cml0eS1zZW5zaXRpdmUgdXBkYXRlcycsXG4gICAgKTtcblxuICAgIC8vIEVuc3VyZSBzdGFjayB3YXMgbm90IGRlcGxveWVkXG4gICAgYXdhaXQgZXhwZWN0KFxuICAgICAgZml4dHVyZS5hd3MuY2xvdWRGb3JtYXRpb24uc2VuZChcbiAgICAgICAgbmV3IERlc2NyaWJlU3RhY2tzQ29tbWFuZCh7XG4gICAgICAgICAgU3RhY2tOYW1lOiBmaXh0dXJlLmZ1bGxTdGFja05hbWUoJ3NpbXBsZS0xJyksXG4gICAgICAgIH0pLFxuICAgICAgKSxcbiAgICApLnJlamVjdHMudG9UaHJvdygnZG9lcyBub3QgZXhpc3QnKTtcbiAgfSksXG4pO1xuIl19
@@ -0,0 +1,90 @@
1
+ import { DescribeStackResourcesCommand, DescribeStacksCommand } from '@aws-sdk/client-cloudformation';
2
+ import { integTest, withCliLibFixture } from '../../lib';
3
+
4
+ jest.setTimeout(2 * 60 * 60_000); // Includes the time to acquire locks, worst-case single-threaded runtime
5
+
6
+ integTest(
7
+ 'cli-lib synth',
8
+ withCliLibFixture(async (fixture) => {
9
+ await fixture.cdk(['synth', fixture.fullStackName('simple-1')]);
10
+ expect(fixture.template('simple-1')).toEqual(
11
+ expect.objectContaining({
12
+ // Checking for a small subset is enough as proof that synth worked
13
+ Resources: expect.objectContaining({
14
+ queue276F7297: expect.objectContaining({
15
+ Type: 'AWS::SQS::Queue',
16
+ Properties: {
17
+ VisibilityTimeout: 300,
18
+ },
19
+ Metadata: {
20
+ 'aws:cdk:path': `${fixture.stackNamePrefix}-simple-1/queue/Resource`,
21
+ },
22
+ }),
23
+ }),
24
+ }),
25
+ );
26
+ }),
27
+ );
28
+
29
+ integTest(
30
+ 'cli-lib list',
31
+ withCliLibFixture(async (fixture) => {
32
+ const listing = await fixture.cdk(['list'], { captureStderr: false });
33
+ expect(listing).toContain(fixture.fullStackName('simple-1'));
34
+ }),
35
+ );
36
+
37
+ integTest(
38
+ 'cli-lib deploy',
39
+ withCliLibFixture(async (fixture) => {
40
+ const stackName = fixture.fullStackName('simple-1');
41
+
42
+ try {
43
+ // deploy the stack
44
+ await fixture.cdk(['deploy', stackName], {
45
+ neverRequireApproval: true,
46
+ });
47
+
48
+ // verify the number of resources in the stack
49
+ const expectedStack = await fixture.aws.cloudFormation.send(
50
+ new DescribeStackResourcesCommand({
51
+ StackName: stackName,
52
+ }),
53
+ );
54
+ expect(expectedStack.StackResources?.length).toEqual(3);
55
+ } finally {
56
+ // delete the stack
57
+ await fixture.cdk(['destroy', stackName], {
58
+ captureStderr: false,
59
+ });
60
+ }
61
+ }),
62
+ );
63
+
64
+ integTest(
65
+ 'security related changes without a CLI are expected to fail when approval is required',
66
+ withCliLibFixture(async (fixture) => {
67
+ const stdErr = await fixture.cdk(['deploy', fixture.fullStackName('simple-1')], {
68
+ onlyStderr: true,
69
+ captureStderr: true,
70
+ allowErrExit: true,
71
+ neverRequireApproval: false,
72
+ });
73
+
74
+ expect(stdErr).toContain(
75
+ 'This deployment will make potentially sensitive changes according to your current security approval level',
76
+ );
77
+ expect(stdErr).toContain(
78
+ '"--require-approval" is enabled and stack includes security-sensitive updates',
79
+ );
80
+
81
+ // Ensure stack was not deployed
82
+ await expect(
83
+ fixture.aws.cloudFormation.send(
84
+ new DescribeStacksCommand({
85
+ StackName: fixture.fullStackName('simple-1'),
86
+ }),
87
+ ),
88
+ ).rejects.toThrow('does not exist');
89
+ }),
90
+ );