forked from MetOffice/growss
-
Notifications
You must be signed in to change notification settings - Fork 0
205 lines (179 loc) · 9.72 KB
/
Copy pathtrack-review-project.yaml
File metadata and controls
205 lines (179 loc) · 9.72 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
# ------------------------------------------------------------------------------
# (c) Crown copyright Met Office. All rights reserved.
# The file LICENCE, distributed with this code, contains details of the terms
# under which the code may be used.
# ------------------------------------------------------------------------------
# This workflow runs whenever a pull request in the repository is marked as
# "Ready for review" (i.e., a non-Draft PR).
name: Track Review Project
on:
workflow_call:
inputs:
runner:
description: 'The runner to use for the job'
required: false
type: string
default: 'ubuntu-24.04'
project_org:
description: 'The organization that owns the project'
required: false
type: string
default: 'MetOffice'
project_number:
description: 'The project number'
required: false
type: number
default: 376 # Simulation Systems Review Tracker
permissions:
actions: read
contents: read
pull-requests: write
jobs:
track_project:
runs-on: ${{ inputs.runner }}
timeout-minutes: 5
env:
REPO: ${{ github.repository }}
steps:
# Required as running from the workflow_run trigger
- name: Checkout repo
uses: actions/checkout@v6
- name: Download PR data artifact
uses: actions/github-script@v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
return artifact.name == "context.json"
})[0];
let download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});
let fs = require('fs');
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/context.zip`, Buffer.from(download.data));
- name: Extract PR Data
run: |
unzip context.zip
pr_num=$(jq -r '.pr_num' context.json)
pr_id=$(jq -r '.pr_id' context.json)
echo "PR_NUM=$pr_num" >> $GITHUB_ENV
unzip -q context.zip
jq -r '"PR_NUM=\(.pr_num)\nPR_ID=\(.pr_id)"' context.json >> $GITHUB_ENV
- name: Assign Author if no assignees
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
run: |
pr_data=$(gh pr view -R "$REPO" "$PR_NUM" --json assignees,author)
if [[ $(echo "$pr_data" | jq '.assignees | length') -eq 0 ]]; then
author=$(echo "$pr_data" | jq -r '.author.login')
gh pr edit -R "$REPO" "$PR_NUM" --add-assignee "$author"
fi
- name: Extract reviewers and determine state
env:
GH_TOKEN: ${{ github.token }}
run: |
pr_data=$(gh pr view -R "$REPO" "$PR_NUM" --json body,reviews,reviewRequests)
# Extract reviewers from PR body
pr_body=$(echo "$pr_data" | jq -r '.body')
scitech_reviewer=$(echo "$pr_body" | grep -oP "Sci/Tech Reviewer:\s?@\K([\w\-]{1,39})\b" || true)
code_reviewer=$(echo "$pr_body" | grep -oP "Code Reviewer:\s?@\K([\w\-]{1,39})\b" || true)
echo "Expected SciTech Reviewer: ${scitech_reviewer:-None}"
echo "Expected Code Reviewer: ${code_reviewer:-None}"
echo "SCITECH_REVIEWER=$scitech_reviewer" >> $GITHUB_ENV
echo "CODE_REVIEWER=$code_reviewer" >> $GITHUB_ENV
# Determine review state
reviews=$(echo "$pr_data" | jq -c '{reviews, reviewRequests}')
sr_requested=$(echo "$reviews" | jq --arg sr "$scitech_reviewer" '[.reviewRequests[] | select(.login == $sr)] | length')
cr_requested=$(echo "$reviews" | jq --arg cr "$code_reviewer" '[.reviewRequests[] | select(.login == $cr)] | length')
# Determine required state
sr_approved=$(echo "$reviews" | jq --arg sr "$scitech_reviewer" '[.reviews[] | select(.author.login == $sr and .state == "APPROVED")] | length')
sr_changes=$(echo "$reviews" | jq --arg sr "$scitech_reviewer" '[.reviews[] | select(.author.login == $sr and (.state == "CHANGES_REQUESTED" or .state == "COMMENTED"))] | length')
[[ ! -z "$scitech_reviewer" ]] && [[ "$sr_requested" -eq 0 ]] && [[ "$sr_approved" -eq 0 ]] && [[ "$sr_changes" -eq 0 ]] && echo "SR_REQUEST_REQUIRED=true" >> $GITHUB_ENV
if [[ -z "$scitech_reviewer" ]] && [[ "$cr_requested" -gt 0 ]]; then
echo "No SR set and CR requested so assuming this is trivial"
else
if [[ "$sr_approved" -eq 0 ]]; then
if [[ -z "$scitech_reviewer" ]] && [[ "$sr_changes" -eq 0 ]]; then
required_state="In Progress"
elif [[ "$sr_changes" -gt 0 ]] && [[ "$sr_requested" -eq 0 ]]; then
required_state="Changes Requested"
else
required_state="SciTech Review"
fi
echo "REQUIRED_STATE=$required_state" >> $GITHUB_ENV
exit 0
fi
fi
cr_approved=$(echo "$reviews" | jq --arg cr "$code_reviewer" '[.reviews[] | select(.author.login == $cr and .state == "APPROVED")] | length')
cr_changes=$(echo "$reviews" | jq --arg cr "$code_reviewer" '[.reviews[] | select(.author.login == $cr and (.state == "CHANGES_REQUESTED" or .state == "COMMENTED"))] | length')
if [[ "$cr_approved" -gt 0 ]]; then
required_state="Approved"
elif [[ "cr_changes" -eq 0 ]] && [[ "$cr_requested" -eq 0 ]] && [[ "$sr_approved" -eq 0 ]]; then
required_state="In Progress"
elif [[ "$cr_changes" -gt 0 ]] && [[ "$cr_requested" -eq 0 ]]; then
required_state="Changes Requested"
else
required_state="Code Review"
[[ "$cr_requested" -eq 0 ]] && [[ "$cr_approved" -eq 0 ]] && [[ "$cr_changes" -eq 0 ]] && echo "CR_REQUEST_REQUIRED=true" >> $GITHUB_ENV
fi
echo "REQUIRED_STATE=$required_state" >> $GITHUB_ENV
- name: Request SciTech review, if needed
if: env.SR_REQUEST_REQUIRED == 'true' && env.REQUIRED_STATE == 'SciTech Review' && env.SCITECH_REVIEWER != ''
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
run: gh pr edit -R "$REPO" "$PR_NUM" --add-reviewer "$SCITECH_REVIEWER"
- name: Request code review, if needed
if: env.CR_REQUEST_REQUIRED == 'true' && env.REQUIRED_STATE == 'Code Review' && env.CODE_REVIEWER != ''
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
run: |
gh pr edit -R "$REPO" "$PR_NUM" --add-reviewer "$CODE_REVIEWER"
- name: Update project tracking
env:
GH_TOKEN: ${{ secrets.PROJECT_ACTION_PAT }}
run: |
# Get project data
project_data=$(gh api graphql -f query='
query($org: String!, $number: Int!) {
organization(login: $org){
projectV2(number: $number) {
id
fields(first:20) {
nodes {
... on ProjectV2Field { id name }
... on ProjectV2SingleSelectField { id name options { id name } }
}
}
}
}
}' -f org=${{ inputs.project_org }} -F number=${{ inputs.project_number }})
project_id=$(echo "$project_data" | jq -r '.data.organization.projectV2.id')
status_field_id=$(echo "$project_data" | jq -r '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .id')
status_option_id=$(echo "$project_data" | jq -r --arg status "$REQUIRED_STATE" '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .options[] | select(.name==$status) | .id')
scitech_field_id=$(echo "$project_data" | jq -r '.data.organization.projectV2.fields.nodes[] | select(.name== "SciTech Review") | .id')
code_field_id=$(echo "$project_data" | jq -r '.data.organization.projectV2.fields.nodes[] | select(.name== "Code Review") | .id')
# Add PR to project and update fields
item_id=$(gh api graphql -f query='
mutation($project:ID!, $pr:ID!) {
addProjectV2ItemById(input: {projectId: $project, contentId: $pr}) {
item { id }
}
}' -f project="$project_id" -f pr="$PR_ID" --jq '.data.addProjectV2ItemById.item.id')
# Update all fields in single mutation
gh api graphql -f query='
mutation($project: ID!, $item: ID!, $status_field: ID!, $status_value: String!, $cr_field: ID!, $cr_value: String!, $sr_field: ID!, $sr_value: String!) {
set_status: updateProjectV2ItemFieldValue(input: {projectId: $project, itemId: $item, fieldId: $status_field, value: {singleSelectOptionId: $status_value}}) { projectV2Item { id } }
set_code_review: updateProjectV2ItemFieldValue(input: {projectId: $project, itemId: $item, fieldId: $cr_field, value: {text: $cr_value}}) { projectV2Item { id } }
set_scitech_review: updateProjectV2ItemFieldValue(input: {projectId: $project, itemId: $item, fieldId: $sr_field, value: {text: $sr_value}}) { projectV2Item { id } }
}' -f project="$project_id" -f item="$item_id" -f status_field="$status_field_id" -f status_value="$status_option_id" -f cr_field="$code_field_id" -f cr_value="$CODE_REVIEWER" -f sr_field="$scitech_field_id" -f sr_value="$SCITECH_REVIEWER" --silent