-
Notifications
You must be signed in to change notification settings - Fork 93
392 lines (356 loc) · 16.3 KB
/
ready_for_review.yml
File metadata and controls
392 lines (356 loc) · 16.3 KB
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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
name: Pull Request Ready for Review
on:
pull_request:
types: [opened, reopened, synchronize, ready_for_review, converted_to_draft, review_requested]
workflow_run:
workflows: ["Code Checks", "Check CODEOWNERS Entries", "Service Tags", "CodeQL"]
types: [completed]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref_name }}
cancel-in-progress: true
permissions:
id-token: write
contents: read
jobs:
get-pr-data:
name: Get PR Data
permissions: write-all
runs-on: ubuntu-latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
outputs:
pr_number: ${{ steps.get_pr_data.outputs.pr_number }}
pr_draft: ${{ steps.get_pr_data.outputs.pr_draft }}
pr_labels: ${{ steps.get_pr_data.outputs.pr_labels }}
pr_requested_teams: ${{ steps.get_pr_data.outputs.pr_requested_teams }}
pr_branch: ${{ steps.get_pr_data.outputs.pr_branch }}
steps:
- name: Checkout PR HEAD ref
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Get pull_request data
id: get_pr_data
run: |
if ${{ github.event_name == 'pull_request' }}; then
echo "pr_number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
echo "pr_draft=${{ github.event.pull_request.draft }}" >> $GITHUB_OUTPUT
echo "pr_labels=$(echo '${{ toJSON(github.event.pull_request.labels.*.name) }}' | jq -c '.')" >> $GITHUB_OUTPUT
echo "pr_branch=${{ github.head_ref }}" >> $GITHUB_OUTPUT
if ${{ github.event.pull_request.draft }}; then
echo "pr_requested_teams=[]" >> $GITHUB_OUTPUT
else
echo "pr_requested_teams=$(echo '${{ toJSON(github.event.pull_request.requested_teams.*.slug) }}' | jq -c '.')" >> $GITHUB_OUTPUT
fi
elif ${{ github.event_name == 'workflow_run' }}; then
if ${{ github.event.workflow_run.event == 'push' }}; then
HEAD_BRANCH="${{ github.event.workflow_run.head_branch }}"
echo "Workflow was triggered by push to $HEAD_BRANCH. Labeling not required."
exit 0
elif ${{ toJSON(github.event.workflow_run.pull_requests) != '[]' }}; then
PR_NUMBER="${{ github.event.workflow_run.pull_requests[0].number }}"
echo "pr_number=${PR_NUMBER}" >> $GITHUB_OUTPUT
echo "pr_branch=${{ github.event.workflow_run.head_branch }}" >> $GITHUB_OUTPUT
# Fetch PR details from GitHub API
PR_INFO=$(gh api /repos/${{ github.repository }}/pulls/${PR_NUMBER} --jq '{
draft: .draft,
labels: [.labels[].name],
requested_teams: [.requested_teams[].slug]
}')
# Extract and store individual fields
PR_DRAFT=$(echo "$PR_INFO" | jq -r '.draft')
PR_LABELS=$(echo "$PR_INFO" | jq -c '.labels')
echo "pr_draft=$PR_DRAFT" >> $GITHUB_OUTPUT
echo "pr_labels=$PR_LABELS" >> $GITHUB_OUTPUT
if [[ "$PR_DRAFT" == "true" ]]; then
echo "pr_requested_teams=[]" >> $GITHUB_OUTPUT
else
PR_REQUESTED_TEAMS=$(echo "$PR_INFO" | jq -c '.requested_teams')
echo "Requested Teams: $PR_REQUESTED_TEAMS"
echo "pr_requested_teams=$PR_REQUESTED_TEAMS" >> $GITHUB_OUTPUT
fi
else
echo "Workflow run has no associated pull requests. Labeling not performed."
exit 0
fi
else
echo "event_name: ${{ github.event_name }}"
echo "Pull Request not successfully retrieved."
exit 1
fi
- name: Remove require-backend-approval label
if: steps.get_pr_data.outputs.pr_draft == 'true'
uses: actions-ecosystem/action-remove-labels@v1
with:
number: ${{ steps.get_pr_data.outputs.pr_number }}
labels: require-backend-approval
- name: Remove ready-for-backend-review label
if: steps.get_pr_data.outputs.pr_draft == 'true'
uses: actions-ecosystem/action-remove-labels@v1
with:
number: ${{ needs.get-pr-data.outputs.pr_number }}
labels: ready-for-backend-review
- name: Remove ready-for-review label
if: steps.get_pr_data.outputs.pr_draft == 'true'
uses: actions-ecosystem/action-remove-labels@v1
with:
number: ${{ steps.get_pr_data.outputs.pr_number }}
labels: ready-for-review
- name: Fail if draft
if: steps.get_pr_data.outputs.pr_draft == 'true'
run: exit 1
check-workflow-status:
name: Check Workflow Statuses
needs: [get-pr-data]
if: needs.get-pr-data.outputs.pr_draft == 'false'
runs-on: ubuntu-latest
permissions: write-all
env:
pr_number: ${{ needs.get-pr-data.outputs.pr_number }}
pr_branch: ${{ needs.get-pr-data.outputs.pr_branch }}
outputs:
failures_detected: ${{ steps.check_workflow_status.outputs.failures_detected }}
steps:
- name: Check if workflows have completed successfully
id: check_workflow_status
run: |
failures_detected=false
failures=false
unsuccessful_count=0
GITHUB_REPOSITORY="department-of-veterans-affairs/vets-api"
WORKFLOWS=("Code Checks" "Check CODEOWNERS Entries" "Service Tags" "CodeQL")
for WORKFLOW in "${WORKFLOWS[@]}"; do
# Check if there's any in-progress run for this workflow
IN_PROGRESS=$(gh run list --repo "$GITHUB_REPOSITORY" --workflow="$WORKFLOW" --status=in_progress --json headBranch,event,headSha -q \
".[] | select(.headBranch == \"${pr_branch}\" and .event == \"pull_request\") | .headSha" | head -n 1)
if [ -n "$IN_PROGRESS" ]; then
echo "$WORKFLOW is still in progress."
failures=true
else
# Check if the latest completed run was successful
SUCCESSFUL=$(gh run list --repo "$GITHUB_REPOSITORY" --workflow="$WORKFLOW" --status=completed --json headBranch,event,conclusion,headSha -q \
".[] | select(.headBranch == \"${pr_branch}\" and .event == \"pull_request\") | select(.conclusion == \"success\") | .headSha" | head -n 1)
if [ -z "$SUCCESSFUL" ]; then
echo "$WORKFLOW did not complete successfully."
((unsuccessful_count++))
failures=true
else
echo "$WORKFLOW completed successfully."
fi
fi
done
if [ "$unsuccessful_count" -eq 4 ]; then
failures=false
echo "All workflows are failing."
fi
echo "$failures"
echo "failures_detected=$failures" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
check-backend-requirement:
name: Check Backend Requirement
needs: [get-pr-data, check-workflow-status]
if: needs.get-pr-data.outputs.pr_draft == 'false'
runs-on: ubuntu-latest
permissions: write-all
env:
failures_detected: ${{ needs.check-workflow-status.outputs.failures_detected }}
pr_number: ${{ needs.get-pr-data.outputs.pr_number }}
pr_labels: ${{ needs.get-pr-data.outputs.pr_labels }}
pr_requested_teams: ${{ needs.get-pr-data.outputs.pr_requested_teams }}
outputs:
exempt_team: ${{ steps.check_backend_requirement.outputs.exempt_team }}
backend_approval_required: ${{ steps.check_backend_requirement.outputs.backend_approval_required }}
steps:
- name: Check Backend Requirement
id: check_backend_requirement
run: |
backend_approval_required=false
exempt_team=false
PR_REQUESTED_TEAMS="${{ env.pr_requested_teams }}"
PR_REQUESTED_TEAMS_JSON=$(echo "$PR_REQUESTED_TEAMS" | sed 's/\[/["/' | sed 's/\]/"]/' | sed 's/,/","/g')
echo "Parsed PR_REQUESTED_TEAMS_JSON: $PR_REQUESTED_TEAMS_JSON"
PR_LABELS="${{ env.pr_labels }}"
EXEMPT_TEAMS=(
octo-identity
lighthouse-dash
lighthouse-pivot
lighthouse-banana-peels
mobile-api-team
accredited-representatives-admin
benefits-admin
)
for TEAM in "${EXEMPT_TEAMS[@]}"; do
echo "Non BE Requested Team: $TEAM"
if echo "$PR_REQUESTED_TEAMS_JSON" | jq -e ". | index(\"$TEAM\")" > /dev/null; then
echo "Excemption Groups were found: $TEAM"
exempt_team=true
backend_approval_required=false
break
fi
done
if [[ "$exempt_team" == "false" ]]; then
echo "Excemption Groups were not found"
echo "PR REQUESTED TEAMS: ${PR_REQUESTED_TEAMS_JSON}"
if echo "$PR_REQUESTED_TEAMS_JSON" | jq -e '. | index("backend-review-group")' > /dev/null || \
echo "$PR_LABELS" | jq -e '. | index("require-backend-approval")' > /dev/null; then
backend_approval_required=true
fi
fi
echo "exempt_team=${exempt_team}" >> $GITHUB_OUTPUT
echo "backend_approval_required=${backend_approval_required}" >> $GITHUB_OUTPUT
- name: Add require-backend-approval label
uses: actions-ecosystem/action-add-labels@v1
if: steps.check_backend_requirement.outputs.backend_approval_required == 'true'
with:
number: ${{ env.pr_number }}
labels: require-backend-approval
- name: Remove require-backend-approval label
uses: actions-ecosystem/action-remove-labels@v1
if: steps.check_backend_requirement.outputs.backend_approval_required == 'false'
with:
number: ${{ env.pr_number }}
labels: require-backend-approval
fetch-pr-reviews:
name: Fetch Pull Request Reviews
needs: [get-pr-data, check-workflow-status, check-backend-requirement]
if: needs.get-pr-data.outputs.pr_draft == 'false'
runs-on: ubuntu-latest
permissions: write-all
env:
exempt_team: ${{ needs.check-backend-requirement.outputs.exempt_team }}
backend_approval_required: ${{ needs.check-backend-requirement.outputs.backend_approval_required }}
failures_detected: ${{ needs.check-workflow-status.outputs.failures_detected }}
pr_number: ${{ needs.get-pr-data.outputs.pr_number }}
pr_branch: ${{ needs.get-pr-data.outputs.pr_branch }}
pr_draft: ${{ needs.get-pr-data.outputs.pr_draft }}
outputs:
author_is_backend: ${{ steps.verify_approval.outputs.author_is_backend }}
team_approval_status: ${{ steps.verify_approval.outputs.team_approval_status }}
approval_status: ${{ steps.verify_approval.outputs.approval_status }}
steps:
- name: Checkout PR HEAD ref
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4.0.2
with:
role-to-assume: ${{ vars.AWS_ASSUME_ROLE }}
aws-region: "us-gov-west-1"
- name: Get bot token from Parameter Store
uses: marvinpinto/action-inject-ssm-secrets@latest
with:
ssm_parameter: /devops/VA_VSP_BOT_GITHUB_TOKEN
env_variable_name: VA_VSP_BOT_GITHUB_TOKEN
- name: Verify backend-review-group approval
id: verify_approval
run: |
approval_status=required
team_approval_status=required
author_is_backend=false
EXEMPT_TEAM="${{ env.exempt_team }}"
EXEMPT_TEAMS=(
octo-identity
lighthouse-dash
lighthouse-pivot
lighthouse-banana-peels
mobile-api-team
accredited-representatives-admin
benefits-admin
)
BACKEND_REVIEWERS=$(gh api /orgs/department-of-veterans-affairs/teams/backend-review-group/members --jq '.[].login')
BACKEND_REVIEWERS=$(echo "$BACKEND_REVIEWERS" | tr '\n' '|' | sed 's/|$//')
echo "Backend reviewers regex: $BACKEND_REVIEWERS"
# Get PR author
PR_AUTHOR=$(gh pr view ${{ env.pr_number }} --json author -q '.author.login')
if echo "$PR_AUTHOR" | grep -qiE "^($BACKEND_REVIEWERS)$"; then
echo "PR author '$PR_AUTHOR' is a backend-review-group member."
echo "author_is_backend=true" >> "$GITHUB_OUTPUT"
fi
APPROVED=$(gh api /repos/${{ github.repository }}/pulls/${pr_number}/reviews --jq '
[ .[]
| select(.state == "APPROVED")
]
| sort_by(.submitted_at)
| reverse
| unique_by(.user.login)
| .[].user.login
' || true)
echo "Approvals: $APPROVED"
TEAM_APPROVALS=$(echo "$APPROVED" | grep -viE "^($BACKEND_REVIEWERS)$" || true)
echo "Team Approvals: $TEAM_APPROVALS"
if [ -z "$TEAM_APPROVALS" ]; then
echo "TEAM APPROVAL REQUIRED"
team_approval_status=required
else
echo "TEAM APPROVAL CONFIRMED"
team_approval_status=confirmed
fi
if [[ "${EXEMPT_TEAM}" == "true" ]]; then
approval_status=confirmed
else
BE_APPROVALS=$(echo "$APPROVED" | grep -iE "^($BACKEND_REVIEWERS)$" || true)
echo "Backend Approvals: $BE_APPROVALS"
if [ -z "$BE_APPROVALS" ]; then
echo "BE APPROVAL REQUIRED"
approval_status=required
else
echo "BE APPROVAL CONFIRMED"
approval_status=confirmed
fi
fi
if [[ "$team_approval_status" == "confirmed" ]]; then
for TEAM in "${EXEMPT_TEAMS[@]}"; do
echo "Fetching members of team: $TEAM"
TEAM_MEMBERS=$(gh api /orgs/department-of-veterans-affairs/teams/${TEAM}/members --paginate --jq '.[].login')
while IFS= read -r MEMBER; do
while IFS= read -r REVIEWER; do
echo "Comparing MEMBER='${MEMBER}' to REVIEWER='${REVIEWER}'"
if [[ -n "$MEMBER" && -n "$REVIEWER" && "$MEMBER" == "$REVIEWER" ]]; then
echo "Reviewer is in exempt team"
echo "exempt_team=true" >> "$GITHUB_OUTPUT"
break 2
fi
done <<< "$APPROVED"
done <<< "$TEAM_MEMBERS"
done
fi
echo "author_is_backend=$author_is_backend" >> "$GITHUB_OUTPUT"
echo "team_approval_status=$team_approval_status" >> "$GITHUB_OUTPUT"
echo "approval_status=$approval_status" >> "$GITHUB_OUTPUT"
env:
GITHUB_TOKEN: ${{ env.VA_VSP_BOT_GITHUB_TOKEN }}
# The ready-for-review label will be added when pull request has not been reviewed
- name: Add ready-for-review label
if: |
needs.check-workflow-status.outputs.failures_detected == 'false' &&
(
steps.verify_approval.outputs.approval_status == 'required' ||
steps.verify_approval.outputs.team_approval_status == 'required'
)
uses: actions-ecosystem/action-add-labels@v1
with:
number: ${{ env.pr_number }}
labels: ready-for-review
# The ready-for-backend-review label will be added when a VFS team member has reviewed the pull request
# The ready-for-backend-review label will be added for backend-review-group team members
- name: Add ready-for-backend-review label
if: |
needs.check-workflow-status.outputs.failures_detected == 'false' &&
needs.check-backend-requirement.outputs.backend_approval_required == 'true' &&
steps.verify_approval.outputs.approval_status == 'required' &&
(
steps.verify_approval.outputs.author_is_backend == 'true' ||
steps.verify_approval.outputs.team_approval_status == 'confirmed'
)
uses: actions-ecosystem/action-add-labels@v1
with:
number: ${{ env.pr_number }}
labels: ready-for-backend-review