-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathgithubHooks.js
More file actions
254 lines (227 loc) · 8.34 KB
/
Copy pathgithubHooks.js
File metadata and controls
254 lines (227 loc) · 8.34 KB
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
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
import config from "../lib/config-loader.js";
import Promise from "bluebird";
import debug from "../lib/debug.js";
import Pull from "../models/pull.js";
import Signature from "../models/signature.js";
import Comment from "../models/comment.js";
import Review from "../models/review.js";
import Status from "../models/status.js";
import Label from "../models/label.js";
import refresh from "../lib/refresh.js";
import getLogin from "../lib/get-user-login.js";
import utils from "../lib/utils.js";
import dbManager from "../lib/db-manager.js";
const HooksController = {
main: function (req, res) {
// Variable for promise that will resolve when the hook is known to have
// succeeded or failed.
var dbUpdated;
var comment;
var secret = req.query.secret;
if (secret !== config.github.hook_secret) {
var m = "Invalid Hook Secret: " + secret;
debug(m);
console.error(m);
return res.status(401).send("Invalid POST");
}
// Begin the webhook decoding
var body;
if (req.headers["content-type"] === "application/json") {
body = req.body;
} else if (
req.headers["content-type"] === "application/x-www-form-urlencoded"
) {
body = JSON.parse(req.body.payload);
} else {
return res
.status(400)
.send("Invalid content-type " + req.headers["content-type"]);
}
var event = req.get("X-GitHub-Event");
debug("Received GitHub webhook, Event: %s, Action: %s", event, body.action);
if (event === "status") {
const updatedStatus = new Status({
repo: body.name,
sha: body.sha,
state: body.state,
context: body.context,
description: body.description,
target_url: body.target_url,
started_at: body.created_at,
completed_at: body.state == "pending" ? null : body.updated_at,
});
dbUpdated = dbManager.updateCommitStatus(updatedStatus);
} else if (event === "issues") {
dbUpdated = handleIssueEvent(body);
} else if (event === "pull_request") {
// Promise that resolves when everything that needs to be done before
// we call `updatePull` has finished.
var preUpdate = handleLabelEvents(body);
switch (body.action) {
case "opened":
case "reopened":
case "closed":
case "edited":
case "merged":
break;
case "synchronize":
preUpdate = dbManager.invalidateSignatures(
body.repository.full_name,
body.pull_request.number,
["QA", "CR"]
);
}
// Update DB with new pull request content.
dbUpdated = preUpdate.then(function () {
return dbManager.updatePull(Pull.fromGithubApi(body.pull_request));
});
} else if (event === "issue_comment") {
if (body.action === "created") {
var promises = [];
// Parse any signature(s) out of the comment.
var sigs = Signature.parseComment(
body.comment,
body.repository.full_name,
body.issue.number
);
promises.push(dbManager.insertSignatures(sigs));
body.comment.number = body.issue.number;
body.comment.repo = body.repository.full_name;
body.comment.type = "issue";
comment = new Comment(body.comment);
promises.push(dbManager.updateComment(comment));
dbUpdated = Promise.all(promises);
} else {
// If the comment was edited or deleted, the easiest way to deal
// with the result is to simply refresh all data for the pull (or
// issue). Otherwise, we'd have to delete or update the comment,
// delete or update any signatures tied to that comment, then
// delete all signatures and re-insert in order them so the
// dev_blocking and such comes out correct.
refreshPullOrIssue(body);
}
} else if (event === "pull_request_review") {
if (body.action === "submitted") {
promises = [];
// Parse any signature(s) out of the comment.
sigs = Signature.parseReview(
body.review,
body.repository.full_name,
body.pull_request.number
);
promises.push(dbManager.insertSignatures(sigs));
body.review.number = body.pull_request.number;
body.review.repo = body.repository.full_name;
const review = new Review(body.review);
promises.push(dbManager.updateReview(review));
dbUpdated = Promise.all(promises);
} else {
// If the review was edited or deleted, the easiest way to deal
// with the result is to simply refresh all data for the pull.
// Otherwise, we'd have to delete or update the review, delete or
// update any signatures tied to that review, then delete all
// signatures and re-insert in order them so the dev_blocking and
// such comes out correct.
refreshPullOrIssue(body);
}
} else if (event === "pull_request_review_comment") {
if (body.action === "deleted") {
dbUpdated = dbManager.deleteReviewComment(
body.repository.full_name,
body.comment.id
);
} else {
body.comment.number = body.pull_request.number;
body.comment.repo = body.repository.full_name;
body.comment.type = "review";
comment = new Comment(body.comment);
dbUpdated = dbManager.updateComment(comment);
}
} else if (event === "check_run") {
const conclusion = body.check_run.conclusion || body.check_run.status;
const state = utils.mapCheckToStatus(conclusion);
const status = new Status({
repo: body.repository.full_name,
sha: body.check_run.head_sha,
state: state,
context: body.check_run.name,
description: conclusion,
target_url: body.check_run.html_url,
started_at: body.check_run.started_at,
completed_at: body.check_run.completed_at,
});
dbUpdated = dbManager.updateCommitStatus(status);
}
if (dbUpdated) {
dbUpdated
.then(
function fulfilled() {
res.status(200).send("Success!");
},
function rejected(err) {
console.log(err);
res.status(500).send(err.toString());
}
)
.done();
} else {
res.status(200).send("Success!");
}
},
};
function handleIssueEvent(body) {
debug("Webhook action: %s for issue #%s", body.action, body.issue.number);
var doneHandling = handleLabelEvents(body);
// Always refresh from the API rather than upserting the webhook body
// directly. The body is just a subset of the fields (and for pull requests,
// not even an issue), and it carries no events, which we need to attribute
// labels and to date the current assignment (date_assigned).
// refreshPullOrIssue dispatches pull-vs-issue from the body itself.
return doneHandling.then(function () {
// Not returning here cause we don't want to delay replying to the
// hook with a 200 since we know what needs to be done.
refreshPullOrIssue(body);
});
}
/**
* Handles the response body if it represents a labeled / unlabled issue
* (or pull) event and returns a promise that is fulfilled when the DB change
* is committed.
*
* Note: will return a fulfilled promise if this is not a label event.
*/
function handleLabelEvents(body) {
var object = body.pull_request || body.issue;
switch (body.action) {
case "labeled":
debug("Added label: %s", body.label.name);
return dbManager.insertLabel(
new Label(
body.label,
object.number,
body.repository.full_name,
getLogin(body.sender),
object.updated_at
)
);
case "unlabeled":
debug("Removed label: %s", body.label.name);
return dbManager.deleteLabel(
new Label(body.label, object.number, body.repository.full_name)
);
}
return Promise.resolve();
}
function refreshPullOrIssue(responseBody) {
var repo = responseBody.repository.full_name;
// The Docs: https://developer.github.com/v3/issues/#list-issues say you can
// tell the difference like this:
if (responseBody.pull_request) {
refresh.pull(repo, responseBody.pull_request.number);
} else if (responseBody.issue.pull_request) {
refresh.pull(repo, responseBody.issue.number);
} else {
refresh.issue(repo, responseBody.issue.number);
}
}
export default HooksController;