Skip to content

Commit a12a3dc

Browse files
committed
Streamline release pipeline and add AI release highlights
- Download CI artifacts instead of rebuilding in release workflow - Create GitHub release as draft for review before publishing - Add agentic workflow to auto-generate release highlights summary - Highlights workflow triggers after release, prepends summary to draft Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 3408e06 commit a12a3dc

2 files changed

Lines changed: 202 additions & 28 deletions

File tree

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
---
2+
name: Release Highlights
3+
description: Generate AI-powered release highlights and prepend to draft release
4+
on:
5+
workflow_run:
6+
workflows: ["Release"]
7+
types: [completed]
8+
permissions:
9+
contents: write
10+
pull-requests: read
11+
engine: copilot
12+
timeout-minutes: 10
13+
network:
14+
allowed:
15+
- defaults
16+
safe-outputs:
17+
update-release:
18+
steps:
19+
- name: Setup release data
20+
env:
21+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
22+
run: |
23+
set -e
24+
25+
# Only proceed if the triggering workflow succeeded
26+
if [ "${{ github.event.workflow_run.conclusion }}" != "success" ]; then
27+
echo "Release workflow did not succeed. Skipping."
28+
exit 1
29+
fi
30+
31+
mkdir -p /tmp/release-data
32+
33+
# Find the latest draft release
34+
gh api "repos/${{ github.repository }}/releases" \
35+
--jq '[.[] | select(.draft)] | .[0]' > /tmp/release-data/current_release.json
36+
37+
RELEASE_TAG=$(jq -r '.tag_name' /tmp/release-data/current_release.json)
38+
RELEASE_ID=$(jq -r '.id' /tmp/release-data/current_release.json)
39+
40+
if [ "$RELEASE_TAG" = "null" ] || [ -z "$RELEASE_TAG" ]; then
41+
echo "No draft release found."
42+
exit 1
43+
fi
44+
45+
echo "Draft release: $RELEASE_TAG (ID: $RELEASE_ID)"
46+
echo "RELEASE_TAG=$RELEASE_TAG" >> "$GITHUB_ENV"
47+
echo "RELEASE_ID=$RELEASE_ID" >> "$GITHUB_ENV"
48+
49+
# Find previous non-draft release
50+
PREV_RELEASE_TAG=$(gh api "repos/${{ github.repository }}/releases" \
51+
--jq '[.[] | select(.draft | not)] | .[0].tag_name // empty')
52+
echo "PREV_RELEASE_TAG=$PREV_RELEASE_TAG" >> "$GITHUB_ENV"
53+
54+
if [ -n "$PREV_RELEASE_TAG" ]; then
55+
echo "Previous release: $PREV_RELEASE_TAG"
56+
57+
# Get commits between tags via API
58+
gh api "repos/${{ github.repository }}/compare/${PREV_RELEASE_TAG}...${RELEASE_TAG}" \
59+
--jq '.commits | [.[] | {sha: .sha[:8], message: (.commit.message | split("\n") | .[0]), author: .author.login}]' \
60+
> /tmp/release-data/commits.json
61+
62+
COMMIT_COUNT=$(jq length /tmp/release-data/commits.json)
63+
echo "✓ Found $COMMIT_COUNT commits"
64+
65+
# Get merged PRs between releases
66+
PREV_DATE=$(gh api "repos/${{ github.repository }}/releases/tags/${PREV_RELEASE_TAG}" --jq '.published_at')
67+
CURR_DATE=$(jq -r '.created_at' /tmp/release-data/current_release.json)
68+
69+
gh pr list \
70+
--state merged \
71+
--limit 100 \
72+
--json number,title,author,labels,mergedAt,url \
73+
--jq "[.[] | select(.mergedAt >= \"$PREV_DATE\" and .mergedAt <= \"$CURR_DATE\")]" \
74+
> /tmp/release-data/pull_requests.json
75+
76+
PR_COUNT=$(jq length /tmp/release-data/pull_requests.json)
77+
echo "✓ Found $PR_COUNT pull requests"
78+
else
79+
echo "No previous release found. This is the first release."
80+
echo "[]" > /tmp/release-data/commits.json
81+
echo "[]" > /tmp/release-data/pull_requests.json
82+
fi
83+
84+
echo "✓ Setup complete"
85+
---
86+
87+
# Release Highlights Generator
88+
89+
Generate an engaging release highlights summary for **${{ github.repository }}** release `${RELEASE_TAG}`.
90+
91+
## Data Available
92+
93+
All data is pre-fetched in `/tmp/release-data/`:
94+
- `current_release.json` - Draft release metadata (tag, name, existing body with auto-generated notes)
95+
- `commits.json` - Commits since `${PREV_RELEASE_TAG}` (sha, message, author)
96+
- `pull_requests.json` - Merged PRs between releases (may be empty if changes were direct commits)
97+
98+
## Workflow
99+
100+
### 1. Load Data
101+
102+
```bash
103+
# View release metadata
104+
cat /tmp/release-data/current_release.json | jq '{tag_name, name, created_at}'
105+
106+
# List commits
107+
cat /tmp/release-data/commits.json | jq -r '.[] | "- \(.message) (\(.sha)) by @\(.author)"'
108+
109+
# List PRs (may be empty)
110+
cat /tmp/release-data/pull_requests.json | jq -r '.[] | "- #\(.number): \(.title) by @\(.author.login)"'
111+
```
112+
113+
### 2. Categorize & Prioritize
114+
115+
Group changes by category (omit categories with no items):
116+
- **⚠️ Breaking Changes** - Requires user action (ALWAYS list first if present)
117+
- **✨ New Features** - User-facing capabilities
118+
- **🐛 Bug Fixes** - Issue resolutions
119+
- **⚡ Performance** - Speed/efficiency improvements
120+
- **🔧 Internal** - Refactoring, dependencies (usually omit from highlights)
121+
122+
Use both commit messages and PR titles to determine categories.
123+
Skip internal/refactoring changes unless they have user impact.
124+
125+
### 3. Write Highlights
126+
127+
Structure:
128+
```markdown
129+
## 🌟 Release Highlights
130+
131+
[1-2 sentence summary of the release theme/focus]
132+
133+
### ⚠️ Breaking Changes
134+
[If any - list FIRST with migration guidance]
135+
136+
### ✨ What's New
137+
[Key features with user benefit]
138+
139+
### 🐛 Bug Fixes & Improvements
140+
[Notable fixes - focus on user impact]
141+
```
142+
143+
**Writing Guidelines:**
144+
- Lead with benefits: "Driver deletion is now 2x faster" not "Optimized delete loop"
145+
- Be specific about what changed and why it matters to users
146+
- Keep it concise and scannable (users grasp key changes in 30 seconds)
147+
- Use professional, enthusiastic tone
148+
- This is a Windows desktop application — write from the end-user perspective
149+
150+
### 4. Handle Special Cases
151+
152+
**First Release** (no `${PREV_RELEASE_TAG}`):
153+
```markdown
154+
## 🎉 First Release
155+
Welcome to the inaugural release! This version includes the following capabilities:
156+
[List primary features]
157+
```
158+
159+
**Maintenance Release** (no user-facing changes):
160+
```markdown
161+
## 🔧 Maintenance Release
162+
Dependency updates and internal improvements to keep things running smoothly.
163+
```
164+
165+
### 5. Update Release
166+
167+
**CRITICAL**: You MUST call the `update_release` MCP tool to prepend highlights to the draft release.
168+
169+
**✅ CORRECT - Call the MCP tool directly:**
170+
```
171+
safeoutputs/update_release(
172+
tag="${RELEASE_TAG}",
173+
operation="prepend",
174+
body="## 🌟 Release Highlights\n\n[Your complete markdown highlights here]"
175+
)
176+
```
177+
178+
**❌ INCORRECT - DO NOT:**
179+
- Write JSON files manually
180+
- Use bash to simulate tool calls
181+
- Create scripts that write to outputs
182+
183+
**Important**: If no action is needed after completing your analysis, you **MUST** call the `noop` safe-output tool:
184+
```
185+
safeoutputs/noop(message="No action needed: [brief explanation]")
186+
```

.github/workflows/release.yml

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ on:
1010

1111
permissions:
1212
contents: write
13+
actions: read
1314

1415
env:
1516
SIGNPATH_ORG_ID: 'c9bd44ce-a067-4f9a-9135-468d00ed0b13'
@@ -20,40 +21,26 @@ jobs:
2021
release:
2122
runs-on: windows-latest
2223
steps:
23-
- name: Checkout tag
24-
uses: actions/checkout@v5
25-
with:
26-
ref: v${{ inputs.version }}
27-
28-
- name: Patch assembly version
24+
- name: Download CI artifact
2925
shell: pwsh
26+
env:
27+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3028
run: |
31-
$version = "${{ inputs.version }}"
32-
$file = "Rapr/Properties/AssemblyInfo.cs"
33-
$content = Get-Content $file -Raw
34-
$content = $content -replace 'AssemblyVersion\(".*?"\)', "AssemblyVersion(`"$version`")"
35-
$content = $content -replace 'AssemblyFileVersion\(".*?"\)', "AssemblyFileVersion(`"$version`")"
36-
Set-Content $file $content
37-
Write-Host "Patched AssemblyInfo.cs to version $version"
38-
39-
- name: Setup MSBuild
40-
uses: microsoft/setup-msbuild@v2
41-
42-
- name: Setup NuGet
43-
uses: nuget/setup-nuget@v2
44-
45-
- name: Restore NuGet packages
46-
run: nuget restore Rapr.sln
47-
48-
- name: Build
49-
run: msbuild Rapr.sln /p:Configuration=Release /p:Platform="Any CPU" /v:minimal
29+
$name = "DriverStoreExplorer-v${{ inputs.version }}"
30+
$resp = gh api "repos/${{ github.repository }}/actions/artifacts?name=$name&per_page=1" | ConvertFrom-Json
31+
if ($resp.total_count -eq 0) { throw "Artifact '$name' not found from CI" }
32+
$id = $resp.artifacts[0].id
33+
Write-Host "Found artifact ID: $id"
34+
gh api "repos/${{ github.repository }}/actions/artifacts/$id/zip" > artifact.zip
35+
Expand-Archive artifact.zip -DestinationPath ci-build
36+
Remove-Item artifact.zip
5037
51-
- name: Upload unsigned artifact
38+
- name: Upload unsigned artifact for SignPath
5239
id: upload-unsigned
5340
uses: actions/upload-artifact@v7
5441
with:
5542
name: DriverStoreExplorer-v${{ inputs.version }}-unsigned
56-
path: Rapr/bin/Release/
43+
path: ci-build/
5744

5845
- name: Submit signing request to SignPath
5946
id: signpath
@@ -80,4 +67,5 @@ jobs:
8067
gh release create "v${{ inputs.version }}" `
8168
"DriverStoreExplorer-v${{ inputs.version }}.zip" `
8269
--title "DriverStore Explorer v${{ inputs.version }}" `
83-
--generate-notes
70+
--generate-notes `
71+
--draft

0 commit comments

Comments
 (0)