--- /dev/null
+# Release Notes Generation - Channel-Specific Behavior
+
+## Overview
+
+The release workflow generates release notes **specific to each channel** by only including commits since the last release of that same channel. It uses your **Release Drafter configuration** (`.github/release-drafter.yml`) for label-based categorization.
+
+## How It Works
+
+### 1. Previous Release Detection
+
+The workflow identifies the previous release for each channel using tag patterns:
+
+#### Stable Channel
+- **Pattern**: `^[0-9]+\.[0-9]+\.[0-9]+$` (e.g., `2.1.0`, `2.0.5`)
+- **Branch**: `stable`
+- **Finds**: Latest stable release (no suffix)
+
+#### Beta Channel
+- **Pattern**: `^[0-9]+\.[0-9]+\.[0-9]+\.b[0-9]+$` (e.g., `2.1.0.b1`, `2.1.0.b2`)
+- **Branch**: `dev`
+- **Finds**: Latest beta release (`.bN` suffix)
+
+#### Nightly Channel
+- **Pattern**: `^[0-9]+\.[0-9]+\.[0-9]+\.dev[0-9]+$` (e.g., `2.1.0.dev20251023`)
+- **Branch**: `dev`
+- **Finds**: Latest nightly release (`.devYYYYMMDD` suffix)
+
+### 2. Release Notes Generation
+
+The workflow generates notes in three steps:
+
+1. **Find PRs in commit range**: Extracts PR numbers from merge commits between the previous tag and HEAD
+2. **Categorize by labels**: Applies the category rules from `.github/release-drafter.yml`:
+ - ⚠ Breaking Changes (`breaking-change` label)
+ - 🚀 New Providers (`new-provider` label)
+ - 🚀 Features and enhancements (`feature`, `enhancement`, `new-feature` labels)
+ - 🐛 Bugfixes (`bugfix` label)
+ - 🧰 Maintenance (`ci`, `documentation`, `maintenance`, `dependencies` labels)
+3. **Add contributors**: Lists all unique contributors from the PRs
+
+### 3. What This Means
+
+#### ✅ Stable Release Notes
+- Include **only commits since the last stable release**
+- **Do NOT include** beta or nightly commits that happened in between
+- Example: `2.0.5` → `2.1.0` only shows stable branch commits
+
+#### ✅ Beta Release Notes
+- Include **only commits since the last beta release**
+- **Do NOT include** nightly commits
+- **Do NOT include** stable commits from stable branch
+- Example: `2.1.0.b2` → `2.1.0.b3` only shows dev branch commits since b2
+
+#### ✅ Nightly Release Notes
+- Include **only commits since the last nightly release**
+- **Do NOT include** beta or stable releases in between
+- Example: `2.1.0.dev20251022` → `2.1.0.dev20251023` only shows dev branch commits since yesterday
+
+## Release Drafter Configuration
+
+✅ The workflow **uses your `.github/release-drafter.yml` configuration** for:
+- Category definitions (labels → section headers)
+- Category titles and emoji
+- Excluded contributors (bots)
+- PR title format
+
+The workflow manually implements the categorization logic to ensure channel-specific commit ranges while preserving your custom formatting.
+
+## Example Release Notes Format
+
+```markdown
+## 📦 Beta Release
+
+_Changes since [2.1.0.b1](https://github.com/music-assistant/server/releases/tag/2.1.0.b1)_
+
+### ⚠ Breaking Changes
+
+- Major API refactoring (by @contributor1 in #123)
+
+### 🚀 Features and enhancements
+
+- Add new audio processor (by @contributor2 in #124)
+- Improve queue management (by @contributor3 in #125)
+
+### 🐛 Bugfixes
+
+- Fix playback issue (by @contributor1 in #126)
+
+### 🧰 Maintenance and dependency bumps
+
+- Update dependencies (by @dependabot in #127)
+- Improve CI pipeline (by @contributor2 in #128)
+
+## :bow: Thanks to our contributors
+
+Special thanks to the following contributors who helped with this release:
+
+@contributor1, @contributor2, @contributor3
+```
+
+## Testing
+
+To verify channel-specific release notes:
+
+1. **Create a beta release** after a stable release:
+ ```bash
+ # Should only show commits on dev branch since last beta
+ # Should NOT include stable branch commits
+ ```
+
+2. **Create a nightly release** after a beta release:
+ ```bash
+ # Should only show commits since yesterday's nightly
+ # Should NOT include beta release notes
+ ```
+
+3. **Create a stable release** after multiple betas:
+ ```bash
+ # Should only show commits on stable branch since last stable
+ # Should NOT include any beta or nightly commits
+ ```
+
+## Verification Commands
+
+```bash
+# Check what will be in next stable release
+git log $(git tag | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -1)..stable --oneline
+
+# Check what will be in next beta release
+git log $(git tag | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.b[0-9]+$' | sort -V | tail -1)..dev --oneline
+
+# Check what will be in next nightly release
+git log $(git tag | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.dev[0-9]+$' | sort -V | tail -1)..dev --oneline
+```
+
+## Testing
+
+To verify channel-specific release notes:
+
+1. **Create a beta release** after a stable release:
+ ```bash
+ # Should only show commits on dev branch since last beta
+ # Should NOT include stable branch commits
+ ```
+
+2. **Create a nightly release** after a beta release:
+ ```bash
+ # Should only show commits since yesterday's nightly
+ # Should NOT include beta release notes
+ ```
+
+3. **Create a stable release** after multiple betas:
+ ```bash
+ # Should only show commits on stable branch since last stable
+ # Should NOT include any beta or nightly commits
+ ```
+
+## Verification Commands
+
+```bash
+# Check what will be in next stable release
+git log $(git tag | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -1)..stable --oneline
+
+# Check what will be in next beta release
+git log $(git tag | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.b[0-9]+$' | sort -V | tail -1)..dev --oneline
+
+# Check what will be in next nightly release
+git log $(git tag | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.dev[0-9]+$' | sort -V | tail -1)..dev --oneline
+```
name: release-dists
path: dist/
- - name: Determine previous version tag
+ - name: Determine previous version tag and temporarily hide other channels
id: prev_version
run: |
CHANNEL="${{ inputs.channel }}"
+ # Define patterns for each channel
+ case "$CHANNEL" in
+ stable)
+ KEEP_PATTERN='^[0-9]+\.[0-9]+\.[0-9]+$'
+ HIDE_PATTERN='\.(b|dev)[0-9]+'
+ ;;
+ beta)
+ KEEP_PATTERN='^[0-9]+\.[0-9]+\.[0-9]+\.b[0-9]+$'
+ HIDE_PATTERN='(^[0-9]+\.[0-9]+\.[0-9]+$|\.dev[0-9]+$)'
+ ;;
+ nightly)
+ KEEP_PATTERN='^[0-9]+\.[0-9]+\.[0-9]+\.dev[0-9]+$'
+ HIDE_PATTERN='(^[0-9]+\.[0-9]+\.[0-9]+$|\.b[0-9]+$)'
+ ;;
+ esac
+
+ echo "📋 Hiding releases from other channels for Release Drafter..."
+
+ # Get all releases and temporarily convert other channels to draft
+ gh release list --limit 100 --json tagName,isDraft,isPrerelease | jq -c '.[]' | while read -r release; do
+ TAG=$(echo "$release" | jq -r '.tagName')
+ IS_DRAFT=$(echo "$release" | jq -r '.isDraft')
+
+ # Skip if already a draft
+ if [ "$IS_DRAFT" = "true" ]; then
+ continue
+ fi
+
+ # Check if this release should be hidden (not matching current channel pattern)
+ if echo "$TAG" | grep -vE "$KEEP_PATTERN" > /dev/null 2>&1; then
+ echo " Temporarily hiding: $TAG"
+ # Store this tag so we can restore it later
+ echo "$TAG" >> /tmp/hidden_releases.txt
+ # Mark as draft (makes it invisible to Release Drafter)
+ gh release edit "$TAG" --draft=true
+ fi
+ done
+
+ # Now find the previous tag of this channel
case "$CHANNEL" in
stable)
- # For stable, find latest stable tag (no beta or dev suffix)
PREV_TAG=$(git tag --sort=-version:refname | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1)
;;
beta)
- # For beta, find latest beta tag
PREV_TAG=$(git tag --sort=-version:refname | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.b[0-9]+$' | head -n 1)
;;
nightly)
- # For nightly, find latest nightly tag
PREV_TAG=$(git tag --sort=-version:refname | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.dev[0-9]+$' | head -n 1)
;;
esac
if [ -z "$PREV_TAG" ]; then
- echo "No previous $CHANNEL release found, using all commits"
+ echo "⚠️ No previous $CHANNEL release found"
echo "prev_tag=" >> $GITHUB_OUTPUT
+ echo "has_prev_tag=false" >> $GITHUB_OUTPUT
else
- echo "Previous $CHANNEL release: $PREV_TAG"
+ echo "✅ Previous $CHANNEL release: $PREV_TAG"
echo "prev_tag=$PREV_TAG" >> $GITHUB_OUTPUT
+ echo "has_prev_tag=true" >> $GITHUB_OUTPUT
fi
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create Release with Release Drafter
id: create_release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ - name: Restore hidden releases
+ if: always()
+ run: |
+ if [ -f /tmp/hidden_releases.txt ]; then
+ echo "📋 Restoring hidden releases..."
+ while read -r TAG; do
+ if [ -n "$TAG" ]; then
+ echo " Restoring: $TAG"
+ gh release edit "$TAG" --draft=false
+ fi
+ done < /tmp/hidden_releases.txt
+ echo "✅ All releases restored"
+ else
+ echo "No releases to restore"
+ fi
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Add channel context to release notes
+ if: steps.prev_version.outputs.has_prev_tag == 'true'
+ run: |
+ PREV_TAG="${{ steps.prev_version.outputs.prev_tag }}"
+ CHANNEL="${{ inputs.channel }}"
+ VERSION="${{ inputs.version }}"
+ REPO="${{ github.repository }}"
+
+ # Get current release notes from Release Drafter
+ gh release view "$VERSION" --json body --jq .body > /tmp/current_notes.md
+
+ # Prepend channel context
+ echo "## 📦 ${CHANNEL^} Release" > /tmp/new_notes.md
+ echo "" >> /tmp/new_notes.md
+ echo "_Changes since [$PREV_TAG](https://github.com/$REPO/releases/tag/$PREV_TAG)_" >> /tmp/new_notes.md
+ echo "" >> /tmp/new_notes.md
+ echo "___" >> /tmp/new_notes.md
+ echo "" >> /tmp/new_notes.md
+ cat /tmp/current_notes.md >> /tmp/new_notes.md
+
+ # Update the release
+ gh release edit "$VERSION" --notes-file /tmp/new_notes.md
+
+ echo "✅ Added channel context to release notes"
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
- name: Extract and append frontend changes to release notes
id: update_notes
env:
if [ -n "$pr_json" ]; then
BODY=$(echo "$pr_json" | jq -r '.body')
- # Extract bullet points from the body, excluding section headers and contributors
- echo "$BODY" | grep -E '^[[:space:]]*[•-]' | grep -v '🙇' | head -20 >> "$FRONTEND_FILE" || true
+ # Extract bullet points from the body, excluding:
+ # - Section headers (🙇)
+ # - Dependabot dependency lines (starting with "Chore(deps")
+ echo "$BODY" | grep -E '^[[:space:]]*[•-]' | \
+ grep -v '🙇' | \
+ grep -viE '^[[:space:]]*[•-][[:space:]]*Chore\(deps' | \
+ head -20 >> "$FRONTEND_FILE" || true
# Extract contributors from frontend PR body
echo "$BODY" | grep -oP '@[a-zA-Z0-9_-]+' >> "$CONTRIBUTORS_FILE" || true