forked from iFixit/pulldasher
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgithubHooks.js
More file actions
235 lines (207 loc) · 8.08 KB
/
githubHooks.js
File metadata and controls
235 lines (207 loc) · 8.08 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
var config = require('../config'),
Promise = require('promise'),
debug = require('../lib/debug')('pulldasher:githubHooks'),
Pull = require('../models/pull'),
Status = require('../models/status'),
Signature = require('../models/signature'),
Issue = require('../models/issue'),
Comment = require('../models/comment'),
Label = require('../models/label'),
refresh = require('../lib/refresh'),
getLogin = require('../lib/get-user-login'),
dbManager = require('../lib/db-manager');
var 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') {
// The payload for statuses looks different from the api response.
// Here we somewhat normalize this by making the repo available on
// the 'repo' property.
body.repo = body.name;
dbUpdated = dbManager.updateCommitStatus(new Status(body));
} 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_name = body.repository.name;
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_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_name = body.repository.name;
body.comment.repo = body.repository.full_name;
body.comment.type = 'review';
comment = new Comment(body.comment);
dbUpdated = dbManager.updateComment(comment);
}
}
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);
// Some of these events are also triggered on pull requests
var isPull = body.issue.pull_request;
var doneHandling = handleLabelEvents(body);
switch(body.action) {
case "opened":
// Always do this for opened issues because a full refresh
// is the easiest way to get *who* assigned the initial labels.
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);
});
case "reopened":
case "closed":
case "edited":
case "assigned":
case "unassigned":
// Default case is update the issue
}
// If github is fooling us and this is actually a pull request,
// let's not create an issue object out of it.
if (isPull) {
return doneHandling.then(function() {
return dbManager.updatePull(Pull.fromGithubApi(body.pull_request));
});
}
return doneHandling.then(function() {
// Copy the full name of the repo into the issue object so we can
// normalize the structure.
body.issue.repo = body.repository.full_name;
return Issue.getFromGH(body.issue);
})
.then(dbManager.updateIssue)
.then(function() {
return reprocessLabels(body.repository.full_name, body.issue.number);
});
}
/**
* 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.name,
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.name,
body.repository.full_name
));
}
return Promise.resolve();
}
/**
* After a label has been added or removed we have to re-process all the labels
* in case one of them matches one of our configured label updaters.
*/
function reprocessLabels(repo, issueNumber) {
if (!config.labels || !config.labels.length) {
return;
}
debug("Reprocessing labels for Issue #%s in repo %s", issueNumber, repo);
return dbManager.getIssue(repo, issueNumber)
.then(dbManager.updateIssue);
}
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.issue.pull_request) {
refresh.pull(repo, responseBody.issue.number);
} else {
refresh.issue(repo, responseBody.issue.number);
}
}
module.exports = HooksController;