/
stage-deployment.ts
142 lines (126 loc) · 4.19 KB
/
stage-deployment.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import * as cdk from '@aws-cdk/core';
import { CloudFormationStackArtifact } from '@aws-cdk/cx-api';
import { isStackArtifact } from '../private/cloud-assembly-internals';
import { pipelineSynth } from '../private/construct-internals';
import { StackDeployment } from './stack-deployment';
import { StackSteps, Step } from './step';
/**
* Properties for a `StageDeployment`
*/
export interface StageDeploymentProps {
/**
* Stage name to use in the pipeline
*
* @default - Use Stage's construct ID
*/
readonly stageName?: string;
/**
* Additional steps to run before any of the stacks in the stage
*
* @default - No additional steps
*/
readonly pre?: Step[];
/**
* Additional steps to run after all of the stacks in the stage
*
* @default - No additional steps
*/
readonly post?: Step[];
/**
* Instructions for additional steps that are run at the stack level
*
* @default - No additional instructions
*/
readonly stackSteps?: StackSteps[];
}
/**
* Deployment of a single `Stage`
*
* A `Stage` consists of one or more `Stacks`, which will be
* deployed in dependency order.
*/
export class StageDeployment {
/**
* Create a new `StageDeployment` from a `Stage`
*
* Synthesizes the target stage, and deployes the stacks found inside
* in dependency order.
*/
public static fromStage(stage: cdk.Stage, props: StageDeploymentProps = {}) {
const assembly = pipelineSynth(stage);
if (assembly.stacks.length === 0) {
// If we don't check here, a more puzzling "stage contains no actions"
// error will be thrown come deployment time.
throw new Error(`The given Stage construct ('${stage.node.path}') should contain at least one Stack`);
}
const stepFromArtifact = new Map<CloudFormationStackArtifact, StackDeployment>();
for (const artifact of assembly.stacks) {
const step = StackDeployment.fromArtifact(artifact);
stepFromArtifact.set(artifact, step);
}
if (props.stackSteps) {
for (const stackstep of props.stackSteps) {
const stackArtifact = assembly.getStackArtifact(stackstep.stack.artifactId);
const thisStep = stepFromArtifact.get(stackArtifact);
if (!thisStep) {
throw new Error('Logic error: we just added a step for this artifact but it disappeared.');
}
thisStep.addStackSteps(stackstep.pre ?? [], stackstep.changeSet ?? [], stackstep.post ?? []);
}
}
for (const artifact of assembly.stacks) {
const thisStep = stepFromArtifact.get(artifact);
if (!thisStep) {
throw new Error('Logic error: we just added a step for this artifact but it disappeared.');
}
const stackDependencies = artifact.dependencies.filter(isStackArtifact);
for (const dep of stackDependencies) {
const depStep = stepFromArtifact.get(dep);
if (!depStep) {
throw new Error(`Stack '${artifact.id}' depends on stack not found in same Stage: '${dep.id}'`);
}
thisStep.addStackDependency(depStep);
}
}
return new StageDeployment(Array.from(stepFromArtifact.values()), {
stageName: stage.stageName,
...props,
});
}
/**
* The display name of this stage
*/
public readonly stageName: string;
/**
* Additional steps that are run before any of the stacks in the stage
*/
public readonly pre: Step[];
/**
* Additional steps that are run after all of the stacks in the stage
*/
public readonly post: Step[];
/**
* Instructions for additional steps that are run at stack level
*/
public readonly stackSteps: StackSteps[];
private constructor(
/** The stacks deployed in this stage */
public readonly stacks: StackDeployment[], props: StageDeploymentProps = {}) {
this.stageName = props.stageName ?? '';
this.pre = props.pre ?? [];
this.post = props.post ?? [];
this.stackSteps = props.stackSteps ?? [];
}
/**
* Add an additional step to run before any of the stacks in this stage
*/
public addPre(...steps: Step[]) {
this.pre.push(...steps);
}
/**
* Add an additional step to run after all of the stacks in this stage
*/
public addPost(...steps: Step[]) {
this.post.push(...steps);
}
}