Skip to content

Commit e829262

Browse files
priyank-pjoyeecheung
authored andcommitted
todo: warn new commits after review (nodejs#65)
warn new commits after review also added `lint-fix` command
1 parent 174aa93 commit e829262

9 files changed

Lines changed: 196 additions & 3 deletions

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Then create a file named `.ncurc` under your `$HOME` directory (`~/.ncurc`);
2727

2828
```
2929
{
30-
"username": "you_github_username"
30+
"username": "you_github_username",
3131
"token": "token_that_you_created"
3232
}
3333
```
@@ -90,7 +90,7 @@ $ git commit --amend -F msg.txt
9090
- [x] Check if commiters match authors
9191
- [x] Check 48-hour wait
9292
- [x] Check two TSC approval for semver-major
93-
- [ ] Warn new commits after reviews
93+
- [x] Warn new commits after reviews
9494
- [ ] Check number of files changed (request pre-backport)
9595

9696
### Contributing

lib/pr_checker.js

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ class PRChecker {
3434
this.reviewers = reviewers;
3535
this.pr = pr;
3636
this.comments = comments;
37+
// this.reviews and this.commits must
38+
// be in order as received from github api
39+
// to check if new commits were pushed after
40+
// the last review.
3741
this.reviews = reviews;
3842
this.commits = commits;
3943
this.collaboratorEmails = new Set(
@@ -44,14 +48,15 @@ class PRChecker {
4448
checkAll() {
4549
const status = [
4650
this.checkReviews(),
51+
this.checkCommitsAfterReview(),
4752
this.checkPRWait(new Date()),
4853
this.checkCI()
4954
];
5055

5156
if (this.authorIsNew()) {
5257
status.push(this.checkAuthor());
5358
}
54-
// TODO: maybe invalidate review after new commits?
59+
5560
// TODO: check for pre-backport, Github API v4
5661
// does not support reading files changed
5762

@@ -231,6 +236,38 @@ class PRChecker {
231236
// 3. is not authored by the people opening the PR
232237
return true;
233238
}
239+
240+
checkCommitsAfterReview() {
241+
const {
242+
commits, reviews, logger
243+
} = this;
244+
245+
if (reviews.length < 1) {
246+
return true;
247+
}
248+
249+
const commitIndex = commits.length - 1;
250+
const reviewIndex = reviews.length - 1;
251+
const lastCommit = commits[commitIndex].commit;
252+
const lastReview = reviews[reviewIndex];
253+
254+
const commitDate = lastCommit.committedDate;
255+
const reviewDate = lastReview.publishedAt;
256+
257+
let status = true;
258+
if (commitDate > reviewDate) {
259+
logger.warn('Changes were pushed since the last review:');
260+
commits.forEach((commit) => {
261+
commit = commit.commit;
262+
if (commit.committedDate > reviewDate) {
263+
status = false;
264+
logger.warn(`- ${commit.message.split('\n')[0]}`);
265+
}
266+
});
267+
}
268+
269+
return status;
270+
}
234271
}
235272

236273
module.exports = PRChecker;

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"test": "npm run test-unit && npm run lint",
1111
"test-unit": "mocha --require intelli-espower-loader test/unit/*.test.js --exit",
1212
"test-all": "mocha --require intelli-espower-loader test/**/*.test.js --exit",
13+
"lint-fix": "eslint . --fix",
1314
"coverage": "nyc --reporter=html --reporter=text --reporter=text-summary npm test",
1415
"coverage-all": "nyc --reporter=lcov --reporter=text --reporter=text-summary npm run test-all",
1516
"lint": "eslint . --cache",

test/fixtures/data.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ const simpleCommits = readJSON('simple_commits.json');
3131

3232
const collabArr = readJSON('collaborators.json');
3333

34+
const singleCommitAfterReview = {
35+
commits: readJSON('single_commit_after_review_commits.json'),
36+
reviews: readJSON('single_commit_after_review_reviews.json')
37+
};
38+
const multipleCommitsAfterReview = {
39+
commits: readJSON('multiple_commits_after_review_commits.json'),
40+
reviews: readJSON('multiple_commits_after_review_reviews.json')
41+
};
42+
3443
collabArr.forEach((c) => {
3544
Object.setPrototypeOf(c, Collaborator.prototype);
3645
});
@@ -59,6 +68,8 @@ module.exports = {
5968
commentsWithLGTM,
6069
oddCommits,
6170
simpleCommits,
71+
singleCommitAfterReview,
72+
multipleCommitsAfterReview,
6273
collaborators,
6374
firstTimerPR,
6475
semverMajorPR,
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[{
2+
"commit": {
3+
"committedDate": "2017-10-25T11:27:02Z",
4+
"oid": "f230e691459f8b0f448e1eec1b83fbf7f708eba3",
5+
"bodyText": "src: add requested feature",
6+
"message": "src: add requested feature\nkjlks jskld ksa",
7+
"author": {
8+
"login": "bar"
9+
}
10+
}
11+
},{
12+
"commit": {
13+
"committedDate": "2017-10-25T12:42:02Z",
14+
"oid": "9416475a6dc1b27c3e343dcbc07674a439c88db1",
15+
"bodyText": "nit: edit mistakes",
16+
"message": "nit: fix errors\n fixed requested errors",
17+
"author": {
18+
"login": "bar"
19+
}
20+
}
21+
}]
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[{
2+
"bodyText": "Good idea",
3+
"state": "APPROVED",
4+
"author": {
5+
"login": "foo"
6+
},
7+
"url": "https://github.com/nodejs/node/pull/16438#pullrequestreview-89923489",
8+
"publishedAt": "2017-09-23T11:19:25Z"
9+
}]
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[{
2+
"commit": {
3+
"committedDate": "2017-10-25T11:27:02Z",
4+
"oid": "6c0945cbeea2cbbc97d13a3d9e3fe68bd145b985",
5+
"bodyText": "src: fix issue with es-modules",
6+
"message": "single commit was pushed after review",
7+
"author": {
8+
"login": "bar"
9+
}
10+
}
11+
}]
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[{
2+
"bodyText": "Good idea",
3+
"state": "APPROVED",
4+
"author": {
5+
"login": "foo"
6+
},
7+
"url": "https://github.com/nodejs/node/pull/16438#pullrequestreview-71480624",
8+
"publishedAt": "2017-10-24T11:19:25Z"
9+
}]

test/unit/pr_checker.test.js

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ const {
1414
rejectingReviews,
1515
commentsWithCI,
1616
commentsWithLGTM,
17+
singleCommitAfterReview,
18+
multipleCommitsAfterReview,
1719
oddCommits,
1820
simpleCommits,
1921
collaborators,
@@ -38,13 +40,16 @@ describe('PRChecker', () => {
3840
let checkCIStub;
3941
let authorIsNewStub;
4042
let checkAuthorStub;
43+
let checkCommitsAfterReviewStub;
4144

4245
before(() => {
4346
checkReviewsStub = sinon.stub(checker, 'checkReviews');
4447
checkPRWaitStub = sinon.stub(checker, 'checkPRWait');
4548
checkCIStub = sinon.stub(checker, 'checkCI');
4649
authorIsNewStub = sinon.stub(checker, 'authorIsNew').returns(true);
4750
checkAuthorStub = sinon.stub(checker, 'checkAuthor');
51+
checkCommitsAfterReviewStub =
52+
sinon.stub(checker, 'checkCommitsAfterReview');
4853
});
4954

5055
after(() => {
@@ -53,6 +58,7 @@ describe('PRChecker', () => {
5358
checkCIStub.restore();
5459
authorIsNewStub.restore();
5560
checkAuthorStub.restore();
61+
checkCommitsAfterReviewStub.restore();
5662
});
5763

5864
it('should run necessary checks', () => {
@@ -63,6 +69,7 @@ describe('PRChecker', () => {
6369
assert.strictEqual(checkCIStub.calledOnce, true);
6470
assert.strictEqual(authorIsNewStub.calledOnce, true);
6571
assert.strictEqual(checkAuthorStub.calledOnce, true);
72+
assert.strictEqual(checkCommitsAfterReviewStub.calledOnce, true);
6673
});
6774
});
6875

@@ -300,4 +307,91 @@ describe('PRChecker', () => {
300307
assert.deepStrictEqual(logger.logs, expectedLogs);
301308
});
302309
});
310+
311+
describe('checkCommitsAfterReview', () => {
312+
let logger = new TestLogger();
313+
314+
afterEach(() => {
315+
logger.clear();
316+
});
317+
318+
it('should warn about commit pushed since the last review', () => {
319+
const { commits, reviews } = singleCommitAfterReview;
320+
321+
const expectedLogs = {
322+
warn: [
323+
[ 'Changes were pushed since the last review:' ],
324+
[ '- single commit was pushed after review' ]
325+
],
326+
info: [],
327+
trace: [],
328+
error: []
329+
};
330+
331+
const checker = new PRChecker(logger, {
332+
pr: firstTimerPR,
333+
reviewers: allGreenReviewers,
334+
comments: commentsWithLGTM,
335+
collaborators,
336+
reviews,
337+
commits
338+
});
339+
340+
let status = checker.checkCommitsAfterReview();
341+
assert.deepStrictEqual(status, false);
342+
assert.deepStrictEqual(logger.logs, expectedLogs);
343+
});
344+
345+
it('should warn about multiple commits since the last review', () => {
346+
const { commits, reviews } = multipleCommitsAfterReview;
347+
348+
const expectedLogs = {
349+
warn: [
350+
[ 'Changes were pushed since the last review:' ],
351+
[ '- src: add requested feature' ],
352+
[ '- nit: fix errors' ]
353+
],
354+
info: [],
355+
trace: [],
356+
error: []
357+
};
358+
359+
const checker = new PRChecker(logger, {
360+
pr: firstTimerPR,
361+
reviewers: allGreenReviewers,
362+
comments: commentsWithLGTM,
363+
collaborators,
364+
reviews,
365+
commits
366+
});
367+
368+
let status = checker.checkCommitsAfterReview();
369+
assert.deepStrictEqual(status, false);
370+
assert.deepStrictEqual(logger.logs, expectedLogs);
371+
});
372+
373+
it('should skip the check if there are no reviews', () => {
374+
const { commits } = multipleCommitsAfterReview;
375+
376+
const expectedLogs = {
377+
warn: [],
378+
info: [],
379+
trace: [],
380+
error: []
381+
};
382+
383+
const checker = new PRChecker(logger, {
384+
pr: firstTimerPR,
385+
reviewers: allGreenReviewers,
386+
comments: commentsWithLGTM,
387+
reviews: [],
388+
collaborators,
389+
commits
390+
});
391+
392+
let status = checker.checkCommitsAfterReview();
393+
assert.deepStrictEqual(status, true);
394+
assert.deepStrictEqual(logger.logs, expectedLogs);
395+
});
396+
});
303397
});

0 commit comments

Comments
 (0)