Skip to content

Commit 9ce8eda

Browse files
committed
fix(ci): trust PR reviewers by repo permission
1 parent 94148d5 commit 9ce8eda

1 file changed

Lines changed: 63 additions & 8 deletions

File tree

.github/workflows/pr-author-inactivity.yml

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,32 @@ jobs:
6565
return Boolean(login && login.endsWith('[bot]'));
6666
}
6767
68-
const TRUSTED_ASSOCIATIONS = new Set(['OWNER', 'MEMBER', 'COLLABORATOR']);
68+
const TRUSTED_PERMISSIONS = new Set(['admin', 'maintain', 'write', 'triage']);
69+
const trustedReviewerCache = new Map();
6970
70-
function isTrustedReviewer(actor) {
71-
const association = actor?.author_association || actor?.authorAssociation;
72-
return Boolean(actor?.user?.login) && TRUSTED_ASSOCIATIONS.has(association);
71+
async function isTrustedReviewer(login) {
72+
if (!login || isBot(login)) {
73+
return false;
74+
}
75+
76+
if (trustedReviewerCache.has(login)) {
77+
return trustedReviewerCache.get(login);
78+
}
79+
80+
try {
81+
const response = await github.rest.repos.getCollaboratorPermissionLevel({
82+
owner: context.repo.owner,
83+
repo: context.repo.repo,
84+
username: login,
85+
});
86+
87+
const trusted = TRUSTED_PERMISSIONS.has(response.data.permission);
88+
trustedReviewerCache.set(login, trusted);
89+
return trusted;
90+
} catch (error) {
91+
trustedReviewerCache.set(login, false);
92+
return false;
93+
}
7394
}
7495
7596
async function paginate(method, params) {
@@ -84,11 +105,11 @@ jobs:
84105
direction: 'asc',
85106
});
86107
87-
function collectFeedbackAt(author, collection, getLogin, getTimestamp, predicate = () => true) {
108+
function collectFeedbackAt(author, trustedLogins, collection, getLogin, getTimestamp, predicate = () => true) {
88109
return collection
89110
.filter((item) => {
90111
const login = getLogin(item);
91-
if (!login || login === author || isBot(login) || !isTrustedReviewer(item)) {
112+
if (!login || login === author || isBot(login) || !trustedLogins.has(login)) {
92113
return false;
93114
}
94115
return predicate(item);
@@ -158,6 +179,39 @@ jobs:
158179
}
159180
}
160181
182+
const candidateTrustedLogins = new Set();
183+
for (const comment of comments) {
184+
const login = comment.user?.login;
185+
if (login && login !== author && !isBot(login)) {
186+
candidateTrustedLogins.add(login);
187+
}
188+
}
189+
for (const review of reviews) {
190+
const login = review.user?.login;
191+
if (login && login !== author && !isBot(login)) {
192+
candidateTrustedLogins.add(login);
193+
}
194+
}
195+
for (const reviewComment of reviewComments) {
196+
const login = reviewComment.user?.login;
197+
if (login && login !== author && !isBot(login)) {
198+
candidateTrustedLogins.add(login);
199+
}
200+
}
201+
202+
const trustedLogins = new Set();
203+
await Promise.all(
204+
[...candidateTrustedLogins].map(async (login) => {
205+
if (await isTrustedReviewer(login)) {
206+
trustedLogins.add(login);
207+
}
208+
}),
209+
);
210+
211+
if (DRY_RUN && pr.number === 642) {
212+
core.info(`DEBUG #642 trustedLogins ${JSON.stringify([...trustedLogins])}`);
213+
}
214+
161215
let latestAuthorResponseAt = 0;
162216
163217
for (const comment of comments) {
@@ -195,9 +249,10 @@ jobs:
195249
}
196250
197251
const feedbackAts = [
198-
...collectFeedbackAt(author, comments, (comment) => comment.user?.login, (comment) => ts(comment.created_at)),
252+
...collectFeedbackAt(author, trustedLogins, comments, (comment) => comment.user?.login, (comment) => ts(comment.created_at)),
199253
...collectFeedbackAt(
200254
author,
255+
trustedLogins,
201256
reviews,
202257
(review) => review.user?.login,
203258
(review) => ts(review.submitted_at || review.created_at),
@@ -206,7 +261,7 @@ jobs:
206261
return state !== 'APPROVED' && state !== 'DISMISSED' && state !== 'PENDING';
207262
},
208263
),
209-
...collectFeedbackAt(author, reviewComments, (reviewComment) => reviewComment.user?.login, (reviewComment) => ts(reviewComment.created_at)),
264+
...collectFeedbackAt(author, trustedLogins, reviewComments, (reviewComment) => reviewComment.user?.login, (reviewComment) => ts(reviewComment.created_at)),
210265
].filter((feedbackAt) => feedbackAt > latestAuthorResponseAt);
211266
212267
if (feedbackAts.length === 0) {

0 commit comments

Comments
 (0)