Skip to content

Commit f17f29e

Browse files
authoredNov 26, 2021
fix(codepipeline): cross-env pipeline cannot be created in Stage (#17730)
Because a cross-environment pipeline cannot be created in `Stage`, it cannot be deployed using CDK Pipelines. The error is: ``` Error: You cannot add a dependency from 'AAA' (in Stage 'BBB') to 'CCC' (in the App): dependency cannot cross stage boundaries ``` Root cause is that the `Pipeline` construct creates a support stack in the `App` scope, which is outside its containing `Stage`, and hence the dependency crosses stage boundaries. Fixes #17643. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 87fd60f commit f17f29e

File tree

2 files changed

+34
-14
lines changed

2 files changed

+34
-14
lines changed
 

‎packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts

+19-9
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,18 @@ import * as iam from '@aws-cdk/aws-iam';
44
import * as kms from '@aws-cdk/aws-kms';
55
import * as s3 from '@aws-cdk/aws-s3';
66
import {
7-
App, ArnFormat, BootstraplessSynthesizer, DefaultStackSynthesizer,
8-
IStackSynthesizer, Lazy, Names, PhysicalName, RemovalPolicy, Resource, Stack, Token,
7+
ArnFormat,
8+
BootstraplessSynthesizer,
9+
DefaultStackSynthesizer,
10+
IStackSynthesizer,
11+
Lazy,
12+
Names,
13+
PhysicalName,
14+
RemovalPolicy,
15+
Resource,
16+
Stack,
17+
Stage as CdkStage,
18+
Token,
919
} from '@aws-cdk/core';
1020
import { Construct } from 'constructs';
1121
import { ActionCategory, IAction, IPipeline, IStage, PipelineNotificationEvents, PipelineNotifyOnOptions } from './action';
@@ -620,7 +630,7 @@ export class Pipeline extends PipelineBase {
620630
throw new Error("You need to specify an explicit account when using CodePipeline's cross-region support");
621631
}
622632

623-
const app = this.requireApp();
633+
const app = this.supportScope();
624634
const supportStackId = `cross-region-stack-${pipelineAccount}:${actionRegion}`;
625635
let supportStack = app.node.tryFindChild(supportStackId) as CrossRegionSupportStack;
626636
if (!supportStack) {
@@ -816,7 +826,7 @@ export class Pipeline extends PipelineBase {
816826
let targetAccountStack: Stack | undefined = this._crossAccountSupport[targetAccount];
817827
if (!targetAccountStack) {
818828
const stackId = `cross-account-support-stack-${targetAccount}`;
819-
const app = this.requireApp();
829+
const app = this.supportScope();
820830
targetAccountStack = app.node.tryFindChild(stackId) as Stack;
821831
if (!targetAccountStack) {
822832
const actionRegion = action.actionProperties.resource
@@ -1026,12 +1036,12 @@ export class Pipeline extends PipelineBase {
10261036
return region;
10271037
}
10281038

1029-
private requireApp(): App {
1030-
const app = this.node.root;
1031-
if (!app || !App.isApp(app)) {
1032-
throw new Error('Pipeline stack which uses cross-environment actions must be part of a CDK app');
1039+
private supportScope(): CdkStage {
1040+
const scope = CdkStage.of(this);
1041+
if (!scope) {
1042+
throw new Error('Pipeline stack which uses cross-environment actions must be part of a CDK App or Stage');
10331043
}
1034-
return app;
1044+
return scope;
10351045
}
10361046
}
10371047

‎packages/@aws-cdk/aws-codepipeline/test/cross-env.test.ts

+15-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
import '@aws-cdk/assert-internal/jest';
22
import * as iam from '@aws-cdk/aws-iam';
33
import * as s3 from '@aws-cdk/aws-s3';
4-
import { Stack, App } from '@aws-cdk/core';
4+
import { Stack, App, Stage as CdkStage } from '@aws-cdk/core';
5+
import { Construct } from 'constructs';
56
import * as codepipeline from '../lib';
67
import { FakeBuildAction } from './fake-build-action';
78
import { FakeSourceAction } from './fake-source-action';
89

9-
describe.each(['legacy', 'modern'])('with %s synthesis', (synthesisStyle: string) => {
10+
describe.each([
11+
['legacy', false],
12+
['legacy', true],
13+
['modern', false],
14+
['modern', true],
15+
])('with %s synthesis, in Stage=%p', (synthesisStyle: string, inStage: boolean) => {
1016
let app: App;
17+
let stackScope: Construct;
1118
let stack: Stack;
1219
let sourceArtifact: codepipeline.Artifact;
1320
let initialStages: codepipeline.StageProps[];
@@ -18,7 +25,9 @@ describe.each(['legacy', 'modern'])('with %s synthesis', (synthesisStyle: string
1825
...synthesisStyle === 'modern' ? { '@aws-cdk/core:newStyleStackSynthesis': true } : undefined,
1926
},
2027
});
21-
stack = new Stack(app, 'PipelineStack', { env: { account: '2222', region: 'us-east-1' } });
28+
stackScope = inStage ? new CdkStage(app, 'MyStage') : app;
29+
30+
stack = new Stack(stackScope, 'PipelineStack', { env: { account: '2222', region: 'us-east-1' } });
2231
sourceArtifact = new codepipeline.Artifact();
2332
initialStages = [
2433
{
@@ -114,7 +123,8 @@ describe.each(['legacy', 'modern'])('with %s synthesis', (synthesisStyle: string
114123
}));
115124

116125
// THEN
117-
const asm = app.synth();
126+
let asm = app.synth();
127+
asm = inStage ? asm.getNestedAssembly('assembly-MyStage') : asm;
118128
const supportStack = asm.getStackByName(`${stack.stackName}-support-eu-west-1`);
119129

120130
// THEN
@@ -123,7 +133,7 @@ describe.each(['legacy', 'modern'])('with %s synthesis', (synthesisStyle: string
123133
});
124134

125135
test('when twiddling another stack', () => {
126-
const stack2 = new Stack(app, 'Stack2', { env: { account: '2222', region: 'eu-west-1' } });
136+
const stack2 = new Stack(stackScope, 'Stack2', { env: { account: '2222', region: 'eu-west-1' } });
127137

128138
// WHEN
129139
stage.addAction(new FakeBuildAction({

0 commit comments

Comments
 (0)
Please sign in to comment.