Skip to content

Commit b080865

Browse files
committed
CodeCommit PR tooling
1 parent 86fc1a9 commit b080865

File tree

4 files changed

+204
-1
lines changed

4 files changed

+204
-1
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Utilities and samples for building on CodeBuild
44

55
### Sample App: Simple Calculator Service
66

7-
Simple Node.js Express-based web service that demonstrates continuous integration with AWS CodeBuild and continuous deployment with AWS CodeDeploy/CodePipeline. This application was written for demo purposes only, and is definitely not production ready.
7+
Simple Node.js Express-based web service that demonstrates continuous integration with AWS CodeBuild, AWS CodeCommit, and GitHub, as well as continuous deployment with AWS CodeDeploy/CodePipeline. This application was written for demo purposes only, and is definitely not production ready.
88

99
### CI Tooling (Buildspecs)
1010

@@ -22,6 +22,7 @@ The ci_tools folder contains the following tools for use with AWS Lambda and Ama
2222
* trigger_codebuild.js: Lambda function to start a CodeBuild build.
2323
* slack_notifications.js: Lambda function to post CodeBuild build notifications into a Slack channel.
2424
* email_notifications.js: Lambda function to send CodeBuild build notification emails via Amazon SES.
25+
* codecommit_pr_notifications: Lambda function to start a CodeBuild build for CodeCommit pull request notifications, and comment on a CodeCommit pull request for CodeCommit build notifications.
2526
* cwe-rule-configuration/branch_ci.json: CloudWatch Events rule pattern to start a CodeBuild build for every push to the master branch of a CodeCommit repository.
2627
* cwe-rule-configuration/slack_event_pattern.json: CloudWatch Events rule pattern to notify Slack for failed CodeBuild builds.
2728
* cwe-rule-configuration/nightly_build_input.json: CloudWatch Events target input to start a CodeBuild build with a specific buildspec override and project name.
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
'use strict';
2+
3+
const AWS = require('aws-sdk');
4+
const codebuild = new AWS.CodeBuild();
5+
const codecommit = new AWS.CodeCommit();
6+
const cloudwatchlogs = new AWS.CloudWatchLogs();
7+
const defaultCodebuildProject = process.env.projectName;
8+
9+
function startBuild(params, callback) {
10+
codebuild.startBuild(params, (error, data) => {
11+
if (error) {
12+
console.log(error, error.stack);
13+
return callback(error);
14+
}
15+
16+
console.log(data);
17+
return callback(null, data);
18+
});
19+
}
20+
21+
function postComment(params, callback) {
22+
console.log(params.content);
23+
codecommit.postCommentForPullRequest(params, (error, data) => {
24+
if (error) {
25+
console.log(error, error.stack);
26+
return callback(error);
27+
}
28+
29+
console.log(data);
30+
return callback(null, data);
31+
});
32+
}
33+
34+
function handlePullRequestEvent(event, callback) {
35+
// Start a build if this is a notification about a pull request update
36+
const eventType = event.detail.event;
37+
if (eventType == "pullRequestCreated" || eventType == "pullRequestSourceBranchUpdated") {
38+
const pullRequestId = event.detail.pullRequestId;
39+
// TODO Replace this line with your own logic for how to map CodeCommit repos to CodeBuild projects
40+
const projectName = defaultCodebuildProject;
41+
42+
var params = {
43+
projectName: projectName,
44+
sourceVersion: event.detail.sourceCommit,
45+
// We will use the pull request env variables later to post a comment about the build to the PR
46+
environmentVariablesOverride: [
47+
{
48+
name: "CODECOMMIT_PULL_REQUEST_ID",
49+
value: event.detail.pullRequestId,
50+
type: "PLAINTEXT"
51+
},
52+
{
53+
name: "CODECOMMIT_PULL_REQUEST_SRC_COMMIT",
54+
value: event.detail.sourceCommit,
55+
type: "PLAINTEXT"
56+
},
57+
{
58+
name: "CODECOMMIT_PULL_REQUEST_DST_COMMIT",
59+
value: event.detail.destinationCommit,
60+
type: "PLAINTEXT"
61+
}
62+
]
63+
};
64+
startBuild(params, callback);
65+
} else {
66+
callback("Not a buildable pull request update");
67+
}
68+
}
69+
70+
function handleBuildEvent(event, callback) {
71+
// Parse a CodeBuild event notification,
72+
// and post a comment to a CodeCommit pull request
73+
// about the status of the build
74+
75+
// Check that this build was triggered from a CodeCommit pull request
76+
const buildVariables = event.detail['additional-information'].environment['environment-variables'];
77+
const pullRequestIdEnvVar = buildVariables.find(function(buildEnvVar) {
78+
return buildEnvVar.name == "CODECOMMIT_PULL_REQUEST_ID";
79+
});
80+
const sourceCommitEnvVar = buildVariables.find(function(buildEnvVar) {
81+
return buildEnvVar.name == "CODECOMMIT_PULL_REQUEST_SRC_COMMIT";
82+
});
83+
const destinationCommitEnvVar = buildVariables.find(function(buildEnvVar) {
84+
return buildEnvVar.name == "CODECOMMIT_PULL_REQUEST_DST_COMMIT";
85+
});
86+
87+
if (pullRequestIdEnvVar && sourceCommitEnvVar && destinationCommitEnvVar) {
88+
const buildArn = event.detail['build-id'];
89+
const buildId = buildArn.split('/').pop();
90+
const buildUuid = buildId.split(':').pop();
91+
const sourceUrl = event.detail['additional-information'].source.location;
92+
const repoName = sourceUrl.split('/').pop();
93+
const projectName = event.detail['project-name'];
94+
const buildStatus = event.detail['build-status'];
95+
const region = event.region;
96+
// Only comment once per build and build status
97+
const requestToken = buildArn + buildStatus;
98+
99+
// Construct a comment based on build status
100+
var comment = `Build ${buildUuid} for project ${projectName} `;
101+
102+
switch (buildStatus) {
103+
case 'IN_PROGRESS':
104+
comment += "is **in progress**.";
105+
break;
106+
case 'SUCCEEDED':
107+
comment += "**succeeded!**";
108+
break;
109+
case 'STOPPED':
110+
comment += "was **canceled**.";
111+
break;
112+
case 'TIMED_OUT':
113+
comment += "**timed out**.";
114+
break;
115+
default:
116+
comment += "**failed**."
117+
}
118+
119+
comment += ` Visit the [AWS CodeBuild console](https:\/\/${region}.console.aws.amazon.com\/codebuild\/home?` +
120+
`region=${region}#\/builds\/${encodeURI(buildId)}\/view\/new) to view the build details.`;
121+
122+
var pullRequestParams = {
123+
repositoryName: repoName,
124+
pullRequestId: pullRequestIdEnvVar.value,
125+
beforeCommitId: destinationCommitEnvVar.value,
126+
afterCommitId: sourceCommitEnvVar.value,
127+
content: comment,
128+
clientRequestToken: requestToken
129+
};
130+
131+
// Add build logs snippet to the comment
132+
if (buildStatus != 'IN_PROGRESS' &&
133+
buildStatus != 'SUCCEEDED' &&
134+
buildStatus != 'STOPPED' &&
135+
event.detail['additional-information'].logs &&
136+
event.detail['additional-information'].logs['stream-name'] &&
137+
event.detail['additional-information'].logs['group-name']) {
138+
139+
var logParams = {
140+
logGroupName: event.detail['additional-information'].logs['group-name'],
141+
logStreamName: event.detail['additional-information'].logs['stream-name'],
142+
limit: 30,
143+
startFromHead: false
144+
};
145+
cloudwatchlogs.getLogEvents(logParams, function(err, data) {
146+
if (err) {
147+
// an error occurred, ignore and post the comment without the logs
148+
console.log(err, err.stack);
149+
} else {
150+
var logLines = data.events.map(function(event) {
151+
return event.message;
152+
}).join("");
153+
pullRequestParams.content += "\n```\n" + logLines + "\n```\n";
154+
}
155+
156+
postComment(pullRequestParams, callback);
157+
});
158+
} else {
159+
postComment(pullRequestParams, callback);
160+
}
161+
} else {
162+
callback("Not a pull-request build");
163+
}
164+
}
165+
166+
exports.handler = (event, context, callback) => {
167+
try {
168+
console.log(event);
169+
170+
if (event.source && event.source == "aws.codebuild" &&
171+
event['detail-type'] == "CodeBuild Build State Change") {
172+
handleBuildEvent(event, callback);
173+
} else if (event.source && event.source == "aws.codecommit" &&
174+
event['detail-type'] == "CodeCommit Pull Request State Change") {
175+
handlePullRequestEvent(event, callback);
176+
} else {
177+
callback("Not a pull-request-related CodeCommit or CodeBuild event");
178+
}
179+
} catch (error) {
180+
console.log('Caught Error: ', error);
181+
callback(error);
182+
}
183+
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "codecommit-pull-request-bot",
3+
"version": "1.0.0",
4+
"main": "codecommit_pr_notifications.js",
5+
"dependencies": {
6+
"aws-sdk": "^2.154.0"
7+
},
8+
"private": "true"
9+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"source": [
3+
"aws.codecommit",
4+
"aws.codebuild"
5+
],
6+
"detail-type": [
7+
"CodeCommit Pull Request State Change",
8+
"CodeBuild Build State Change"
9+
]
10+
}

0 commit comments

Comments
 (0)