1919#
2020# Community review GitHub Action - sets the community review labels on PRs to show case
2121# community review activity. See Flip-518 for details
22+ # This github action also sets the target version as a label on PRs.
23+ #
24+ # If more capabilities are added to this script we should consider renaming it to have a
25+ # more generic name.
2226set -e
2327
2428# =============================================================================
@@ -29,6 +33,7 @@ REPO_NAME=flink
2933LGTM_LABEL=" community-reviewed-LGTM"
3034COMMUNITY_REVIEW_LABEL=" community-reviewed"
3135USER_CACHE_FILENAME=" user_cache.txt"
36+ PR_CACHE_FILENAME=" pr_number_cache.txt"
3237
3338# =============================================================================
3439# Community review script - is passed a github token that it uses for authentication
@@ -40,6 +45,8 @@ USER_CACHE_FILENAME="user_cache.txt"
4045# no changes requested
4146# - community-reviewed - set if a non-committer has reviewed the PR.
4247# Note only one of the above labels should be present on a PR.
48+ #
49+ # Additionally this script adds the target branch as a label to each PR.
4350# =============================================================================
4451main () {
4552 local token=" ${1?missing token} "
@@ -52,6 +59,9 @@ main() {
5259 node {
5360 number
5461 isDraft
62+ baseRef {
63+ name
64+ }
5565 timelineItems(first: 100, itemTypes: [PULL_REQUEST_REVIEW]) {
5666 nodes {
5767 ... on PullRequestReview {
@@ -99,10 +109,11 @@ main() {
99109 fi
100110 restResponse=" $( call_github_graphql_api " $token " " $payload " ) "
101111 check_github_graphql_response " $restResponse "
102- receivedPullRequests=" $( jq ' .data.repository.pullRequests.edges' <<< " $restResponse" ) "
112+ local receivedPullRequests=" $( jq ' .data.repository.pullRequests.edges' <<< " $restResponse" ) "
103113
104114 printf " Filtering %4s received pull requests... " " $( JSONArrayLength " $receivedPullRequests " ) "
105- receivedPullRequests=$( jq ' [.[] | select((.node.isDraft = false) and (.node.timelineItems.nodes | type != "array" or length > 0))]' <<< " $receivedPullRequests" )
115+
116+ receivedPullRequests=$( jq ' [.[] | select((.node.isDraft = false))]' <<< " $receivedPullRequests" )
106117 printf " %2s PR retained" " $( JSONArrayLength " $receivedPullRequests " ) "
107118
108119 pullRequests=$( jq --argjson a1 " $pullRequests " --argjson a2 " $receivedPullRequests " ' $a1 + $a2' <<< ' {}' )
@@ -147,23 +158,76 @@ process_each_pr() {
147158
148159 printf " \n(%s/%s) PR %s - " " $counter " " $prCount " " $pr_number "
149160
150- # find the node for our pr
151- local pr_reviews
152- if [[ " $hasNextPage " == " false" ]]; then
153- all_reviews=" $( jq --argjson number " $pr_number " -r ' .[] | select(.node.number==$number) | .node.timelineItems.nodes' <<< " $pullRequests" ) "
154- else
155- all_reviews=" $( get_all_reviews_for_pr " $token " " $pr_number " ) "
161+ # Add target branch as label
162+ local target_branch=$( jq --argjson number " $pr_number " -r ' .[] | select(.node.number==$number) | .node.baseRef.name' <<< " $pullRequests" )
163+ process_target_branch_label " $token " " $pr_number " " $target_branch "
164+
165+ # Add review orientated labels
166+ local has_timeline_items=$( jq --argjson number " $pr_number " -r ' .[] | select(.node.number==$number) | (.node.timelineItems.nodes | type != "array" or length > 0)' <<< " $pullRequests" )
167+ # Only process reviews if there are timeline items
168+ if [[ " $has_timeline_items " == " true" ]]; then
169+ local all_reviews
170+ if [[ " $hasNextPage " == " false" ]]; then
171+ all_reviews=" $( jq --argjson number " $pr_number " -r ' .[] | select(.node.number==$number) | .node.timelineItems.nodes' <<< " $pullRequests" ) "
172+ else
173+ all_reviews=" $( get_all_reviews_for_pr " $token " " $pr_number " ) "
174+ fi
175+ # leave only the latest reviews per reviewer in a comma separated form
176+ pr_reviewers=" $( jq ' . | sort_by([.author.login, .createdAt]) | reverse | unique_by(.author.login) | .[] | [.author.login, .state, .createdAt] | join(",")' <<< " $all_reviews" ) "
177+
178+ printf " Reviews %s Reviewers %s\n" " $( JSONArrayLength " $all_reviews " ) " " $( wc -l <<< " $pr_reviewers" | xargs) "
179+
180+ process_pr_reviews " $token " " $pr_number " " $pr_reviewers " || exit
156181 fi
157- # leave only the latest reviews per reviewer in a comma separated form
158- pr_reviewers=" $( jq ' . | sort_by([.author.login, .createdAt]) | reverse | unique_by(.author.login) | .[] | [.author.login, .state, .createdAt] | join(",")' <<< " $all_reviews" ) "
159-
160- printf " Reviews %s Reviewers %s\n" " $( JSONArrayLength " $all_reviews " ) " " $( wc -l <<< " $pr_reviewers" | xargs) "
161-
162- process_pr_reviews " $token " " $pr_number " " $pr_reviewers " || exit
163182 (( counter++ ))
164183 done <<< " $prNumbersAndPaging" || exit
165184}
166185
186+ # =============================================================================
187+ # Process target branch label for a PR
188+ # Arguments:
189+ # $1 - GitHub API token for authentication
190+ # $2 - PR number
191+ # $3 - Target branch name
192+ # =============================================================================
193+ process_target_branch_label () {
194+ local token=" ${1?missing token} "
195+ local pr_number=" ${2?missing pr number} "
196+ local target_branch=" ${3?missing target branch} "
197+
198+ local file_name=$PR_CACHE_FILENAME
199+
200+ local pr_found=false
201+ if [[ -e " $file_name " ]]; then
202+
203+ while IFS=, read -r pr_number_from_file; do
204+ if [[ " $pr_number_from_file " == " $pr_number " ]]; then
205+ pr_found=true
206+ break
207+ fi
208+ done < $file_name
209+ fi
210+
211+ # Only process labels for release branches.
212+ if [[ " $target_branch " == release-* && " $pr_found " == " false" ]]; then
213+ local target_branch_label=" target:${target_branch} "
214+
215+ # Ensure the target branch label exists before trying to add it to the PR
216+ ensure_label_exists " $token " " $target_branch_label "
217+
218+ # Get existing labels
219+ local existing_labels=$( call_github_get_labels_api " $pr_number " )
220+
221+ # Add target branch label if it doesn't exist on the PR
222+ if [[ ! " $existing_labels " =~ (^| [[:space:]])" $target_branch_label " ($| [[:space:]]) ]]; then
223+ call_github_mutate_label_api " $token " " $target_branch_label " " POST" " $pr_number " || exit
224+ printf " Added target branch label %s to PR %s\n" " $target_branch_label " " $pr_number "
225+ local line=" $pr_number "
226+ echo " $line " >> $file_name
227+ fi
228+ fi
229+ }
230+
167231# =============================================================================
168232# Process pr reviews for a pr
169233# The pr reviews a line for each review, with the user, creation time and review state
@@ -313,8 +377,7 @@ get_all_reviews_for_pr() {
313377 fi
314378 restResponse=" $( call_github_graphql_api " $token " " $payload " ) "
315379 check_github_graphql_response " $restResponse "
316- local cutdownRestResponse
317- cutdownRestResponse=" $( jq ' .data.repository.pullRequest.timelineItems.nodes' <<< " $restResponse" ) "
380+ local cutdownRestResponse=" $( jq ' .data.repository.pullRequest.timelineItems.nodes' <<< " $restResponse" ) "
318381 hasNextPage=$( jq ' .data.repository.pullRequest.timelineItems.pageInfo.hasNextPage' <<< " $restResponse" )
319382 cursor=$( jq ' .data.repository.pullRequest.timelineItems.pageInfo.endCursor' <<< " $restResponse" )
320383 # remove quotes from cursor
@@ -352,6 +415,37 @@ call_github_graphql_api() {
352415 " https://api.github.com/graphql"
353416}
354417
418+ # =============================================================================
419+ # Check if a label exists in the repository and create it if it doesn't
420+ # Arguments:
421+ # $1 - GitHub API token for authentication
422+ # $2 - Label name to check/create
423+ # =============================================================================
424+ ensure_label_exists () {
425+ local token=" ${1?missing token} "
426+ local label_name=" ${2?missing label name} "
427+
428+ local color=" 90EE90"
429+
430+ # Check if the label exists
431+ local label_exists=$( curl --fail --no-progress-meter -s \
432+ -H " Accept: application/json" \
433+ -H " Authorization: Bearer $token " \
434+ " https://api.github.com/repos/$REPO_OWNER /$REPO_NAME /labels/$label_name " \
435+ -w " %{http_code}" -o /dev/null || echo " 404" )
436+
437+ # If label doesn't exist (404), create it
438+ if [[ " $label_exists " == " 404" ]]; then
439+ sprintf " Creating label %s" " $label_name "
440+ curl --fail --no-progress-meter -s \
441+ -H " Content-Type: application/json" \
442+ -H " Authorization: Bearer $token " \
443+ -X POST \
444+ -d " {\" name\" :\" $label_name \" ,\" color\" :\" $color \" }" \
445+ " https://api.github.com/repos/$REPO_OWNER /$REPO_NAME /labels"
446+ fi
447+ }
448+
355449# =============================================================================
356450# Check the response of the cURL request. Exit in case of error.
357451# Arguments:
@@ -475,7 +569,7 @@ call_github_get_user_push_permission() {
475569 " https://api.github.com/repos/$REPO_OWNER /$REPO_NAME /collaborators/$user_name /permission" ) || exit
476570 push_permission=$( jq -r ' .user.permissions.push' <<< " $permissions" )
477571 # write line to file
478- line=" $user_name ,$push_permission "
572+ local line=" $user_name ,$push_permission "
479573 echo " $line " >> $file_name
480574 fi
481575 # echo out the permissions
0 commit comments