1- # This workflow is triggered on a pull request labeled with 'notify-slack'. It sends a Slack notification
2- # to alert the team that a PR is ready for review.
1+ # This workflow will post to Slack when a PR is labeled with "notify-slack".
2+ # It will notify the appropriate reviewers based on the current UTC hour.
3+ # It also ensures that Slack is not notified multiple times for the same PR.
4+ # The reviewers are defined in a Base64 encoded JSON file stored in a GitHub secret.
5+ # After notifying, it records the notification in the PR comments and securely deletes the reviewers file.
6+ # The workflow is designed to be efficient and secure, ensuring that sensitive information is handled properly.
37
48name : Ready for Review
59
812 types : [labeled]
913
1014permissions :
11- pull-requests : read
15+ pull-requests : write
1216
1317concurrency :
1418 group : readyforreviewci-${{ github.ref }}
@@ -17,16 +21,116 @@ concurrency:
1721jobs :
1822 notify-slack :
1923 if : contains(github.event.pull_request.labels.*.name, 'notify-slack')
20-
2124 runs-on : ubuntu-latest
2225
2326 steps :
24- - name : Send Slack Notification
27+ - name : Check for prior Slack notification comment
28+ id : check
29+ run : |
30+ COMMENTS=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
31+ https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments)
32+
33+ COMMENT_ID=$(echo "$COMMENTS" | jq '.[] | select(.body | contains("<!-- slack-notified -->")) | .id')
34+
35+ if [ -n "$COMMENT_ID" ]; then
36+ echo "Slack already notified."
37+ echo "notified=true" >> $GITHUB_OUTPUT
38+ echo "comment_id=$COMMENT_ID" >> $GITHUB_OUTPUT
39+ else
40+ echo "notified=false" >> $GITHUB_OUTPUT
41+ fi
42+
43+ - name : Exit if already notified
44+ if : steps.check.outputs.notified == 'true'
45+ continue-on-error : true
46+ run : echo "Slack already notified — skipping remaining steps."
47+
48+ - name : Decode reviewer config (Base64)
49+ if : steps.check.outputs.notified == 'false'
50+ id : config
51+ run : |
52+ echo "${{ secrets.REVIEWER_CONFIG_B64 }}" | base64 -d > reviewers.json
53+
54+ - name : Choose Reviewers From Config
55+ if : steps.check.outputs.notified == 'false'
56+ id : pick
57+ run : |
58+ HOUR=$(date -u +'%H')
59+ DAY=$(date -u +%a | tr '[:upper:]' '[:lower:]')
60+ echo "UTC Hour: $HOUR | Day: $DAY"
61+
62+ REVIEWERS=""
63+
64+ for reviewer in $(jq -r 'keys[]' reviewers.json); do
65+ START=$(jq -r ".\"$reviewer\".start" reviewers.json)
66+ END=$(jq -r ".\"$reviewer\".end" reviewers.json)
67+ SLACK_ID=$(jq -r ".\"$reviewer\".slack_id" reviewers.json)
68+
69+ if jq -e ".\"$reviewer\".days" reviewers.json > /dev/null; then
70+ DAYS=$(jq -r ".\"$reviewer\".days[]" reviewers.json | tr '\n' ' ')
71+ [[ "$DAYS" != *"$DAY"* ]] && continue
72+ fi
73+
74+ if [ "$HOUR" -ge "$START" ] && [ "$HOUR" -lt "$END" ]; then
75+ REVIEWERS+="<@$SLACK_ID> "
76+ fi
77+ done
78+
79+ if [ -z "$REVIEWERS" ]; then
80+ REVIEWERS="<!here> _(No reviewers available — notifying team)_"
81+ fi
82+
83+ echo "reviewers=$REVIEWERS" >> $GITHUB_OUTPUT
84+
85+ - name : Notify Slack
86+ if : steps.check.outputs.notified == 'false'
2587 uses : rtCamp/action-slack-notify@v2.3.3
2688 env :
2789 SLACK_WEBHOOK : ${{ secrets.SLACK_PRIVATE_TEAM_WEBHOOK }}
2890 SLACK_USERNAME : " spectromate"
2991 SLACK_ICON_EMOJI : " :robot_panic:"
3092 SLACK_COLOR : " #A020F0"
3193 SLACK_MESSAGE : |
32- :review: *<${{ github.event.pull_request.html_url }}|${{ github.event.pull_request.title }}>* is ready for review!
94+ :review: *<${{ github.event.pull_request.html_url }}|${{ github.event.pull_request.title }}>* is ready for review!
95+ Pinging: ${{ steps.pick.outputs.reviewers }}
96+
97+ - name : Create or Update Slack comment manually
98+ if : steps.check.outputs.notified == 'false'
99+ run : |
100+ TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
101+ BODY="✅ Slack reviewers notified at $TIMESTAMP\n${{ steps.pick.outputs.reviewers }}\n<!-- slack-notified -->"
102+
103+ if [ -n "${{ steps.check.outputs.comment_id }}" ]; then
104+ echo "Updating existing comment..."
105+ curl -s -X PATCH -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
106+ -H "Accept: application/vnd.github.v3+json" \
107+ -d "$(jq -nc --arg body "$BODY" '{body: $body}')" \
108+ https://api.github.com/repos/${{ github.repository }}/issues/comments/${{ steps.check.outputs.comment_id }}
109+ else
110+ echo "Creating new comment..."
111+ curl -s -X POST -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
112+ -H "Accept: application/vnd.github.v3+json" \
113+ -d "$(jq -nc --arg body "$BODY" '{body: $body}')" \
114+ https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments
115+ fi
116+
117+ - name : Securely overwrite and delete reviewers.json
118+ if : always()
119+ run : |
120+ if [ -f reviewers.json ]; then
121+ echo "Shredding reviewers.json"
122+ dd if=/dev/urandom of=reviewers.json bs=1 count=$(stat -c%s reviewers.json) conv=notrunc status=none
123+ rm -f reviewers.json
124+ echo "Secure deletion complete."
125+ else
126+ echo "No reviewers.json file to delete."
127+ fi
128+
129+ - name : Final Job Status Summary
130+ if : always()
131+ run : |
132+ if [ "${{ steps.check.outputs.notified }}" == "true" ]; then
133+ echo "✅ Job completed: Slack was already notified (comment updated if needed)."
134+ else
135+ echo "✅ Job completed: Slack notification was posted successfully."
136+ fi
0 commit comments