From 009d68912267de9dcf4136a7d80a652a891b7bb9 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Fri, 28 Jan 2022 18:13:51 +0100 Subject: [PATCH] fix(pipelines): undeployable due to dependency cycle (#18686) A dependency cycle was inadvertently introduced to CDK Pipelines in #18492. Fix that dependency cycle, and also one in Cognito IdentityPools. Add facilities to the `assertions` library to automatically detect this in the future, to stop errors like this from slipping in. We could make it a separate assertion method (`Template.fromStack().assertNoCycles()`), but the only thing that will do is give you an opportunity to forget to put the test in. Instead, we just check it by default for every generated template. Fixes #18673. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../assertions/lib/private/conditions.ts | 4 +- .../@aws-cdk/assertions/lib/private/cyclic.ts | 175 ++++++++++++++++++ .../assertions/lib/private/mappings.ts | 4 +- .../assertions/lib/private/outputs.ts | 4 +- .../assertions/lib/private/parameters.ts | 4 +- .../assertions/lib/private/resources.ts | 8 +- .../assertions/lib/private/template.ts | 14 +- packages/@aws-cdk/assertions/lib/template.ts | 2 + .../@aws-cdk/assertions/test/template.test.ts | 31 +++- .../lib/identitypool.ts | 14 +- .../test/integ.identitypool.expected.json | 5 - .../lib/codepipeline/_codebuild-factory.ts | 8 +- .../test/codepipeline/codebuild-step.test.ts | 7 +- .../integ.newpipeline-with-vpc.expected.json | 52 +----- .../test/integ.newpipeline.expected.json | 26 +-- .../@monocdk-experiment/assert/tsconfig.json | 4 +- packages/aws-cdk-lib/package.json | 5 +- 17 files changed, 254 insertions(+), 113 deletions(-) create mode 100644 packages/@aws-cdk/assertions/lib/private/cyclic.ts diff --git a/packages/@aws-cdk/assertions/lib/private/conditions.ts b/packages/@aws-cdk/assertions/lib/private/conditions.ts index e7c4665dee219..6ed10379dea9e 100644 --- a/packages/@aws-cdk/assertions/lib/private/conditions.ts +++ b/packages/@aws-cdk/assertions/lib/private/conditions.ts @@ -2,7 +2,7 @@ import { filterLogicalId, formatFailure, matchSection } from './section'; import { Template } from './template'; export function findConditions(template: Template, logicalId: string, props: any = {}): { [key: string]: { [key: string]: any } } { - const section: { [key: string] : {} } = template.Conditions; + const section: { [key: string] : {} } = template.Conditions ?? {}; const result = matchSection(filterLogicalId(section, logicalId), props); if (!result.match) { @@ -13,7 +13,7 @@ export function findConditions(template: Template, logicalId: string, props: any } export function hasCondition(template: Template, logicalId: string, props: any): string | void { - const section: { [key: string] : {} } = template.Conditions; + const section: { [key: string] : {} } = template.Conditions ?? {}; const result = matchSection(filterLogicalId(section, logicalId), props); if (result.match) { return; diff --git a/packages/@aws-cdk/assertions/lib/private/cyclic.ts b/packages/@aws-cdk/assertions/lib/private/cyclic.ts new file mode 100644 index 0000000000000..07451a126ad28 --- /dev/null +++ b/packages/@aws-cdk/assertions/lib/private/cyclic.ts @@ -0,0 +1,175 @@ +import { Resource, Template } from './template'; + +/** + * Check a template for cyclic dependencies + * + * This will make sure that we don't happily validate templates + * in unit tests that wouldn't deploy to CloudFormation anyway. + */ +export function checkTemplateForCyclicDependencies(template: Template): void { + const logicalIds = new Set(Object.keys(template.Resources ?? {})); + + const dependencies = new Map>(); + for (const [logicalId, resource] of Object.entries(template.Resources ?? {})) { + dependencies.set(logicalId, intersect(findResourceDependencies(resource), logicalIds)); + } + + // We will now progressively remove entries from the map of 'dependencies' that have + // 0 elements in them. If we can't do that anymore and the map isn't empty, we + // have a cyclic dependency. + while (dependencies.size > 0) { + const free = Array.from(dependencies.entries()).filter(([_, deps]) => deps.size === 0); + if (free.length === 0) { + // Oops! + const cycle = findCycle(dependencies); + + const cycleResources: any = {}; + for (const logicalId of cycle) { + cycleResources[logicalId] = template.Resources?.[logicalId]; + } + + throw new Error(`Template is undeployable, these resources have a dependency cycle: ${cycle.join(' -> ')}:\n\n${JSON.stringify(cycleResources, undefined, 2)}`); + } + + for (const [logicalId, _] of free) { + for (const deps of dependencies.values()) { + deps.delete(logicalId); + } + dependencies.delete(logicalId); + } + } +} + +function findResourceDependencies(res: Resource): Set { + return new Set([ + ...toArray(res.DependsOn ?? []), + ...findExpressionDependencies(res.Properties), + ]); +} + +function toArray(x: A | A[]): A[] { + return Array.isArray(x) ? x : [x]; +} + +function findExpressionDependencies(obj: any): Set { + const ret = new Set(); + recurse(obj); + return ret; + + function recurse(x: any): void { + if (!x) { return; } + if (Array.isArray(x)) { + x.forEach(recurse); + } + if (typeof x === 'object') { + const keys = Object.keys(x); + if (keys.length === 1 && keys[0] === 'Ref') { + ret.add(x[keys[0]]); + } else if (keys.length === 1 && keys[0] === 'Fn::GetAtt') { + ret.add(x[keys[0]][0]); + } else if (keys.length === 1 && keys[0] === 'Fn::Sub') { + const argument = x[keys[0]]; + const pattern = Array.isArray(argument) ? argument[0] : argument; + for (const logId of logicalIdsInSubString(pattern)) { + ret.add(logId); + } + const contextDict = Array.isArray(argument) ? argument[1] : undefined; + if (contextDict) { + Object.values(contextDict).forEach(recurse); + } + } else { + Object.values(x).forEach(recurse); + } + } + } +} + +/** + * Return the logical IDs found in a {Fn::Sub} format string + */ +function logicalIdsInSubString(x: string): string[] { + return analyzeSubPattern(x).flatMap((fragment) => { + switch (fragment.type) { + case 'getatt': + case 'ref': + return [fragment.logicalId]; + case 'literal': + return []; + } + }); +} + + +function analyzeSubPattern(pattern: string): SubFragment[] { + const ret: SubFragment[] = []; + let start = 0; + + let ph0 = pattern.indexOf('${', start); + while (ph0 > -1) { + if (pattern[ph0 + 2] === '!') { + // "${!" means "don't actually substitute" + start = ph0 + 3; + ph0 = pattern.indexOf('${', start); + continue; + } + + const ph1 = pattern.indexOf('}', ph0 + 2); + if (ph1 === -1) { + break; + } + const placeholder = pattern.substring(ph0 + 2, ph1); + + if (ph0 > start) { + ret.push({ type: 'literal', content: pattern.substring(start, ph0) }); + } + if (placeholder.includes('.')) { + const [logicalId, attr] = placeholder.split('.'); + ret.push({ type: 'getatt', logicalId: logicalId!, attr: attr! }); + } else { + ret.push({ type: 'ref', logicalId: placeholder }); + } + + start = ph1 + 1; + ph0 = pattern.indexOf('${', start); + } + + if (start < pattern.length - 1) { + ret.push({ type: 'literal', content: pattern.substr(start) }); + } + + return ret; +} + +type SubFragment = + | { readonly type: 'literal'; readonly content: string } + | { readonly type: 'ref'; readonly logicalId: string } + | { readonly type: 'getatt'; readonly logicalId: string; readonly attr: string }; + + +function intersect(xs: Set, ys: Set): Set { + return new Set(Array.from(xs).filter(x => ys.has(x))); +} + +/** + * Find cycles in a graph + * + * Not the fastest, but effective and should be rare + */ +function findCycle(deps: ReadonlyMap>): string[] { + for (const node of deps.keys()) { + const cycle = recurse(node, [node]); + if (cycle) { return cycle; } + } + throw new Error('No cycle found. Assertion failure!'); + + function recurse(node: string, path: string[]): string[] | undefined { + for (const dep of deps.get(node) ?? []) { + if (dep === path[0]) { return [...path, dep]; } + + const cycle = recurse(dep, [...path, dep]); + if (cycle) { return cycle; } + } + + return undefined; + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/assertions/lib/private/mappings.ts b/packages/@aws-cdk/assertions/lib/private/mappings.ts index e080843dd87f8..e8788fb2ef112 100644 --- a/packages/@aws-cdk/assertions/lib/private/mappings.ts +++ b/packages/@aws-cdk/assertions/lib/private/mappings.ts @@ -2,7 +2,7 @@ import { filterLogicalId, formatFailure, matchSection } from './section'; import { Template } from './template'; export function findMappings(template: Template, logicalId: string, props: any = {}): { [key: string]: { [key: string]: any } } { - const section: { [key: string] : {} } = template.Mappings; + const section: { [key: string] : {} } = template.Mappings ?? {}; const result = matchSection(filterLogicalId(section, logicalId), props); if (!result.match) { @@ -13,7 +13,7 @@ export function findMappings(template: Template, logicalId: string, props: any = } export function hasMapping(template: Template, logicalId: string, props: any): string | void { - const section: { [key: string]: {} } = template.Mappings; + const section: { [key: string]: {} } = template.Mappings ?? {}; const result = matchSection(filterLogicalId(section, logicalId), props); if (result.match) { diff --git a/packages/@aws-cdk/assertions/lib/private/outputs.ts b/packages/@aws-cdk/assertions/lib/private/outputs.ts index f00f05bc9bb0f..39509698d0e43 100644 --- a/packages/@aws-cdk/assertions/lib/private/outputs.ts +++ b/packages/@aws-cdk/assertions/lib/private/outputs.ts @@ -2,7 +2,7 @@ import { filterLogicalId, formatFailure, matchSection } from './section'; import { Template } from './template'; export function findOutputs(template: Template, logicalId: string, props: any = {}): { [key: string]: { [key: string]: any } } { - const section = template.Outputs; + const section = template.Outputs ?? {}; const result = matchSection(filterLogicalId(section, logicalId), props); if (!result.match) { @@ -13,7 +13,7 @@ export function findOutputs(template: Template, logicalId: string, props: any = } export function hasOutput(template: Template, logicalId: string, props: any): string | void { - const section: { [key: string]: {} } = template.Outputs; + const section: { [key: string]: {} } = template.Outputs ?? {}; const result = matchSection(filterLogicalId(section, logicalId), props); if (result.match) { return; diff --git a/packages/@aws-cdk/assertions/lib/private/parameters.ts b/packages/@aws-cdk/assertions/lib/private/parameters.ts index b708460caf399..0e73160ea5a75 100644 --- a/packages/@aws-cdk/assertions/lib/private/parameters.ts +++ b/packages/@aws-cdk/assertions/lib/private/parameters.ts @@ -2,7 +2,7 @@ import { filterLogicalId, formatFailure, matchSection } from './section'; import { Template } from './template'; export function findParameters(template: Template, logicalId: string, props: any = {}): { [key: string]: { [key: string]: any } } { - const section: { [key: string] : {} } = template.Parameters; + const section: { [key: string] : {} } = template.Parameters ?? {}; const result = matchSection(filterLogicalId(section, logicalId), props); if (!result.match) { @@ -13,7 +13,7 @@ export function findParameters(template: Template, logicalId: string, props: any } export function hasParameter(template: Template, logicalId: string, props: any): string | void { - const section: { [key: string] : {} } = template.Parameters; + const section: { [key: string] : {} } = template.Parameters ?? {}; const result = matchSection(filterLogicalId(section, logicalId), props); if (result.match) { return; diff --git a/packages/@aws-cdk/assertions/lib/private/resources.ts b/packages/@aws-cdk/assertions/lib/private/resources.ts index 68e8e6c2ddff8..00a57c05f9b26 100644 --- a/packages/@aws-cdk/assertions/lib/private/resources.ts +++ b/packages/@aws-cdk/assertions/lib/private/resources.ts @@ -4,7 +4,7 @@ import { formatFailure, matchSection } from './section'; import { Resource, Template } from './template'; export function findResources(template: Template, type: string, props: any = {}): { [key: string]: { [key: string]: any } } { - const section = template.Resources; + const section = template.Resources ?? {}; const result = matchSection(filterType(section, type), props); if (!result.match) { @@ -15,7 +15,7 @@ export function findResources(template: Template, type: string, props: any = {}) } export function hasResource(template: Template, type: string, props: any): string | void { - const section = template.Resources; + const section = template.Resources ?? {}; const result = matchSection(filterType(section, type), props); if (result.match) { return; @@ -46,14 +46,14 @@ export function hasResourceProperties(template: Template, type: string, props: a } export function countResources(template: Template, type: string): number { - const section = template.Resources; + const section = template.Resources ?? {}; const types = filterType(section, type); return Object.entries(types).length; } function addEmptyProperties(template: Template): Template { - let section = template.Resources; + let section = template.Resources ?? {}; Object.keys(section).map((key) => { if (!section[key].hasOwnProperty('Properties')) { diff --git a/packages/@aws-cdk/assertions/lib/private/template.ts b/packages/@aws-cdk/assertions/lib/private/template.ts index fc5d0cb6b1e01..4aea34a2b5132 100644 --- a/packages/@aws-cdk/assertions/lib/private/template.ts +++ b/packages/@aws-cdk/assertions/lib/private/template.ts @@ -1,15 +1,19 @@ // Partial types for CloudFormation Template export type Template = { - Resources: { [logicalId: string]: Resource }, - Outputs: { [logicalId: string]: Output }, - Mappings: { [logicalId: string]: Mapping }, - Parameters: { [logicalId: string]: Parameter }, - Conditions: { [logicalId: string]: Condition }, + // In actuality this is not optional, but we sometimes don't generate it so we + // need to account for that. + Resources?: { [logicalId: string]: Resource }, + Outputs?: { [logicalId: string]: Output }, + Mappings?: { [logicalId: string]: Mapping }, + Parameters?: { [logicalId: string]: Parameter }, + Conditions?: { [logicalId: string]: Condition }, } export type Resource = { Type: string; + DependsOn?: string | string[]; + Properties?: { [key: string]: any }; [key: string]: any; } diff --git a/packages/@aws-cdk/assertions/lib/template.ts b/packages/@aws-cdk/assertions/lib/template.ts index ec23538eaf4aa..6399d3a971897 100644 --- a/packages/@aws-cdk/assertions/lib/template.ts +++ b/packages/@aws-cdk/assertions/lib/template.ts @@ -4,6 +4,7 @@ import * as fs from 'fs-extra'; import { Match } from './match'; import { Matcher } from './matcher'; import { findConditions, hasCondition } from './private/conditions'; +import { checkTemplateForCyclicDependencies } from './private/cyclic'; import { findMappings, hasMapping } from './private/mappings'; import { findOutputs, hasOutput } from './private/outputs'; import { findParameters, hasParameter } from './private/parameters'; @@ -47,6 +48,7 @@ export class Template { private constructor(template: { [key: string]: any }) { this.template = template as TemplateType; + checkTemplateForCyclicDependencies(this.template); } /** diff --git a/packages/@aws-cdk/assertions/test/template.test.ts b/packages/@aws-cdk/assertions/test/template.test.ts index 92bdb405ab9ce..dcdb73e61da71 100644 --- a/packages/@aws-cdk/assertions/test/template.test.ts +++ b/packages/@aws-cdk/assertions/test/template.test.ts @@ -5,11 +5,11 @@ import { Capture, Match, Template } from '../lib'; describe('Template', () => { test('fromString', () => { const template = Template.fromString(`{ - "Resources": { - "Foo": { + "Resources": { + "Foo": { "Type": "Baz::Qux", "Properties": { "Fred": "Waldo" } - } + } } }`); @@ -79,11 +79,11 @@ describe('Template', () => { describe('fromString', () => { test('default', () => { const assertions = Template.fromString(`{ - "Resources": { - "Foo": { + "Resources": { + "Foo": { "Type": "Baz::Qux", "Properties": { "Fred": "Waldo" } - } + } } }`); assertions.resourceCountIs('Baz::Qux', 1); @@ -1084,6 +1084,25 @@ describe('Template', () => { expect(Object.keys(result).length).toEqual(0); }); }); + + test('throws when given a template with cyclic dependencies', () => { + expect(() => { + Template.fromJSON({ + Resources: { + Res1: { + Type: 'Foo', + Properties: { + Thing: { Ref: 'Res2' }, + }, + }, + Res2: { + Type: 'Foo', + DependsOn: ['Res1'], + }, + }, + }); + }).toThrow(/dependency cycle/); + }); }); function expectToThrow(fn: () => void, msgs: (RegExp | string)[], done: jest.DoneCallback): void { diff --git a/packages/@aws-cdk/aws-cognito-identitypool/lib/identitypool.ts b/packages/@aws-cdk/aws-cognito-identitypool/lib/identitypool.ts index cefab0caa54d9..1fd89f238e0b0 100644 --- a/packages/@aws-cdk/aws-cognito-identitypool/lib/identitypool.ts +++ b/packages/@aws-cdk/aws-cognito-identitypool/lib/identitypool.ts @@ -437,7 +437,12 @@ export class IdentityPool extends Resource implements IIdentityPool { unauthenticatedRole: this.unauthenticatedRole, roleMappings: props.roleMappings, }); - attachment.node.addDependency(this); + + // This added by the original author, but it's causing cyclic dependencies. + // Don't know why this was added in the first place, but I'm disabling it for now and if + // no complaints come from this, we're probably safe to remove it altogether. + // attachment.node.addDependency(this); + Array.isArray(attachment); } /** @@ -461,7 +466,12 @@ export class IdentityPool extends Resource implements IIdentityPool { unauthenticatedRole: this.unauthenticatedRole, roleMappings, }); - attachment.node.addDependency(this); + + // This added by the original author, but it's causing cyclic dependencies. + // Don't know why this was added in the first place, but I'm disabling it for now and if + // no complaints come from this, we're probably safe to remove it altogether. + // attachment.node.addDependency(this); + Array.isArray(attachment); } /** diff --git a/packages/@aws-cdk/aws-cognito-identitypool/test/integ.identitypool.expected.json b/packages/@aws-cdk/aws-cognito-identitypool/test/integ.identitypool.expected.json index b555de31baa1e..b2ec1baf42d81 100644 --- a/packages/@aws-cdk/aws-cognito-identitypool/test/integ.identitypool.expected.json +++ b/packages/@aws-cdk/aws-cognito-identitypool/test/integ.identitypool.expected.json @@ -402,11 +402,6 @@ } }, "DependsOn": [ - "identitypoolAuthenticatedRoleDefaultPolicyCB4D2992", - "identitypoolAuthenticatedRoleB074B49D", - "identitypoolE2A6D099", - "identitypoolUnauthenticatedRoleDefaultPolicyBFACCE98", - "identitypoolUnauthenticatedRoleE61CAC70", "OtherPool7DA7F2F7", "OtherPoolUserPoolAuthenticationProviderClient08F670F8", "PoolD3F588B8", diff --git a/packages/@aws-cdk/pipelines/lib/codepipeline/_codebuild-factory.ts b/packages/@aws-cdk/pipelines/lib/codepipeline/_codebuild-factory.ts index 5d73e23784314..3414554cd5197 100644 --- a/packages/@aws-cdk/pipelines/lib/codepipeline/_codebuild-factory.ts +++ b/packages/@aws-cdk/pipelines/lib/codepipeline/_codebuild-factory.ts @@ -5,7 +5,7 @@ import * as codepipeline from '@aws-cdk/aws-codepipeline'; import * as codepipeline_actions from '@aws-cdk/aws-codepipeline-actions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; -import { IDependable, Stack } from '@aws-cdk/core'; +import { IDependable, Stack, Token } from '@aws-cdk/core'; import { Construct, Node } from 'constructs'; import { FileSetLocation, ShellStep, StackOutputReference } from '../blueprint'; import { PipelineQueries } from '../helpers-internal/pipeline-queries'; @@ -271,9 +271,13 @@ export class CodeBuildFactory implements ICodePipelineActionFactory { projectScope = obtainScope(scope, actionName); } + const safePipelineName = Token.isUnresolved(options.pipeline.pipeline.pipelineName) + ? `${Stack.of(options.pipeline).stackName}/${Node.of(options.pipeline.pipeline).id}` + : options.pipeline.pipeline.pipelineName; + const project = new codebuild.PipelineProject(projectScope, this.constructId, { projectName: this.props.projectName, - description: `Pipeline step ${options.pipeline.pipeline.pipelineName}/${stage.stageName}/${actionName}`, + description: `Pipeline step ${safePipelineName}/${stage.stageName}/${actionName}`.substring(0, 255), environment, vpc: projectOptions.vpc, subnetSelection: projectOptions.subnetSelection, diff --git a/packages/@aws-cdk/pipelines/test/codepipeline/codebuild-step.test.ts b/packages/@aws-cdk/pipelines/test/codepipeline/codebuild-step.test.ts index 01069ee7adee0..393f1ffb965ba 100644 --- a/packages/@aws-cdk/pipelines/test/codepipeline/codebuild-step.test.ts +++ b/packages/@aws-cdk/pipelines/test/codepipeline/codebuild-step.test.ts @@ -55,12 +55,7 @@ test('CodeBuild projects have a description', () => { Template.fromStack(pipelineStack).hasResourceProperties( 'AWS::CodeBuild::Project', { - Description: { - 'Fn::Join': [ - '', - ['Pipeline step ', { Ref: 'Pipeline9850B417' }, '/Build/Synth'], - ], - }, + Description: 'Pipeline step PipelineStack/Pipeline/Build/Synth', }, ); }); diff --git a/packages/@aws-cdk/pipelines/test/integ.newpipeline-with-vpc.expected.json b/packages/@aws-cdk/pipelines/test/integ.newpipeline-with-vpc.expected.json index a3f12aa278c48..4bd2e638afb4c 100644 --- a/packages/@aws-cdk/pipelines/test/integ.newpipeline-with-vpc.expected.json +++ b/packages/@aws-cdk/pipelines/test/integ.newpipeline-with-vpc.expected.json @@ -1363,18 +1363,7 @@ "Cache": { "Type": "NO_CACHE" }, - "Description": { - "Fn::Join": [ - "", - [ - "Pipeline step ", - { - "Ref": "Pipeline9850B417" - }, - "/Build/Synth" - ] - ] - }, + "Description": "Pipeline step PipelineStack/Pipeline/Build/Synth", "EncryptionKey": "alias/aws/s3", "VpcConfig": { "SecurityGroupIds": [ @@ -1962,18 +1951,7 @@ "Cache": { "Type": "NO_CACHE" }, - "Description": { - "Fn::Join": [ - "", - [ - "Pipeline step ", - { - "Ref": "Pipeline9850B417" - }, - "/UpdatePipeline/SelfMutate" - ] - ] - }, + "Description": "Pipeline step PipelineStack/Pipeline/UpdatePipeline/SelfMutate", "EncryptionKey": "alias/aws/s3", "VpcConfig": { "SecurityGroupIds": [ @@ -2316,18 +2294,7 @@ "Cache": { "Type": "NO_CACHE" }, - "Description": { - "Fn::Join": [ - "", - [ - "Pipeline step ", - { - "Ref": "Pipeline9850B417" - }, - "/Assets/FileAsset1" - ] - ] - }, + "Description": "Pipeline step PipelineStack/Pipeline/Assets/FileAsset1", "EncryptionKey": "alias/aws/s3", "VpcConfig": { "SecurityGroupIds": [ @@ -2429,18 +2396,7 @@ "Cache": { "Type": "NO_CACHE" }, - "Description": { - "Fn::Join": [ - "", - [ - "Pipeline step ", - { - "Ref": "Pipeline9850B417" - }, - "/Assets/FileAsset2" - ] - ] - }, + "Description": "Pipeline step PipelineStack/Pipeline/Assets/FileAsset2", "EncryptionKey": "alias/aws/s3", "VpcConfig": { "SecurityGroupIds": [ diff --git a/packages/@aws-cdk/pipelines/test/integ.newpipeline.expected.json b/packages/@aws-cdk/pipelines/test/integ.newpipeline.expected.json index 47c1f4d129631..37cd5d99fd7f8 100644 --- a/packages/@aws-cdk/pipelines/test/integ.newpipeline.expected.json +++ b/packages/@aws-cdk/pipelines/test/integ.newpipeline.expected.json @@ -2044,18 +2044,7 @@ "Cache": { "Type": "NO_CACHE" }, - "Description": { - "Fn::Join": [ - "", - [ - "Pipeline step ", - { - "Ref": "Pipeline9850B417" - }, - "/Build/Synth" - ] - ] - }, + "Description": "Pipeline step PipelineStack/Pipeline/Build/Synth", "EncryptionKey": "alias/aws/s3" } }, @@ -2355,18 +2344,7 @@ "Cache": { "Type": "NO_CACHE" }, - "Description": { - "Fn::Join": [ - "", - [ - "Pipeline step ", - { - "Ref": "Pipeline9850B417" - }, - "/UpdatePipeline/SelfMutate" - ] - ] - }, + "Description": "Pipeline step PipelineStack/Pipeline/UpdatePipeline/SelfMutate", "EncryptionKey": "alias/aws/s3" } } diff --git a/packages/@monocdk-experiment/assert/tsconfig.json b/packages/@monocdk-experiment/assert/tsconfig.json index b426f95fcb96a..e1b9688cb975e 100644 --- a/packages/@monocdk-experiment/assert/tsconfig.json +++ b/packages/@monocdk-experiment/assert/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { - "target":"ES2018", - "lib": ["es2018"], + "target":"ES2019", + "lib": ["es2019"], "module": "CommonJS", "declaration": true, "strict": true, diff --git a/packages/aws-cdk-lib/package.json b/packages/aws-cdk-lib/package.json index e2ec7540fb741..61d5b8455d10f 100644 --- a/packages/aws-cdk-lib/package.json +++ b/packages/aws-cdk-lib/package.json @@ -433,7 +433,6 @@ "./aws-codestarconnections": "./aws-codestarconnections/index.js", "./aws-codestarnotifications": "./aws-codestarnotifications/index.js", "./aws-cognito": "./aws-cognito/index.js", - "./aws-cognito-identitypool": "./aws-cognito-identitypool/index.js", "./aws-config": "./aws-config/index.js", "./aws-connect": "./aws-connect/index.js", "./aws-cur": "./aws-cur/index.js", @@ -472,6 +471,7 @@ "./aws-finspace": "./aws-finspace/index.js", "./aws-fis": "./aws-fis/index.js", "./aws-fms": "./aws-fms/index.js", + "./aws-forecast": "./aws-forecast/index.js", "./aws-frauddetector": "./aws-frauddetector/index.js", "./aws-fsx": "./aws-fsx/index.js", "./aws-gamelift": "./aws-gamelift/index.js", @@ -486,6 +486,7 @@ "./aws-iam": "./aws-iam/index.js", "./aws-imagebuilder": "./aws-imagebuilder/index.js", "./aws-inspector": "./aws-inspector/index.js", + "./aws-inspectorv2": "./aws-inspectorv2/index.js", "./aws-iot": "./aws-iot/index.js", "./aws-iot1click": "./aws-iot1click/index.js", "./aws-iotanalytics": "./aws-iotanalytics/index.js", @@ -499,7 +500,9 @@ "./aws-kendra": "./aws-kendra/index.js", "./aws-kinesis": "./aws-kinesis/index.js", "./aws-kinesisanalytics": "./aws-kinesisanalytics/index.js", + "./aws-kinesisanalyticsv2": "./aws-kinesisanalyticsv2/index.js", "./aws-kinesisfirehose": "./aws-kinesisfirehose/index.js", + "./aws-kinesisvideo": "./aws-kinesisvideo/index.js", "./aws-kms": "./aws-kms/index.js", "./aws-lakeformation": "./aws-lakeformation/index.js", "./aws-lambda": "./aws-lambda/index.js",