Skip to content

Commit 0d5805f

Browse files
Update the comment correctly and use ### instead of ## for titles
1 parent 2b7350f commit 0d5805f

File tree

2 files changed

+55
-42
lines changed

2 files changed

+55
-42
lines changed

.github/pull_request_template.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
## Goal
1+
### Goal
22
<!-- What & why in one or two sentences. -->
33

4-
## Implementation
4+
### Implementation
55
- ...
66

7-
## Testing
7+
### Testing
88
<!-- Steps to verify / commands -->
99

10-
## Checklist
10+
### Checklist
1111
- [ ] The title is ready for release notes
1212
- [ ] Issue linked (if any)
1313
- [ ] Tests/docs updated

.github/workflows/pr-quality.yml

Lines changed: 51 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,43 @@
1-
# .github/workflows/pr-quality-check.yml
1+
# .github/workflows/pr-checklist.yml
22
name: PR Quality check
33

4-
# Add a constant anchor we can always find
5-
env:
6-
PR_QUALITY_ANCHOR: "<!-- pr-quality-anchor -->"
7-
84
on:
9-
pull_request:
5+
pull_request_target:
106
types: [opened, edited, synchronize, labeled, unlabeled, reopened]
117

128
permissions:
139
contents: read
1410
pull-requests: write
1511
issues: write
1612

13+
env:
14+
# Hidden marker to always find/update the same comment
15+
PR_CHECKLIST_ANCHOR: "<!-- pr-checklist-anchor -->"
16+
# Title bypass label for release-notes length rule
17+
TITLE_BYPASS_LABEL: "pr:ignore-for-release"
18+
# Title word limits (for release notes)
19+
MIN_TITLE_WORDS: "5"
20+
MAX_TITLE_WORDS: "12"
21+
1722
jobs:
18-
pr-quality:
23+
pr-checklist:
1924
runs-on: ubuntu-latest
2025

2126
steps:
22-
- name: Validate PR quality
27+
- name: Validate PR
2328
id: validate
2429
env:
2530
TITLE: ${{ github.event.pull_request.title }}
2631
BODY: ${{ github.event.pull_request.body }}
2732
LABELS_CSV: ${{ join(github.event.pull_request.labels.*.name, ',') }}
33+
MIN_WORDS: ${{ env.MIN_TITLE_WORDS }}
34+
MAX_WORDS: ${{ env.MAX_TITLE_WORDS }}
35+
BYPASS: ${{ env.TITLE_BYPASS_LABEL }}
2836
run: |
2937
set -euo pipefail
3038
3139
failures=""
3240
33-
# ---- Settings ----
34-
MIN_WORDS=5
35-
MAX_WORDS=12
36-
TITLE_BYPASS_LABEL="pr:ignore-for-release"
37-
3841
has_label () {
3942
case ",${LABELS_CSV}," in
4043
*,"$1",*) return 0 ;;
@@ -51,30 +54,34 @@ jobs:
5154
return 1
5255
}
5356
54-
# --- 1) Title check
55-
if ! has_label "$TITLE_BYPASS_LABEL"; then
56-
title_words=$(echo "$TITLE" | tr -s '[:space:]' ' ' | sed -e 's/^ *//' -e 's/ *$//' | wc -w | xargs)
57-
if [ -z "$title_words" ]; then title_words=0; fi
57+
# ---- 1) Title length (unless bypass label present)
58+
if ! has_label "$BYPASS"; then
59+
title_words=$(echo "$TITLE" | tr -s '[:space:]' ' ' | sed -e 's/^ *//' -e 's/ *$//' | wc -w | xargs || true)
60+
if [ -z "${title_words:-}" ]; then title_words=0; fi
5861
if [ "$title_words" -lt "$MIN_WORDS" ] || [ "$title_words" -gt "$MAX_WORDS" ]; then
59-
failures="${failures}\n- **Title** should be ${MIN_WORDS}–${MAX_WORDS} words for release notes. Current: ${title_words} word(s). (Add \`${TITLE_BYPASS_LABEL}\` to bypass.)"
62+
failures="${failures}\n- **Title** should be concise for release notes: ${MIN_WORDS}–${MAX_WORDS} words. Current: ${title_words}. (Add \`$BYPASS\` to bypass.)"
6063
fi
6164
fi
6265
63-
# --- 2) Has pr:* label
66+
# ---- 2) At least one pr:* label
6467
if ! has_any_pr_label; then
65-
failures="${failures}\n- Missing required label: at least one label starting with \`pr:\`."
68+
failures="${failures}\n- Missing required label: at least one \`pr:\` label (e.g., \`pr:new-feature\`, \`pr:bug\`)."
6669
fi
6770
68-
# --- 3) Sections non-empty
71+
# ---- 3) Required sections (### headers): Goal / Implementation / Testing
72+
BODY="${BODY:-}"
73+
6974
section_nonempty () {
75+
# Extract text under '### <Header>' until next '###' (or end)
7076
local hdr="$1"
7177
local section
72-
section="$(printf "%s" "$BODY" | awk -v h="^##[[:space:]]*$hdr[[:space:]]*$" '
78+
section="$(printf "%s" "$BODY" | awk -v h="^###[[:space:]]*$hdr[[:space:]]*$" '
7379
BEGIN { insec=0 }
7480
$0 ~ h { insec=1; next }
75-
insec && $0 ~ /^##[[:space:]]/ { insec=0 }
81+
insec && $0 ~ /^###[[:space:]]/ { insec=0 }
7682
insec { print }
7783
')"
84+
# Strip HTML comments and whitespace-only lines
7885
section="$(printf "%s" "$section" \
7986
| sed -E 's/<!--(.|\n)*?-->//g' \
8087
| sed -E 's/^[[:space:]]+|[[:space:]]+$//g' \
@@ -84,7 +91,7 @@ jobs:
8491
8592
for hdr in Goal Implementation Testing; do
8693
if ! section_nonempty "$hdr"; then
87-
failures="${failures}\n- Section **${hdr}** is missing or empty."
94+
failures="${failures}\n- Section **${hdr}** is missing or empty (use \`### ${hdr}\` with some content)."
8895
fi
8996
done
9097
@@ -95,19 +102,22 @@ jobs:
95102
printf "%b\n" "$failures"
96103
echo 'EOF'
97104
} >> "$GITHUB_OUTPUT"
105+
# Fail job to block merging if branch protection requires this check
98106
exit 1
99107
else
100108
echo "has_failures=false" >> "$GITHUB_OUTPUT"
101109
fi
102110
103-
- name: Find existing PR Quality Check comment
111+
# Find sticky comment by anchor (no author filter → works for forks)
112+
- name: Find PR Checklist comment
104113
id: find_comment
105114
uses: peter-evans/find-comment@v3
106115
with:
107116
issue-number: ${{ github.event.pull_request.number }}
108-
comment-author: github-actions[bot]
109-
body-includes: ${{ env.PR_QUALITY_ANCHOR }}
117+
body-includes: ${{ env.PR_CHECKLIST_ANCHOR }}
118+
direction: last
110119

120+
# Post or update the sticky comment to ❌ with details
111121
- name: Create or update failure comment
112122
if: failure()
113123
uses: peter-evans/create-or-update-comment@v4
@@ -116,18 +126,21 @@ jobs:
116126
comment-id: ${{ steps.find_comment.outputs.comment-id }}
117127
edit-mode: replace
118128
body: |
119-
<!-- pr-quality-anchor -->
120-
### PR Quality Check ❌ Failed
129+
<!-- pr-checklist-anchor -->
130+
### PR Checklist ❌
121131
122132
The following issues were detected:
123133
124134
${{ steps.validate.outputs.failures }}
125135
126-
**What we check**
127-
1. Title is concise (3–12 words) unless labeled `pr:ignore-for-release`.
128-
2. At least one `pr:` label exists (e.g., `pr:bug`, `pr:new-feature`).
129-
3. Sections `## Goal`, `## Implementation`, and `## Testing` contain content.
136+
**We check**
137+
1. Title is concise (**${{ env.MIN_TITLE_WORDS }}–${{ env.MAX_TITLE_WORDS }} words**) unless labeled `pr:ignore-for-release`.
138+
2. At least one `pr:` label exists.
139+
3. Sections `### Goal`, `### Implementation`, and `### Testing` contain content.
140+
141+
_This comment updates automatically when you edit the PR title/body or labels._
130142
143+
# Flip the same sticky comment to ✅ when everything passes
131144
- name: Create or update success comment
132145
if: success()
133146
uses: peter-evans/create-or-update-comment@v4
@@ -136,12 +149,12 @@ jobs:
136149
comment-id: ${{ steps.find_comment.outputs.comment-id }}
137150
edit-mode: replace
138151
body: |
139-
<!-- pr-quality-anchor -->
140-
### PR Quality Check ✅ Passed
152+
<!-- pr-checklist-anchor -->
153+
### PR Checklist ✅
141154
142155
All required conditions are satisfied:
143-
- Title length is OK (or ignored by label).
156+
- Title length is OK (or ignored via `pr:ignore-for-release`).
144157
- At least one `pr:` label exists.
145-
- Sections `## Goal`, `## Implementation`, and `## Testing` are filled.
158+
- Sections `### Goal`, `### Implementation`, and `### Testing` are filled.
146159
147-
🎉 Great job! This PR is ready for review.
160+
🎉 Looks good!

0 commit comments

Comments
 (0)