Skip to content

Commit 1d05665

Browse files
authored
Fixing review state to APPROVED whe 'LGTM' in COMMENTED review (#72)
* Fixing review state to APPROVED whe 'LGTM' in COMMENTED review * Add tests cases and comments flag * Update README.md for new option
1 parent 744a34d commit 1d05665

12 files changed

+103
-27
lines changed

README.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,12 @@ get-metadata <identifier>
5252
Retrieves metadata for a PR and validates them against nodejs/node PR rules
5353
5454
Options:
55-
--version Show version number [boolean]
56-
--owner, -o GitHub owner of the PR repository [string]
57-
--repo, -r GitHub repository of the PR [string]
58-
--file, -f File to write the metadata in [string]
59-
--help, -h Show help [boolean]
55+
--version Show version number [boolean]
56+
--owner, -o GitHub owner of the PR repository [string]
57+
--repo, -r GitHub repository of the PR [string]
58+
--file, -f File to write the metadata in [string]
59+
--check-comments Check for 'LGTM' in comments [boolean]
60+
--help, -h Show help [boolean]
6061
```
6162

6263
Examples:

lib/args.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ function buildYargs(args = null) {
3737
describe: 'File to write the metadata in',
3838
type: 'string'
3939
})
40+
.option('check-comments', {
41+
demandOption: false,
42+
describe: 'Check for \'LGTM\' in comments',
43+
type: 'boolean'
44+
})
4045
.help()
4146
.alias('help', 'h')
4247
.argv;
@@ -47,8 +52,10 @@ const PR_RE = new RegExp(
4752
'([0-9]+)(?:/(?:files)?)?$');
4853

4954
function checkAndParseArgs(args) {
50-
const { owner = 'nodejs', repo = 'node', identifier, file } = args;
51-
const result = { owner, repo, file };
55+
const {
56+
owner = 'nodejs', repo = 'node', identifier, file, checkComments
57+
} = args;
58+
const result = { owner, repo, file, checkComments };
5259
if (!isNaN(identifier)) {
5360
result.prid = +identifier;
5461
} else {

lib/pr_checker.js

+12-9
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const WEEKDAY_WAIT = 48;
1111
const WEEKEND_WAIT = 72;
1212

1313
const {
14-
REVIEW_SOURCES: { FROM_COMMENT }
14+
REVIEW_SOURCES: { FROM_COMMENT, FROM_REVIEW_COMMENT }
1515
} = require('./reviews');
1616
const {
1717
FIRST_TIME_CONTRIBUTOR, FIRST_TIMER
@@ -45,9 +45,9 @@ class PRChecker {
4545
);
4646
}
4747

48-
checkAll() {
48+
checkAll(comments = false) {
4949
const status = [
50-
this.checkReviews(),
50+
this.checkReviews(comments),
5151
this.checkCommitsAfterReview(),
5252
this.checkPRWait(new Date()),
5353
this.checkCI()
@@ -75,7 +75,7 @@ class PRChecker {
7575
return hint;
7676
}
7777

78-
checkReviews() {
78+
checkReviews(comments = false) {
7979
const {
8080
pr, logger, reviewers: { rejected, approved }
8181
} = this;
@@ -88,7 +88,7 @@ class PRChecker {
8888
let hint = this.getTSCHint(rejected);
8989
logger.warn(`Rejections: ${rejected.length}${hint}`);
9090
for (const { reviewer, review } of rejected) {
91-
logger.warn(`${reviewer.getName()}) rejected in ${review.ref}`);
91+
logger.warn(`${reviewer.getName()} rejected in ${review.ref}`);
9292
}
9393
}
9494
if (approved.length === 0) {
@@ -98,10 +98,13 @@ class PRChecker {
9898
let hint = this.getTSCHint(approved);
9999
logger.info(`Approvals: ${approved.length}${hint}`);
100100

101-
for (const { reviewer, review } of approved) {
102-
if (review.source === FROM_COMMENT) {
103-
logger.info(
104-
`${reviewer.getName()}) approved in via LGTM in comments`);
101+
if (comments) {
102+
for (const {reviewer, review} of approved) {
103+
if (review.source === FROM_COMMENT ||
104+
review.source === FROM_REVIEW_COMMENT) {
105+
logger.warn(
106+
`${reviewer.getName()} approved in via LGTM in comments`);
107+
}
105108
}
106109
}
107110

lib/reviews.js

+36-5
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ const {
44
} = require('./review_state');
55
const { isCollaborator } = require('./collaborators');
66
const { ascending } = require('./comp');
7-
const LGTM_RE = /(\W|^)lgtm(\W|$)/i;
7+
const LGTM_RE = /^lgtm\W?$/i;
88
const FROM_REVIEW = 'review';
99
const FROM_COMMENT = 'comment';
10+
const FROM_REVIEW_COMMENT = 'review_comment';
1011

1112
class Review {
1213
/**
@@ -55,7 +56,14 @@ class ReviewAnalyzer {
5556
const map = new Map();
5657
const collaborators = this.collaborators;
5758
const list = this.reviews
58-
.filter((r) => r.state !== PENDING && r.state !== COMMENTED)
59+
.filter((r) => r.state !== PENDING)
60+
.filter((r) => {
61+
if (r.state === COMMENTED) {
62+
return this.isApprovedInComment(r);
63+
} else {
64+
return true;
65+
}
66+
})
5967
.filter((r) => {
6068
return (isCollaborator(collaborators, r.author));
6169
}).sort((a, b) => {
@@ -80,6 +88,12 @@ class ReviewAnalyzer {
8088
new Review(r.state, r.publishedAt, r.url, FROM_REVIEW)
8189
);
8290
break;
91+
case COMMENTED:
92+
map.set(
93+
login,
94+
new Review(APPROVED, r.publishedAt, r.bodyText, FROM_REVIEW_COMMENT)
95+
);
96+
break;
8397
case DISMISSED:
8498
// TODO: check the state of the dismissed review?
8599
map.delete(login);
@@ -97,7 +111,7 @@ class ReviewAnalyzer {
97111
updateMapByRawReviews(oldMap) {
98112
const comments = this.comments;
99113
const collaborators = this.collaborators;
100-
const withLgtm = comments.filter((c) => LGTM_RE.test(c.bodyText))
114+
const withLgtm = comments.filter((c) => this.hasLGTM(c))
101115
.filter((c) => {
102116
return (isCollaborator(collaborators, c.author));
103117
}).sort((a, b) => {
@@ -133,17 +147,34 @@ class ReviewAnalyzer {
133147
for (const [ login, review ] of reviewers) {
134148
const reviewer = collaborators.get(login.toLowerCase());
135149
if (review.state === APPROVED) {
136-
result.approved.push({ reviewer, review });
150+
result.approved.push({reviewer, review});
137151
} else if (review.state === CHANGES_REQUESTED) {
138152
result.rejected.push({ reviewer, review });
139153
}
140154
}
141155
return result;
142156
}
157+
158+
/**
159+
* @param review
160+
* @returns {boolean}
161+
*/
162+
isApprovedInComment(review) {
163+
return review.state === COMMENTED && this.hasLGTM(review);
164+
}
165+
166+
/**
167+
* @param object
168+
* @param prop: string
169+
* @returns {boolean}
170+
*/
171+
hasLGTM(object) {
172+
return LGTM_RE.test(object.bodyText.trim());
173+
}
143174
}
144175

145176
const REVIEW_SOURCES = {
146-
FROM_COMMENT, FROM_REVIEW
177+
FROM_COMMENT, FROM_REVIEW, FROM_REVIEW_COMMENT
147178
};
148179

149180
module.exports = {

steps/metadata.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ module.exports = async function getMetadata(argv, logger) {
3434
}, 'Generated metadata:');
3535

3636
const checker = new PRChecker(logger, data);
37-
const status = checker.checkAll();
37+
const status = checker.checkAll(argv.checkComments);
3838
return {
3939
status,
4040
request,

test/fixtures/README/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,8 @@ For more information about the governance of the Node.js project, see
252252
**Foo User** &lt;[email protected]&gt; (she/her)
253253
* [Quo](https://github.com/quo) -
254254
**Quo User** &lt;[email protected]&gt; (she/her)
255+
* [Quux](https://github.com/quux) -
256+
**Quux User** &lt;[email protected]&gt; (he/him)
255257

256258
### Collaborator Emeriti
257259

test/fixtures/collaborators.json

+6
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,11 @@
2222
"name": "Quo User",
2323
"email": "[email protected]",
2424
"type": "COLLABORATOR"
25+
},
26+
{
27+
"login": "Quux",
28+
"name": "Quux User",
29+
"email": "[email protected]",
30+
"type": "COLLABORATOR"
2531
}
2632
]

test/fixtures/reviewers_approved.json

+14
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,20 @@
1313
"source": "review"
1414
}
1515
},
16+
{
17+
"reviewer": {
18+
"login": "Quux",
19+
"name": "Quux User",
20+
"email": "[email protected]",
21+
"type": "COLLABORATOR"
22+
},
23+
"review": {
24+
"state": "APPROVED",
25+
"date": "2017-10-24T14:49:52Z",
26+
"ref": "LGTM",
27+
"source": "review_comment"
28+
}
29+
},
1630
{
1731
"reviewer": {
1832
"login": "Baz",

test/fixtures/reviews_approved.json

+9
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@
3535
"url": "https://github.com/nodejs/node/pull/16438#pullrequestreview-71488236",
3636
"publishedAt": "2017-10-24T14:49:52Z"
3737
},
38+
{
39+
"bodyText": "LGTM",
40+
"state": "COMMENTED",
41+
"author": {
42+
"login": "Quux"
43+
},
44+
"url": "https://github.com/nodejs/node/pull/16438#pullrequestreview-71817236",
45+
"publishedAt": "2017-10-24T14:49:52Z"
46+
},
3847
{
3948
"bodyText": "A few nits",
4049
"state": "COMMENTED",

test/unit/args.test.js

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const parseArgs = require('../../lib/args');
44
const assert = require('assert');
55

66
const expected = {
7+
checkComments: false,
78
owner: `nodejs`,
89
repo: `node`,
910
prid: 16637,

test/unit/metadata_gen.test.js

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const expected = `PR-URL: https://github.com/nodejs/node/pull/16438
1919
Fixes: https://github.com/nodejs/node/issues/16437
2020
Refs: https://github.com/nodejs/node/pull/15148
2121
Reviewed-By: Foo User <[email protected]>
22+
Reviewed-By: Quux User <[email protected]>
2223
Reviewed-By: Baz User <[email protected]>
2324
Reviewed-By: Bar User <[email protected]>
2425
`;

test/unit/pr_checker.test.js

+6-5
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,13 @@ describe('PRChecker', () => {
8080

8181
const expectedLogs = {
8282
warn: [
83+
['Quux User(Quux) approved in via LGTM in comments'],
84+
['Bar User(bar) approved in via LGTM in comments'],
8385
['semver-major requires at least two TSC approvals']
8486
],
8587
info: [
8688
['Rejections: 0'],
87-
['Approvals: 3, 1 from TSC (bar)'],
88-
['Bar User(bar)) approved in via LGTM in comments']
89+
['Approvals: 4, 1 from TSC (bar)']
8990
],
9091
error: [],
9192
trace: []
@@ -100,7 +101,7 @@ describe('PRChecker', () => {
100101
collaborators
101102
});
102103

103-
const status = checker.checkReviews();
104+
const status = checker.checkReviews(true);
104105
assert(!status);
105106
assert.deepStrictEqual(logger.logs, expectedLogs);
106107
});
@@ -111,8 +112,8 @@ describe('PRChecker', () => {
111112
const expectedLogs = {
112113
warn: [
113114
['Rejections: 2, 1 from TSC (bar)'],
114-
['Foo User(foo)) rejected in https://github.com/nodejs/node/pull/16438#pullrequestreview-71480624'],
115-
['Bar User(bar)) rejected in https://github.com/nodejs/node/pull/16438#pullrequestreview-71482624'],
115+
['Foo User(foo) rejected in https://github.com/nodejs/node/pull/16438#pullrequestreview-71480624'],
116+
['Bar User(bar) rejected in https://github.com/nodejs/node/pull/16438#pullrequestreview-71482624'],
116117
['Approvals: 0']
117118
],
118119
info: [],

0 commit comments

Comments
 (0)