-
-
Notifications
You must be signed in to change notification settings - Fork 161
163 lines (131 loc) · 6.69 KB
/
pr-review-reminder.yaml
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
name: PR Review Reminder
on:
schedule:
- cron: '0 * * * *' # Runs every hour
jobs:
pr-review-reminder:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v7
env:
CREATE_PR_REVIEW_REMINDER_ENABLED: ${{ vars.CREATE_PR_REVIEW_REMINDER_ENABLED }}
CREATE_PR_REVIEW_REMINDER_AFTER_PR_OPENED_DAYS: ${{ vars.CREATE_PR_REVIEW_REMINDER_AFTER_PR_OPENED_DAYS }}
CREATE_PR_REVIEW_REMINDER_BEFORE_PR_CLOSED_DAYS: ${{ vars.CREATE_PR_REVIEW_REMINDER_BEFORE_PR_CLOSED_DAYS }}
CLOSE_PR_AFTER_DAYS: ${{ vars.CLOSE_PR_AFTER_DAYS }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const owner = 'keyshade-xyz';
const repo = 'keyshade';
const createReviewReminderEnabled = process.env.CREATE_PR_REVIEW_REMINDER_ENABLED || false;
const createReviewReminderAfterPrOpenedDays = process.env.CREATE_PR_REVIEW_REMINDER_AFTER_PR_OPENED_DAYS || 2;
const createReviewReminderBeforePrClosedDays = process.env.CREATE_PR_REVIEW_REMINDER_BEFORE_PR_CLOSED_DAYS || 2;
const closePrAfterDays = process.env.CLOSE_PR_AFTER_DAYS || 7;
const createReviewReminderAfterPrOpenedMilliseconds = createReviewReminderAfterPrOpenedDays * 24 * 60 * 60 * 1000;
const createReviewReminderBeforePrClosedMilliseconds = createReviewReminderBeforePrClosedDays * 24 * 60 * 60 * 1000;
const closePrAfterMilliseconds = closePrAfterDays * 24 * 60 * 60 * 1000;
const now = Date.now();
if(!createReviewReminderEnabled) {
console.log('!!! Dry run, there are no changes made !!!');
}
async function listOpenPrs() {
const pullRequests = [];
let page = 1;
let hasNextPage = true;
while (hasNextPage) {
const pullRequestsResponse = await github.rest.pulls.list({
owner,
repo,
state: 'open',
per_page: 100,
page
});
pullRequests.push(...pullRequestsResponse.data);
hasNextPage = pullRequestsResponse.headers.link && pullRequestsResponse.headers.link.includes('rel="next"');
page++;
}
return pullRequests;
}
async function listPrReviews(pullRequest) {
const reviews = [];
let page = 1;
let hasNextPage = true;
while (hasNextPage) {
const reviewsResponse = await github.rest.pulls.listReviews({
owner,
repo,
pull_number: pullRequest.number,
per_page: 100,
page
});
reviews.push(...reviewsResponse.data);
hasNextPage = reviewsResponse.headers.link && reviewsResponse.headers.link.includes('rel="next"');
page++;
}
return reviews;
}
async function listPrComments(pullRequest) {
const comments = [];
let page = 1;
let hasNextPage = true;
while (hasNextPage) {
const issuesResponse = await github.rest.issues.listComments({
owner,
repo,
issue_number: pullRequest.number,
per_page: 100,
page
});
comments.push(...issuesResponse.data);
hasNextPage = issuesResponse.headers.link && issuesResponse.headers.link.includes('rel="next"');
page++;
}
return comments;
}
async function createReviewReminder(user, pullRequestNumber, comments, createReviewReminderAfter, reviewReminder) {
// Check if it is time to create review reminder
if (now < createReviewReminderAfter) {
return false;
}
// Check if review reminder has already been created
if (comments.some(comment => comment.body === reviewReminder)) {
return false;
}
// Create review reminder
if(createReviewReminderEnabled) {
await github.rest.issues.createComment({
owner,
repo,
issue_number: pullRequestNumber,
body: reviewReminder
});
}
console.log(`PR '${pullRequestNumber}' review reminder created for: ${user.login}`);
return true;
}
async function createReviewReminders() {
const pullRequests = await listOpenPrs();
for (const pullRequest of pullRequests) {
const reviews = await listPrReviews(pullRequest);
const unapprovedReviews = reviews.filter(review => review.state !== 'APPROVED');
const comments = await listPrComments(pullRequest);
const pullRequestCreatedAt = new Date(pullRequest.created_at);
// Check if PR has unapproved reviews
if(unapprovedReviews.length === 0) {
continue;
}
// Create first review reminder
const createFirstReviewReminderAfter = new Date(pullRequestCreatedAt.getTime() + createReviewReminderAfterPrOpenedMilliseconds);
const firstReviewReminder = `@${pullRequest.user.login}, please resolve all open reviews!`;
const isFirstReviewReminderCreated = await createReviewReminder(pullRequest.user, pullRequest.number, comments, createFirstReviewReminderAfter, firstReviewReminder);
if(isFirstReviewReminderCreated) {
continue;
}
// Create final review reminder
const closePrAfter = new Date(pullRequestCreatedAt.getTime() + closePrAfterMilliseconds);
const createFinalReviewReminderAfter = new Date(pullRequestCreatedAt.getTime() + closePrAfterMilliseconds - createReviewReminderBeforePrClosedMilliseconds);
const finalReviewReminder = `@${pullRequest.user.login}, please resolve all open reviews; otherwise this PR will be closed after ${closePrAfter}!`;
await createReviewReminder(pullRequest.user, pullRequest.number, comments, createFinalReviewReminderAfter, finalReviewReminder);
}
}
await createReviewReminders();