Skip to content

Commit e2ced6d

Browse files
authored
ci: Non-blocking bot to remind about missing CHANGELOG entries (hiero-ledger#1734)
Signed-off-by: Parv Ninama <ninamaparv@gmail.com> Signed-off-by: Parv <ninamaparv@gmail.com>
1 parent 37a7742 commit e2ced6d

3 files changed

Lines changed: 130 additions & 6 deletions

File tree

.github/scripts/pr-check-changelog.sh

Lines changed: 127 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/bin/bash
2-
2+
set -euo pipefail
33
# ==============================================================================
44
# Executes When:
55
# - Run by GitHub Actions workflow: .github/workflows/pr-check-changelog.yml
@@ -61,6 +61,7 @@
6161
#
6262
# Dependencies:
6363
# - git (must be able to fetch upstream)
64+
# - jq (required for PR context parsing)
6465
# - grep, sed (standard Linux utilities)
6566
# - CHANGELOG.md (file must exist in the root directory)
6667
#
@@ -76,6 +77,32 @@
7677

7778
CHANGELOG="CHANGELOG.md"
7879

80+
# Ensure jq is available (required for PR context + comments)
81+
command -v jq >/dev/null || {
82+
echo "❌ jq is required but not installed"
83+
exit 1
84+
}
85+
86+
# Validate required environment variables
87+
: "${GITHUB_EVENT_PATH:?GITHUB_EVENT_PATH is not set}"
88+
: "${GITHUB_REPOSITORY:?GITHUB_REPOSITORY is not set}"
89+
90+
# PR Number
91+
PR_NUMBER=$(jq -r '.pull_request.number // empty' "$GITHUB_EVENT_PATH")
92+
if [[ -n "$PR_NUMBER" ]] && ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
93+
echo "❌ Invalid PR_NUMBER: $PR_NUMBER"
94+
exit 1
95+
fi
96+
97+
# GITHUB_TOKEN is only required for PR commenting
98+
if [[ -n "$PR_NUMBER" ]]; then
99+
: "${GITHUB_TOKEN:?GITHUB_TOKEN is required for PR commenting but is not set}"
100+
fi
101+
102+
# Marker
103+
MISSING_MARKER="<!-- changelog-missing-bot -->"
104+
WRONG_SECTION_MARKER="<!-- changelog-wrong-section-bot -->"
105+
79106
# ANSI color codes
80107
RED="\033[31m"
81108
GREEN="\033[32m"
@@ -88,6 +115,57 @@ failed=0
88115
git remote add upstream https://github.com/${GITHUB_REPOSITORY}.git
89116
git fetch upstream main >/dev/null 2>&1
90117

118+
#PR Comment
119+
DRY_RUN="${DRY_RUN:-false}"
120+
post_pr_comment() {
121+
local message="$1"
122+
123+
# Only comment if this is a PR (not workflow_dispatch)
124+
if [[ -z "$PR_NUMBER" ]]; then
125+
echo "ℹ️ No PR_NUMBER set — skipping comment."
126+
return
127+
fi
128+
129+
if [[ "$DRY_RUN" == "true" ]]; then
130+
echo "🔍 [DRY RUN] Would post PR comment on #${PR_NUMBER}"
131+
return
132+
fi
133+
134+
local http_code
135+
http_code=$(curl -sS -o /dev/null -w "%{http_code}" -X POST \
136+
-H "Authorization: Bearer $GITHUB_TOKEN" \
137+
-H "Accept: application/vnd.github+json" \
138+
"https://api.github.com/repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" \
139+
-d "$(jq -n --arg body "$message" '{body: $body}')")
140+
141+
if [[ "$http_code" -lt 200 || "$http_code" -ge 300 ]]; then
142+
echo "⚠️ Failed to post PR comment (HTTP $http_code)"
143+
fi
144+
}
145+
146+
comment_already_exists() {
147+
local marker="$1"
148+
149+
if [[ -z "$PR_NUMBER" ]]; then
150+
return 1
151+
fi
152+
153+
local comments
154+
comments=$(curl -s \
155+
-H "Authorization: Bearer $GITHUB_TOKEN" \
156+
-H "Accept: application/vnd.github+json" \
157+
"https://api.github.com/repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments?per_page=100")
158+
159+
if ! echo "$comments" | jq -e 'type == "array"' >/dev/null 2>&1; then
160+
echo "⚠️ Failed to fetch PR comments — assuming marker absent"
161+
return 1
162+
fi
163+
164+
echo "$comments" | jq -e --arg marker "$marker" '
165+
.[] | select(.body | contains($marker))
166+
' >/dev/null
167+
}
168+
91169
# Get raw diff
92170
raw_diff=$(git diff upstream/main -- "$CHANGELOG")
93171

@@ -119,7 +197,21 @@ done < <(echo "$raw_diff" | grep '^\-' | grep -vE '^(--- |\+\+\+ |@@ )' | sed 's
119197
# 2️⃣b Warn if no added entries
120198
if [[ ${#added_bullets[@]} -eq 0 ]]; then
121199
echo -e "${RED}❌ No new changelog entries detected in this PR.${RESET}"
122-
echo -e "${YELLOW}⚠️ Please add an entry in [UNRELEASED] under the appropriate subheading.${RESET}"
200+
echo -e "${YELLOW}⚠️ Please add an entry in [Unreleased] under the appropriate subheading.${RESET}"
201+
202+
if ! comment_already_exists "$MISSING_MARKER"; then
203+
post_pr_comment "$MISSING_MARKER
204+
👋 **Changelog reminder**
205+
206+
This PR appears to resolve an issue, but no entry was found in **CHANGELOG.md**.
207+
208+
📌 In this repository, all resolved issues — including docs, CI, and workflow changes — are expected to include a changelog entry under **[Unreleased]**, grouped by category (e.g. *Added*, *Fixed*).
209+
210+
If this PR should not require a changelog entry, feel free to clarify in the discussion. Thanks! 🙌"
211+
else
212+
echo "⚠️ Changelog bot comment already exists, skipping"
213+
fi
214+
123215
failed=1
124216
fi
125217

@@ -133,15 +225,17 @@ current_release=""
133225
current_subtitle=""
134226
in_unreleased=0
135227

228+
shopt -s extglob
229+
136230
while IFS= read -r line; do
137231
# Track release sections
138232
if [[ $line =~ ^##\ \[Unreleased\] ]]; then
139233
current_release="Unreleased"
140234
in_unreleased=1
141235
current_subtitle=""
142236
continue
143-
elif [[ $line =~ ^##\ \[.*\] ]]; then
144-
current_release="$line"
237+
elif [[ $line =~ ^#{1,2}\ \[([0-9]+\.[0-9]+\.[0-9]+)\] ]]; then
238+
current_release="${BASH_REMATCH[1]}"
145239
in_unreleased=0
146240
current_subtitle=""
147241
continue
@@ -151,14 +245,18 @@ while IFS= read -r line; do
151245
fi
152246

153247
# Check each added bullet
248+
249+
normalized_line="${line%%+([[:space:]])}"
154250
for added in "${added_bullets[@]}"; do
155-
if [[ "$line" == "$added" ]]; then
251+
normalized_added="${added%%+([[:space:]])}"
252+
253+
if [[ "$normalized_line" == "$normalized_added" ]]; then
156254
if [[ "$in_unreleased" -eq 1 && -n "$current_subtitle" ]]; then
157255
correctly_placed+="$added (placed under $current_subtitle)"$'\n'
158256
elif [[ "$in_unreleased" -eq 1 && -z "$current_subtitle" ]]; then
159257
orphan_entries+="$added (NOT under a subtitle)"$'\n'
160258
elif [[ "$in_unreleased" -eq 0 ]]; then
161-
wrong_release_entries+="$added (added under released version $current_release)"$'\n'
259+
wrong_release_entries+="$added (added under released version [$current_release], expected under [Unreleased])"$'\n'
162260
fi
163261
fi
164262
done
@@ -174,6 +272,29 @@ fi
174272
if [[ -n "$wrong_release_entries" ]]; then
175273
echo -e "${RED}❌ Some changelog entries were added under a released version (should be in [Unreleased]):${RESET}"
176274
echo "$wrong_release_entries"
275+
276+
if ! comment_already_exists "$WRONG_SECTION_MARKER"; then
277+
post_pr_comment "$WRONG_SECTION_MARKER
278+
⚠️ **Changelog placement issue**
279+
280+
Thanks for adding a changelog entry! 🙌
281+
However, one or more entries in this PR were added under a **released version**.
282+
283+
📌 New changelog entries should always go under **[Unreleased]**, grouped beneath an appropriate category (e.g. *Added*, *Fixed*).
284+
285+
Please move the following entries to **[Unreleased]**:
286+
287+
\`\`\`
288+
$wrong_release_entries
289+
\`\`\`
290+
291+
292+
Let us know if you’re unsure where it should live — happy to help!"
293+
294+
else
295+
echo "⚠️ Changelog bot comment already exists, skipping"
296+
fi
297+
177298
failed=1
178299
fi
179300

.github/workflows/pr-check-changelog.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ jobs:
2121
egress-policy: audit
2222

2323
- name: Run local changelog check
24+
env:
25+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2426
run: |
2527
chmod +x .github/scripts/pr-check-changelog.sh
2628
bash .github/scripts/pr-check-changelog.sh

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.
157157

158158
### Changed
159159
- Refactored `setup_client()` in all `examples/query/` files to use `Client.from_env()` for simplified client initialization (#1449)
160+
- Improve the changelog check by posting informative PR comments when entries are missing or placed under a released version. (#1683)
160161
- Updated return of to_bytes function in `src/hiero_sdk_python/transaction/transaction.py`. (#1631)
161162
- Added missing return type `src/hiero_sdk_python/utils/entity_id_helper.py`. (#1622)
162163
- Update `verify_freeze()` to treat only ACCOUNT_FROZEN_FOR_TOKEN as a successful freeze verification (#1515)

0 commit comments

Comments
 (0)