Skip to content

Commit 8b729cf

Browse files
authored
feat: add PR/Issue links to release notes (#420)
## Summary Adds links to PRs and Issues in each release's notes, making it easy for users to see what changed. ### Changes 1. **Workflow update**: The `notify-released` job now adds an "Included in this release" section with: - Links to all merged PRs labeled with `released:vX.Y.Z` - Links to all closed Issues labeled with `released:vX.Y.Z` 2. **Backfill script**: `scripts/update-release-notes.sh` updates past release notes ### Example output in release notes ```markdown --- ## Included in this release ### Pull Requests - [mcuadros#408](netresearch#408) feat: secure web auth - [mcuadros#409](netresearch#409) feat: ntfy-token preset ### Issues - [mcuadros#407](netresearch#407) Add token auth for ntfy ``` ## Test Plan - [x] Dry-run script tested - [ ] Run update script after merge to backfill all releases
2 parents d460498 + a5be442 commit 8b729cf

File tree

2 files changed

+180
-1
lines changed

2 files changed

+180
-1
lines changed

.github/workflows/release-slsa.yml

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,9 +333,10 @@ VERIFICATION_EOF
333333
# Notify PRs and issues included in this release
334334
notify-released:
335335
name: Notify Released PRs/Issues
336-
needs: [resolve-tag, docker]
336+
needs: [resolve-tag, docker, release-notes]
337337
runs-on: ubuntu-latest
338338
permissions:
339+
contents: write
339340
issues: write
340341
pull-requests: write
341342
env:
@@ -432,3 +433,47 @@ ISSUE_EOF
432433
done
433434

434435
echo "Notification complete for ${RELEASE_TAG}"
436+
437+
- name: Update release notes with PR/Issue links
438+
env:
439+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
440+
run: |
441+
set -euo pipefail
442+
443+
# Get existing release notes
444+
gh release view "$RELEASE_TAG" --json body --jq '.body' > /tmp/notes.md
445+
446+
# Check if "Included in this release" section already exists
447+
if grep -q "## Included in this release" /tmp/notes.md; then
448+
echo "PR/Issue links section already exists, skipping"
449+
exit 0
450+
fi
451+
452+
# Query all PRs with this release label
453+
echo "" >> /tmp/notes.md
454+
echo "---" >> /tmp/notes.md
455+
echo "" >> /tmp/notes.md
456+
echo "## Included in this release" >> /tmp/notes.md
457+
echo "" >> /tmp/notes.md
458+
459+
# Get PRs with this release label
460+
PRS=$(gh pr list --state merged --label "released:${RELEASE_TAG}" --json number,title --jq '.[] | "- [#\(.number)](${{ github.server_url }}/${{ github.repository }}/pull/\(.number)) \(.title)"' 2>/dev/null || echo "")
461+
if [[ -n "$PRS" ]]; then
462+
echo "### Pull Requests" >> /tmp/notes.md
463+
echo "" >> /tmp/notes.md
464+
echo "$PRS" >> /tmp/notes.md
465+
echo "" >> /tmp/notes.md
466+
fi
467+
468+
# Get Issues with this release label
469+
ISSUES=$(gh issue list --state closed --label "released:${RELEASE_TAG}" --json number,title --jq '.[] | "- [#\(.number)](${{ github.server_url }}/${{ github.repository }}/issues/\(.number)) \(.title)"' 2>/dev/null || echo "")
470+
if [[ -n "$ISSUES" ]]; then
471+
echo "### Issues" >> /tmp/notes.md
472+
echo "" >> /tmp/notes.md
473+
echo "$ISSUES" >> /tmp/notes.md
474+
echo "" >> /tmp/notes.md
475+
fi
476+
477+
# Update release notes
478+
gh release edit "$RELEASE_TAG" --notes-file /tmp/notes.md
479+
echo "Updated release notes with PR/Issue links"

scripts/update-release-notes.sh

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Update release notes with links to PRs/Issues included in each release
4+
#
5+
# Usage:
6+
# ./scripts/update-release-notes.sh --dry-run # Preview changes
7+
# ./scripts/update-release-notes.sh # Apply changes
8+
#
9+
set -euo pipefail
10+
11+
DRY_RUN="${1:-}"
12+
REPO="netresearch/ofelia"
13+
14+
# Colors for output
15+
RED='\033[0;31m'
16+
GREEN='\033[0;32m'
17+
YELLOW='\033[1;33m'
18+
BLUE='\033[0;34m'
19+
NC='\033[0m' # No Color
20+
21+
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
22+
log_success() { echo -e "${GREEN}[OK]${NC} $1"; }
23+
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
24+
log_dry() { echo -e "${YELLOW}[DRY-RUN]${NC} $1"; }
25+
26+
# Update release notes for a single release
27+
update_release_notes() {
28+
local tag="$1"
29+
local label="released:${tag}"
30+
31+
log_info "Processing release notes for ${tag}"
32+
33+
# Get existing release notes
34+
local notes
35+
notes=$(gh release view "$tag" --repo "$REPO" --json body --jq '.body' 2>/dev/null || echo "")
36+
37+
if [[ -z "$notes" ]]; then
38+
log_warn "No release notes found for ${tag}, skipping"
39+
return 0
40+
fi
41+
42+
# Check if "Included in this release" section already exists
43+
if echo "$notes" | grep -q "## Included in this release"; then
44+
log_info "PR/Issue links section already exists for ${tag}, skipping"
45+
return 0
46+
fi
47+
48+
# Get PRs with this release label
49+
local prs
50+
prs=$(gh pr list --state merged --label "$label" --repo "$REPO" \
51+
--json number,title \
52+
--jq '.[] | "- [#\(.number)](https://github.com/'"$REPO"'/pull/\(.number)) \(.title)"' 2>/dev/null || echo "")
53+
54+
# Get Issues with this release label
55+
local issues
56+
issues=$(gh issue list --state closed --label "$label" --repo "$REPO" \
57+
--json number,title \
58+
--jq '.[] | "- [#\(.number)](https://github.com/'"$REPO"'/issues/\(.number)) \(.title)"' 2>/dev/null || echo "")
59+
60+
# Skip if no PRs or issues found
61+
if [[ -z "$prs" && -z "$issues" ]]; then
62+
log_warn "No PRs or Issues found with label ${label}, skipping"
63+
return 0
64+
fi
65+
66+
# Build the new section
67+
local new_section=""
68+
new_section+="\n---\n\n## Included in this release\n\n"
69+
70+
if [[ -n "$prs" ]]; then
71+
new_section+="### Pull Requests\n\n${prs}\n\n"
72+
fi
73+
74+
if [[ -n "$issues" ]]; then
75+
new_section+="### Issues\n\n${issues}\n\n"
76+
fi
77+
78+
if [[ "$DRY_RUN" == "--dry-run" ]]; then
79+
log_dry "Would update release notes for ${tag}:"
80+
echo -e "$new_section"
81+
else
82+
# Write updated notes to temp file
83+
echo -e "${notes}${new_section}" > /tmp/release_notes.md
84+
gh release edit "$tag" --repo "$REPO" --notes-file /tmp/release_notes.md
85+
log_success "Updated release notes for ${tag}"
86+
fi
87+
}
88+
89+
# Main
90+
main() {
91+
if [[ "$DRY_RUN" == "--dry-run" ]]; then
92+
echo -e "${YELLOW}╔═══════════════════════════════════════════════════════════════╗${NC}"
93+
echo -e "${YELLOW}║ DRY RUN MODE ║${NC}"
94+
echo -e "${YELLOW}║ No changes will be made to GitHub ║${NC}"
95+
echo -e "${YELLOW}╚═══════════════════════════════════════════════════════════════╝${NC}"
96+
else
97+
echo -e "${RED}╔═══════════════════════════════════════════════════════════════╗${NC}"
98+
echo -e "${RED}║ LIVE MODE ║${NC}"
99+
echo -e "${RED}║ Changes WILL be made to GitHub releases ║${NC}"
100+
echo -e "${RED}╚═══════════════════════════════════════════════════════════════╝${NC}"
101+
echo ""
102+
read -p "Are you sure you want to proceed? (yes/no): " confirm
103+
if [[ "$confirm" != "yes" ]]; then
104+
echo "Aborted."
105+
exit 1
106+
fi
107+
fi
108+
109+
# Get all releases sorted by date (oldest first)
110+
local releases
111+
releases=$(gh release list --repo "$REPO" --json tagName,publishedAt \
112+
--jq 'sort_by(.publishedAt) | .[].tagName')
113+
114+
# Process each release starting from v0.7.0
115+
local start_processing=false
116+
117+
for tag in $releases; do
118+
# Start processing from v0.7.0
119+
if [[ "$tag" == "v0.7.0" ]]; then
120+
start_processing=true
121+
fi
122+
123+
if [[ "$start_processing" == true ]]; then
124+
update_release_notes "$tag"
125+
fi
126+
done
127+
128+
echo ""
129+
echo -e "${GREEN}═══════════════════════════════════════════════════════════════${NC}"
130+
echo -e "${GREEN}Release notes update complete!${NC}"
131+
echo -e "${GREEN}═══════════════════════════════════════════════════════════════${NC}"
132+
}
133+
134+
main "$@"

0 commit comments

Comments
 (0)