Skip to content

Commit c08d2cf

Browse files
doc-2458: notify slack error fix (#9123)
* doc-2458: notify slack error fix * doc-2458: making script run on ready-for-review and label * doc-2458: reverting notify-slack to original until figure out 403 error
1 parent 26fcb1a commit c08d2cf

File tree

1 file changed

+65
-141
lines changed

1 file changed

+65
-141
lines changed
Lines changed: 65 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,21 @@
1-
# This workflow posts to Slack when a PR is labeled with "notify-slack".
2-
# It notifies reviewers based on each reviewer's local-time window from reviewers.json.
3-
# It also prevents multiple Slack notifications for the same PR using a hidden PR comment marker.
4-
#
5-
# SECURITY NOTE:
6-
# - This workflow uses pull_request_target so it can comment on PRs from forks.
7-
# - Do NOT checkout the PR head in this workflow. This file intentionally does not use actions/checkout.
8-
# "Notify once" semantics: we create a marker comment FIRST (lock). If we can't write the marker,
9-
# we refuse to notify to avoid duplicate Slack notifications.
10-
11-
name: Ready for Review Slack Notification
12-
13-
'on':
14-
pull_request_target:
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.
7+
8+
name: Ready for Review
9+
10+
on:
11+
pull_request:
1512
types: [labeled]
1613

1714
permissions:
18-
issues: write
19-
pull-requests: read
15+
pull-requests: write
2016

2117
concurrency:
22-
group: readyforreviewci-pr-${{ github.event.pull_request.number }}
18+
group: readyforreviewci-${{ github.ref }}
2319
cancel-in-progress: true
2420

2521
jobs:
@@ -28,141 +24,68 @@ jobs:
2824
runs-on: ubuntu-latest
2925

3026
steps:
31-
# SECURITY: Intentionally no checkout. Do NOT checkout PR head in pull_request_target workflows.
32-
33-
- name: Decode reviewers.json secret
34-
env:
35-
REVIEWERS_JSON_B64: ${{ secrets.REVIEWER_CONFIG_B64 }}
36-
run: |
37-
echo "::group::Decode reviewer config"
38-
if [ -z "${REVIEWERS_JSON_B64:-}" ]; then
39-
echo "::warning::REVIEWER_CONFIG_B64 secret is empty or not set; no reviewers will be selected."
40-
echo "{}" > reviewers.json
41-
else
42-
echo "$REVIEWERS_JSON_B64" | base64 --decode > reviewers.json || {
43-
echo "::warning::Failed to decode REVIEWER_CONFIG_B64; no reviewers will be selected."
44-
echo "{}" > reviewers.json
45-
}
46-
fi
47-
echo "::endgroup::"
48-
49-
- name: Check if Slack was already notified (marker comment)
27+
- name: Check for prior Slack notification comment
5028
id: check
51-
env:
52-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
53-
MARKER: "<!-- slack-notified -->"
5429
run: |
55-
echo "::group::Check marker comment"
56-
COMMENTS_JSON="$(curl -fsSL -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" "https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments")"
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)
5732
58-
COMMENT_ID="$(echo "$COMMENTS_JSON" | jq -r --arg m "$MARKER" '
59-
[.[] | select(.body // "" | contains($m)) | .id][0] // ""')"
33+
COMMENT_ID=$(echo "$COMMENTS" | jq '.[] | select(.body | contains("<!-- slack-notified -->")) | .id')
6034
6135
if [ -n "$COMMENT_ID" ]; then
62-
echo "notified=true" >> "$GITHUB_OUTPUT"
63-
echo "comment_id=$COMMENT_ID" >> "$GITHUB_OUTPUT"
64-
echo "::notice::Found marker comment id: $COMMENT_ID"
36+
echo "Slack already notified."
37+
echo "notified=true" >> $GITHUB_OUTPUT
38+
echo "comment_id=$COMMENT_ID" >> $GITHUB_OUTPUT
6539
else
66-
echo "notified=false" >> "$GITHUB_OUTPUT"
67-
echo "comment_id=" >> "$GITHUB_OUTPUT"
68-
echo "::notice::No marker comment found."
40+
echo "notified=false" >> $GITHUB_OUTPUT
6941
fi
70-
echo "::endgroup::"
7142
72-
- name: Acquire notify lock (create marker comment if missing)
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)
7349
if: steps.check.outputs.notified == 'false'
74-
id: lock
75-
env:
76-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
77-
MARKER: "<!-- slack-notified -->"
50+
id: config
7851
run: |
79-
echo "::group::Acquire notify lock"
80-
TIMESTAMP="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
81-
BODY=$'🟡 Slack notification locked at '"$TIMESTAMP"$'\n\n'"$MARKER"
82-
83-
RESP="$(curl -fsSL -X POST -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" -d "$(jq -nc --arg body "$BODY" '{body: $body}')" "https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments")"
84-
85-
COMMENT_ID="$(echo "$RESP" | jq -r '.id // ""')"
86-
if [ -z "$COMMENT_ID" ]; then
87-
echo "::error::Failed to create marker comment; refusing to notify to avoid duplicates."
88-
exit 1
89-
fi
90-
91-
echo "comment_id=$COMMENT_ID" >> "$GITHUB_OUTPUT"
92-
echo "::notice::Notify lock acquired with comment id: $COMMENT_ID"
93-
echo "::endgroup::"
52+
echo "${{ secrets.REVIEWER_CONFIG_B64 }}" | base64 -d > reviewers.json
9453
9554
- name: Choose Reviewers From Config
96-
if: steps.lock.outputs.comment_id != ''
55+
if: steps.check.outputs.notified == 'false'
9756
id: pick
9857
run: |
99-
echo "::group::Select reviewers"
100-
to_minutes () {
101-
local t="$1"
102-
if [[ "$t" == *:* ]]; then
103-
local hh="${t%%:*}"
104-
local mm="${t##*:}"
105-
echo $((10#$hh * 60 + 10#$mm))
106-
else
107-
# Back-compat: integer hour => HH:00
108-
echo $((10#$t * 60))
109-
fi
110-
}
111-
112-
now_in_tz () {
113-
local tz="$1"
114-
local hh mm day
115-
hh=$(TZ="$tz" date +%H)
116-
mm=$(TZ="$tz" date +%M)
117-
day=$(TZ="$tz" date +%a | tr '[:upper:]' '[:lower:]')
118-
echo "$((10#$hh * 60 + 10#$mm)) $day $hh $mm"
119-
}
58+
HOUR=$(date -u +'%H')
59+
DAY=$(date -u +%a | tr '[:upper:]' '[:lower:]')
60+
echo "UTC Hour: $HOUR | Day: $DAY"
12061
12162
REVIEWERS=""
12263
123-
for reviewer in $(jq -r 'keys[]' reviewers.json 2>/dev/null || true); do
124-
START_RAW=$(jq -r "."$reviewer".start" reviewers.json 2>/dev/null || echo "")
125-
END_RAW=$(jq -r "."$reviewer".end" reviewers.json 2>/dev/null || echo "")
126-
SLACK_ID=$(jq -r "."$reviewer".slack_id" reviewers.json 2>/dev/null || echo "")
127-
TZID=$(jq -r "."$reviewer".tz // "UTC"" reviewers.json 2>/dev/null || echo "UTC")
128-
129-
if [ -z "$START_RAW" ] || [ -z "$END_RAW" ] || [ -z "$SLACK_ID" ]; then
130-
echo "::warning::Reviewer '$reviewer' missing required fields (start/end/slack_id); skipping."
131-
continue
132-
fi
133-
134-
read -r NOW_MIN DAY HH MM <<< "$(now_in_tz "$TZID")"
135-
echo "Reviewer: $reviewer | TZ: $TZID | Local: ${HH}:${MM} | Day: $DAY"
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)
13668
137-
if jq -e "."$reviewer".days" reviewers.json > /dev/null 2>&1; then
138-
DAYS=$(jq -r "."$reviewer".days[]" reviewers.json 2>/dev/null | tr '\n' ' ')
69+
if jq -e ".\"$reviewer\".days" reviewers.json > /dev/null; then
70+
DAYS=$(jq -r ".\"$reviewer\".days[]" reviewers.json | tr '\n' ' ')
13971
[[ "$DAYS" != *"$DAY"* ]] && continue
14072
fi
14173
142-
START_MIN=$(to_minutes "$START_RAW")
143-
END_MIN=$(to_minutes "$END_RAW")
144-
145-
if [ "$NOW_MIN" -ge "$START_MIN" ] && [ "$NOW_MIN" -lt "$END_MIN" ]; then
74+
if [ "$HOUR" -ge "$START" ] && [ "$HOUR" -lt "$END" ]; then
14675
REVIEWERS+="<@$SLACK_ID> "
14776
fi
14877
done
14978
15079
if [ -z "$REVIEWERS" ]; then
15180
REVIEWERS="<!here> _(No reviewers available — notifying team)_"
152-
echo "::notice::No reviewers matched availability window; using <!here>."
153-
else
154-
echo "::notice::Selected reviewers: $REVIEWERS"
15581
fi
15682
157-
echo "reviewers=$REVIEWERS" >> "$GITHUB_OUTPUT"
158-
echo "::endgroup::"
83+
echo "reviewers=$REVIEWERS" >> $GITHUB_OUTPUT
15984
16085
- name: Notify Slack
161-
if: steps.lock.outputs.comment_id != ''
162-
continue-on-error: true
86+
if: steps.check.outputs.notified == 'false'
16387
uses: rtCamp/[email protected]
16488
env:
165-
REVIEWERS: ${{ steps.pick.outputs.reviewers }}
16689
SLACK_WEBHOOK: ${{ secrets.SLACK_PRIVATE_TEAM_WEBHOOK }}
16790
SLACK_USERNAME: "spectromate"
16891
SLACK_ICON_EMOJI: ":robot_panic:"
@@ -171,42 +94,43 @@ jobs:
17194
:review: *<${{ github.event.pull_request.html_url }}|${{ github.event.pull_request.title }}>* is ready for review!
17295
Pinging: ${{ steps.pick.outputs.reviewers }}
17396
174-
- name: Finalize marker comment
175-
if: steps.lock.outputs.comment_id != ''
176-
continue-on-error: true
177-
env:
178-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
179-
MARKER: "<!-- slack-notified -->"
97+
- name: Create or Update Slack comment manually
98+
if: steps.check.outputs.notified == 'false'
18099
run: |
181-
echo "::group::Finalize marker comment"
182-
TIMESTAMP="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
183-
BODY=$'✅ Slack notification attempted at '"$TIMESTAMP"$'\n\n'"$MARKER"
184-
COMMENT_ID="${{ steps.lock.outputs.comment_id }}"
185-
186-
curl -fsSL -X PATCH -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" -d "$(jq -nc --arg body "$BODY" '{body: $body}')" "https://api.github.com/repos/${{ github.repository }}/issues/comments/$COMMENT_ID" > /dev/null || echo "::warning::Failed to finalize marker comment ($COMMENT_ID)"
187-
echo "::endgroup::"
100+
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
101+
BODY="✅ Slack reviewers notified at $TIMESTAMP \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
188116
189117
- name: Securely overwrite and delete reviewers.json
190118
if: always()
191119
run: |
192-
echo "::group::Cleanup"
193120
if [ -f reviewers.json ]; then
194121
echo "Shredding reviewers.json"
195-
dd if=/dev/urandom of=reviewers.json bs=1 count=$(stat -c%s reviewers.json) conv=notrunc status=none || true
196-
rm -f reviewers.json || true
197-
echo "Cleanup complete."
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."
198125
else
199126
echo "No reviewers.json file to delete."
200127
fi
201-
echo "::endgroup::"
202128
203129
- name: Final Job Status Summary
204130
if: always()
205131
run: |
206132
if [ "${{ steps.check.outputs.notified }}" == "true" ]; then
207-
echo "✅ Job completed: Slack was already notified (marker comment exists)."
208-
elif [ -n "${{ steps.lock.outputs.comment_id }}" ]; then
209-
echo "✅ Job completed: Notify lock acquired; Slack notification attempted."
133+
echo "✅ Job completed: Slack was already notified (comment updated if needed)."
210134
else
211-
echo "ℹ️ Job completed: No lock acquired (likely already notified or skipped)."
135+
echo " Job completed: Slack notification was posted successfully."
212136
fi

0 commit comments

Comments
 (0)