-
Notifications
You must be signed in to change notification settings - Fork 4k
/
Copy pathbuild-action.ts
235 lines (212 loc) · 8.53 KB
/
build-action.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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
import { Construct } from 'constructs';
import { CodeStarConnectionsSourceAction } from '..';
import * as codebuild from '../../../aws-codebuild';
import * as codepipeline from '../../../aws-codepipeline';
import * as iam from '../../../aws-iam';
import * as cdk from '../../../core';
import { Action } from '../action';
import { CodeCommitSourceAction } from '../codecommit/source-action';
/**
* The type of the CodeBuild action that determines its CodePipeline Category -
* Build, or Test.
* The default is Build.
*/
export enum CodeBuildActionType {
/**
* The action will have the Build Category.
* This is the default.
*/
BUILD,
/**
* The action will have the Test Category.
*/
TEST,
}
/**
* Construction properties of the `CodeBuildAction CodeBuild build CodePipeline action`.
*/
export interface CodeBuildActionProps extends codepipeline.CommonAwsActionProps {
/**
* The source to use as input for this action.
*/
readonly input: codepipeline.Artifact;
/**
* The list of additional input Artifacts for this action.
*
* The directories the additional inputs will be available at are available
* during the project's build in the CODEBUILD_SRC_DIR_<artifact-name> environment variables.
* The project's build always starts in the directory with the primary input artifact checked out,
* the one pointed to by the `input` property.
* For more information,
* see https://docs.aws.amazon.com/codebuild/latest/userguide/sample-multi-in-out.html .
*/
readonly extraInputs?: codepipeline.Artifact[];
/**
* The list of output Artifacts for this action.
* **Note**: if you specify more than one output Artifact here,
* you cannot use the primary 'artifacts' section of the buildspec;
* you have to use the 'secondary-artifacts' section instead.
* See https://docs.aws.amazon.com/codebuild/latest/userguide/sample-multi-in-out.html
* for details.
*
* @default the action will not have any outputs
*/
readonly outputs?: codepipeline.Artifact[];
/**
* The action's Project.
*/
readonly project: codebuild.IProject;
/**
* The type of the action that determines its CodePipeline Category -
* Build, or Test.
*
* @default CodeBuildActionType.BUILD
*/
readonly type?: CodeBuildActionType;
/**
* The environment variables to pass to the CodeBuild project when this action executes.
* If a variable with the same name was set both on the project level, and here,
* this value will take precedence.
*
* @default - No additional environment variables are specified.
*/
readonly environmentVariables?: { [name: string]: codebuild.BuildEnvironmentVariable };
/**
* Whether to check for the presence of any secrets in the environment variables of the default type, BuildEnvironmentVariableType.PLAINTEXT.
* Since using a secret for the value of that kind of variable would result in it being displayed in plain text in the AWS Console,
* the construct will throw an exception if it detects a secret was passed there.
* Pass this property as false if you want to skip this validation,
* and keep using a secret in a plain text environment variable.
*
* @default true
*/
readonly checkSecretsInPlainTextEnvVariables?: boolean;
/**
* Trigger a batch build.
*
* Enabling this will enable batch builds on the CodeBuild project.
*
* @default false
*/
readonly executeBatchBuild?: boolean;
/**
* Combine the build artifacts for a batch builds.
*
* Enabling this will combine the build artifacts into the same location for batch builds.
* If `executeBatchBuild` is not set to `true`, this property is ignored.
*
* @default false
*/
readonly combineBatchBuildArtifacts?: boolean;
}
/**
* CodePipeline build action that uses AWS CodeBuild.
*/
export class CodeBuildAction extends Action {
private readonly props: CodeBuildActionProps;
constructor(props: CodeBuildActionProps) {
super({
...props,
category: props.type === CodeBuildActionType.TEST
? codepipeline.ActionCategory.TEST
: codepipeline.ActionCategory.BUILD,
provider: 'CodeBuild',
artifactBounds: { minInputs: 1, maxInputs: 5, minOutputs: 0, maxOutputs: 5 },
inputs: [props.input, ...props.extraInputs || []],
resource: props.project,
});
this.props = props;
}
/**
* Reference a CodePipeline variable defined by the CodeBuild project this action points to.
* Variables in CodeBuild actions are defined using the 'exported-variables' subsection of the 'env'
* section of the buildspec.
*
* @param variableName the name of the variable to reference.
* A variable by this name must be present in the 'exported-variables' section of the buildspec
*
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html#build-spec-ref-syntax
*/
public variable(variableName: string): string {
return this.variableExpression(variableName);
}
protected bound(scope: Construct, _stage: codepipeline.IStage, options: codepipeline.ActionBindOptions):
codepipeline.ActionConfig {
// check for a cross-account action if there are any outputs
if ((this.actionProperties.outputs || []).length > 0) {
const pipelineStack = cdk.Stack.of(scope);
const projectStack = cdk.Stack.of(this.props.project);
if (pipelineStack.account !== projectStack.account) {
throw new Error('A cross-account CodeBuild action cannot have outputs. ' +
'This is a known CodeBuild limitation. ' +
'See https://github.com/aws/aws-cdk/issues/4169 for details');
}
}
// grant the Pipeline role the required permissions to this Project
options.role.addToPolicy(new iam.PolicyStatement({
resources: [this.props.project.projectArn],
actions: [
`codebuild:${this.props.executeBatchBuild ? 'BatchGetBuildBatches' : 'BatchGetBuilds'}`,
`codebuild:${this.props.executeBatchBuild ? 'StartBuildBatch' : 'StartBuild'}`,
`codebuild:${this.props.executeBatchBuild ? 'StopBuildBatch' : 'StopBuild'}`,
],
}));
// allow the Project access to the Pipeline's artifact Bucket
// but only if the project is not imported
// (ie., has a role) - otherwise, the IAM library throws an error
if (this.props.project.role) {
if ((this.actionProperties.outputs || []).length > 0) {
options.bucket.grantReadWrite(this.props.project);
} else {
options.bucket.grantRead(this.props.project);
}
}
if (this.props.project instanceof codebuild.Project) {
this.props.project.bindToCodePipeline(scope, {
artifactBucket: options.bucket,
});
}
for (const inputArtifact of this.actionProperties.inputs || []) {
// if any of the inputs come from the CodeStarConnectionsSourceAction
// with codeBuildCloneOutput=true,
// grant the Project's Role to use the connection
const connectionArn = inputArtifact.getMetadata(CodeStarConnectionsSourceAction._CONNECTION_ARN_PROPERTY);
if (connectionArn) {
this.props.project.addToRolePolicy(new iam.PolicyStatement({
actions: ['codestar-connections:UseConnection'],
resources: [connectionArn],
}));
}
// if any of the inputs come from the CodeCommitSourceAction
// with codeBuildCloneOutput=true,
// grant the Project's Role git pull access to the repository
const codecommitRepositoryArn = inputArtifact.getMetadata(CodeCommitSourceAction._FULL_CLONE_ARN_PROPERTY);
if (codecommitRepositoryArn) {
this.props.project.addToRolePolicy(new iam.PolicyStatement({
actions: ['codecommit:GitPull'],
resources: [codecommitRepositoryArn],
}));
}
}
const configuration: any = {
ProjectName: this.props.project.projectName,
EnvironmentVariables: this.props.environmentVariables &&
cdk.Stack.of(scope).toJsonString(codebuild.Project.serializeEnvVariables(this.props.environmentVariables,
this.props.checkSecretsInPlainTextEnvVariables ?? true, this.props.project)),
};
if ((this.actionProperties.inputs || []).length > 1) {
// lazy, because the Artifact name might be generated lazily
configuration.PrimarySource = cdk.Lazy.string({ produce: () => this.props.input.artifactName });
}
if (this.props.executeBatchBuild) {
configuration.BatchEnabled = 'true';
this.props.project.enableBatchBuilds();
if (this.props.combineBatchBuildArtifacts) {
configuration.CombineArtifacts = 'true';
}
}
return {
configuration,
};
}
}