1+ #! /bin/bash
2+ # This runs Vale on updated files in last commit, checks if the Vale alerts are on new/modified lines, and if so, builds curl request
3+ # for GitHub review comment API. Also checks if a commment already exists before posting.
4+ # To test locally, create a Github personal access token and export a $GITHUB_AUTH_TOKEN environmental variable to use the token
5+
6+ # Check if jq is installed
7+ hash jq 2> /dev/null || { echo >&2 " Error: jq is not installed" ; exit 1; }
8+
9+ # Set $PULL_NUMBER and $COMMIT_ID if not passed as variables
10+ if [ $# -eq 0 ]; then
11+ COMMIT_ID=$( git log -n 1 --pretty=format:" %H" )
12+ PULL_NUMBER=$( curl -s " https://api.github.com/search/issues?q=$COMMIT_ID " | jq ' .items[0].number' )
13+ else
14+ PULL_NUMBER=$1
15+ COMMIT_ID=$2
16+ fi
17+
18+ FILES=$( git diff --name-only HEAD~1 HEAD --diff-filter=d " *.adoc" ' :(exclude)_unused_topics/*' )
19+
20+ function post_review_comment {
21+
22+ LINE_NUMBER=$3
23+ BODY=$1
24+ FILENAME=$2
25+ echo " Sending review comment curl request..."
26+ curl -L -X POST -H " Accept: application/vnd.github+json" -H " Authorization: Bearer $GITHUB_AUTH_TOKEN " -H " X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/openshift/openshift-docs/pulls/$PULL_NUMBER /comments -d ' {"body":"' " $BODY " ' ","commit_id":"' " $COMMIT_ID " ' ","path":"' " $FILENAME " ' ","line":' " $LINE_NUMBER " ' ,"side":"RIGHT"}'
27+
28+ }
29+
30+ function get_vale_errors {
31+ echo " Getting the Vale errors and PR comments and filtering out existing comments..."
32+ local vale_json=" $1 "
33+ local pull_comments_json=" $2 "
34+
35+ # jq map and filter for new review comments only
36+ updated_vale_json=$( jq -n --argjson vale " $vale_json " --argjson comments " $pull_comments_json " ' $vale | map(select(. as $v | $comments | any(.path == $v.path and .line == $v.line and .body == $v.body) | not))' | jq)
37+
38+ export updated_vale_json
39+
40+ }
41+
42+ # Run vale with the custom template on updated files
43+ for FILE in ${FILES} ;
44+ do
45+ # Clean out conditional markup in place and parse for vale errors
46+ sed -i ' s/ifdef::.*\|ifndef::.*\|ifeval::.*\|endif::.*/ /' " $FILE "
47+ vale_json=$( vale --minAlertLevel=error --output=.vale/templates/bot-comment-output.tmpl " $FILE " | jq)
48+
49+ # Check if there are Vale errors before processing the file further.
50+ if [[ " $vale_json " != " []" ]]; then
51+ echo " Vale errors found in the file..."
52+ # Check if Vale review comments already exist in the PR
53+ pull_comments_json=$( curl -L -H " Accept: application/vnd.github+json" -H " Authorization: Bearer $GITHUB_AUTH_TOKEN " -H " X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/openshift/openshift-docs/pulls/$PULL_NUMBER /comments | jq)
54+ # Check if the response is not empty
55+ if [[ " $pull_comments_json " != " []" ]]; then
56+ get_vale_errors " $vale_json " " $pull_comments_json "
57+ else
58+ echo " No existing comments found..."
59+ updated_vale_json=" $vale_json "
60+ fi
61+ else
62+ echo " No Vale errors found in the file, moving to next file..."
63+ continue # move to next file
64+ fi
65+
66+ # Following logic checks if the line number is a part of the git diff. If it's not part of the diff it will be discarded.
67+ # We only want to check new/modified content, plus the GitHub API only accepts comments within the diff for the review comments endpoint.
68+ if [[ " $updated_vale_json " == " []" ]]; then
69+ echo " All Vale alerts already have existing comments, moving to next file..."
70+ continue # move to next file
71+ else
72+ echo " Checking if Vale alerts with existing comments are part of added or modified content..."
73+ fi
74+
75+ # Iterate through $vale_json and post a comment if required
76+ jq -c ' .[]' <<< " $updated_vale_json" | while IFS= read -r object; do
77+ BODY=$( echo " $object " | jq -r ' .body' )
78+ FILENAME=$( echo " $object " | jq -r ' .path' )
79+ LINE_NUMBER=$( echo " $object " | jq -r ' .line' )
80+
81+ # Check the unified file diff for the alert and file
82+ file_diff=$( git diff --unified=0 --stat --diff-filter=AM HEAD~1 HEAD " ${FILENAME} " ' :(exclude)_unused_topics/*' )
83+
84+ # Iterate through each line to find the line diff info and check if the alert is in the diff
85+ while read -r line; do
86+ # Check if the line contains the hunk beginning with @@
87+ if [[ $line =~ @@ ]]; then
88+
89+ # Valid:
90+ # @@ -35 +31 @@
91+ # @@ -35 +31,5 @@
92+
93+ # Check if there is a comma in the number pairing before @@
94+ if [[ $line =~ \+ .* \, .* \ @@ ]]; then
95+ # There are comma separated numbers before closing @@. Grab the number before the comma as the diff_start_line, after the comma is the added_lines.
96+ added_lines=$( echo " $line " | grep -oP ' \d+\s+@@' | grep -oP ' \d+' )
97+ diff_start_line=$( echo " $line " | awk -F' +' ' {print $2}' | awk -F' ,' ' {print $1}' )
98+ else
99+ # There are no comma seperated numbers. Consider the number after the plus as diff_start_line with no added lines - this means there's a modification on a single line
100+ added_lines=0
101+ diff_start_line=$( echo " $line " | grep -oP ' \+\d+\s+@@' | grep -oP ' \d+' )
102+ fi
103+
104+ # If the last_number is 0, disregard the hunk and move to the next hunk as zero lines were modified (deletions only)
105+ if [ " $diff_start_line " -eq 0 ]; then
106+ continue
107+ fi
108+
109+ # Check if the LINE_NUMBER falls within the range (diff_start_line) to (diff_start_line + added_lines)
110+ if (( LINE_NUMBER >= diff_start_line && LINE_NUMBER <= diff_start_line + added_lines )) ; then
111+
112+ post_review_comment " $BODY " " $FILENAME " " $LINE_NUMBER "
113+
114+ break # Exit the loop since the alert is within the diff, move on to the next JSON object
115+ else
116+ echo " Vale error alert not part of the file's added/modified content..."
117+ fi
118+ fi
119+
120+ done <<< " $file_diff"
121+
122+ done
123+
124+ done
0 commit comments