-
Notifications
You must be signed in to change notification settings - Fork 2
171 lines (150 loc) · 6.52 KB
/
want-lgtm-all.yml
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
# Copyright 2023 The Authors (see AUTHORS file)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: 'want-lgtm-all'
on:
pull_request:
types:
- 'opened'
- 'edited'
- 'reopened'
- 'synchronize'
- 'ready_for_review'
- 'review_requested'
- 'review_request_removed'
pull_request_review:
types:
- 'submitted'
- 'dismissed'
workflow_call:
concurrency:
group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}'
cancel-in-progress: true
permissions:
actions: 'write'
pull-requests: 'read'
jobs:
want-lgtm-all:
if: |-
${{ contains(fromJSON('["pull_request", "pull_request_review"]'), github.event_name) }}
runs-on: 'ubuntu-latest'
steps:
- id: 'want-lgtm'
name: 'Validate Reviews'
if: |-
${{ contains(github.event.pull_request.body, 'want_lgtm=all') }}
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea' # ratchet:actions/github-script@v7
with:
retries: 3
script: |-
// get all submitted reviews
const submittedReviews = await github.paginate(github.rest.pulls.listReviews, {
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
});
// multiple users can submit multiple reviews with different statuses
// aggregate the status per login in chronological order to get latest status
const reviewStateByLogin = {};
submittedReviews
.filter((r) => r.user.login !== context.payload.pull_request.user.login)
.sort((a, b) => new Date(a.submitted_at) - new Date(b.submitted_at))
.forEach((r) => {
// add value if it doesnt not exist
if (!Object.hasOwn(reviewStateByLogin, r.user.login)) {
reviewStateByLogin[r.user.login] = r.state;
return;
}
// always update state if not approved
if (reviewStateByLogin[r.user.login] !== "APPROVED") {
reviewStateByLogin[r.user.login] = r.state;
return;
}
// do not update approved state for comment
if (
reviewStateByLogin[r.user.login] === "APPROVED" &&
r.state !== "COMMENTED"
) {
reviewStateByLogin[r.user.login] = r.state;
}
});
// get all reviews without an approved status
const unapprovedReviews = Object.entries(reviewStateByLogin)
.filter(([key, value]) => value.toUpperCase() !== "APPROVED")
.map(([key, value]) => key);
core.info("Unapproved review(s): " + JSON.stringify(unapprovedReviews));
if (unapprovedReviews.length > 0) {
core.setFailed("Unapproved review(s): " + unapprovedReviews.join(", "));
return;
}
const { data: requestedReviewers } =
await github.rest.pulls.listRequestedReviewers({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
});
const pendingUserApprovals = requestedReviewers.users.map((u) => u.login);
const pendingTeamApprovals = requestedReviewers.teams.map((t) => t.slug);
const pendingApprovals = [...pendingUserApprovals, ...pendingTeamApprovals];
core.info("Pending approval(s): " + JSON.stringify(pendingApprovals));
if (pendingApprovals.length > 0) {
core.setFailed("Pending approval(s): " + pendingApprovals.join(", "));
return;
}
// sanity check, require at least one reviewer
if (submittedReviews.length === 0 && pendingApprovals.length === 0) {
core.setFailed(
"At least one reviewer is required when specifying want_lgtm_all. Please add a reviewer or remove want_lgtm_all from the PR body."
);
return;
}
# when a pull_request_review is submitted and all required reviewers have approved
# we need to re-trigger any previously failed pull_request event for our workflow
# this is because pull_request and pull_request_review are seen as two different
# status checks by github
- id: 'rerun-status-checks'
name: 'Re-run Status Checks'
if: |-
${{ contains(github.event.pull_request.body, 'want_lgtm=all') && github.event_name == 'pull_request_review' }}
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea' # ratchet:actions/github-script@v7
with:
retries: 3
script: |-
// get the filename for this workflow
const workflowFilename = process.env.GITHUB_WORKFLOW_REF.split("@")[0]
.split("/")
.pop();
// get the latest failed workflow runs for our file and this branch
const workflows = await github.paginate(github.rest.actions.listWorkflowRuns, {
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: workflowFilename,
branch: context.payload.pull_request.head.ref,
event: "pull_request",
status: "failure",
per_page: 100,
});
// filter workflow runs only for this pull request number
const unsuccessfulRuns = workflows
.filter((w) =>
w.pull_requests.map((p) => p.number).includes(context.issue.number)
)
.sort((x) => x.id);
// retrigger the latest run for our unsuccessful workflow run
if (unsuccessfulRuns.length > 0) {
await github.rest.actions.reRunWorkflow({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: unsuccessfulRuns[0].id,
});
}