diff --git a/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/sdk-api-metadata.json b/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/sdk-api-metadata.json index 83d53e4eb03bc..c1f19ce849b8e 100644 --- a/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/sdk-api-metadata.json +++ b/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/sdk-api-metadata.json @@ -1284,16 +1284,5 @@ }, "internetmonitor": { "name": "InternetMonitor" - }, - "ivsrealtime": { - "prefix": "ivs-realtime", - "name": "IVSRealTime" - }, - "vpclattice": { - "prefix": "vpc-lattice", - "name": "VPCLattice" - }, - "osis": { - "name": "OSIS" } } \ No newline at end of file diff --git a/packages/aws-cdk/lib/util/work-graph.ts b/packages/aws-cdk/lib/util/work-graph.ts index c51a9a2b3f4e8..d35eaf8b9d166 100644 --- a/packages/aws-cdk/lib/util/work-graph.ts +++ b/packages/aws-cdk/lib/util/work-graph.ts @@ -272,7 +272,7 @@ export class WorkGraph { this.readyPool.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0)); if (this.readyPool.length === 0 && activeCount === 0 && pendingCount > 0) { - throw new Error(`Unable to make progress anymore among: ${this}`); + throw new Error(`Unable to make progress anymore, dependency cycle between remaining artifacts: ${this.findCycle().join(' -> ')}`); } } @@ -283,6 +283,31 @@ export class WorkGraph { } } } + + /** + * Find cycles in a graph + * + * Not the fastest, but effective and should be rare + */ + private findCycle(): string[] { + const self = this; + for (const nodeId of Object.keys(this.nodes)) { + const cycle = recurse(nodeId, [nodeId]); + if (cycle) { return cycle; } + } + return ['No cycle found!']; + + function recurse(nodeId: string, path: string[]): string[] | undefined { + for (const dep of self.nodes[nodeId].dependencies ?? []) { + if (dep === path[0]) { return [...path, dep]; } + + const cycle = recurse(dep, [...path, dep]); + if (cycle) { return cycle; } + } + + return undefined; + } + } } export interface WorkGraphActions { @@ -297,4 +322,4 @@ function sum(xs: number[]) { ret += x; } return ret; -} \ No newline at end of file +} diff --git a/packages/aws-cdk/test/work-graph.test.ts b/packages/aws-cdk/test/work-graph.test.ts index 5b7a2d1235ec8..8486d667e991f 100644 --- a/packages/aws-cdk/test/work-graph.test.ts +++ b/packages/aws-cdk/test/work-graph.test.ts @@ -328,6 +328,7 @@ describe('WorkGraph', () => { toDeploy: createArtifacts([ { id: 'A', type: 'stack', stackDependencies: ['A'] }, ]), + expectedError: 'A -> A', }, { scenario: 'A -> B, B -> A', @@ -335,6 +336,7 @@ describe('WorkGraph', () => { { id: 'A', type: 'stack', stackDependencies: ['B'] }, { id: 'B', type: 'stack', stackDependencies: ['A'] }, ]), + expectedError: 'A -> B -> A', }, { scenario: 'A, B -> C, C -> D, D -> B', @@ -344,12 +346,13 @@ describe('WorkGraph', () => { { id: 'C', type: 'stack', stackDependencies: ['D'] }, { id: 'D', type: 'stack', stackDependencies: ['B'] }, ]), + expectedError: 'B -> C -> D -> B', }, - ])('Failure - Graph Circular Dependencies - $scenario', async ({ toDeploy }) => { + ])('Failure - Graph Circular Dependencies - $scenario', async ({ toDeploy, expectedError }) => { const graph = new WorkGraph(); addTestArtifactsToGraph(toDeploy, graph); - await expect(graph.doParallel(1, callbacks)).rejects.toThrowError(/Unable to make progress anymore/); + await expect(graph.doParallel(1, callbacks)).rejects.toThrowError(new RegExp(`Unable to make progress.*${expectedError}`)); }); });