Skip to content

Publish releases

Publish releases #69

Workflow file for this run

name: Publish releases
# Creates a GitHub release, builds and publishes to PyPI, and creates a PR in the server repo
# Can be triggered manually with a version number or called by the auto-release workflow
on:
workflow_dispatch:
inputs:
version:
description: "Version number (e.g., 2.16.7) - Creates release, publishes to PyPI, and updates server repo"
required: true
type: string
workflow_call:
inputs:
version:
description: "Version number (e.g., 2.16.7)"
required: true
type: string
secrets:
PYPI_TOKEN:
required: true
PRIVILEGED_GITHUB_TOKEN:
required: true
env:
PYTHON_VERSION: "3.13"
jobs:
test:
name: Run Tests
uses: ./.github/workflows/test.yml
create-release:
name: Create Release
needs: test
runs-on: ubuntu-latest
outputs:
version: ${{ inputs.version }}
steps:
- name: Checkout the repository
uses: actions/checkout@v5
- name: Create and push tag
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "${{ inputs.version }}" -m "Release ${{ inputs.version }}"
git push origin "${{ inputs.version }}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Generate release notes with Release Drafter
id: release_drafter
uses: release-drafter/[email protected]
with:
config-name: release-drafter.yml
version: ${{ inputs.version }}
tag: ${{ inputs.version }}
publish: true
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Check if release notes are empty and add commit log fallback
run: |
RELEASE_BODY=$(gh release view "${{ inputs.version }}" --json body -q .body)
# Check if release body is empty or just says "No changes"
if [[ -z "$RELEASE_BODY" ]] || [[ "$RELEASE_BODY" =~ "No changes" ]]; then
echo "Release notes are empty or contain 'No changes'. Generating commit log..."
# Get the previous tag
PREV_TAG=$(git describe --tags --abbrev=0 "${{ inputs.version }}^" 2>/dev/null || echo "")
if [[ -z "$PREV_TAG" ]]; then
# If no previous tag, get all commits
COMMIT_LOG=$(git log --pretty=format:"* %s (%h)" "${{ inputs.version }}")
else
# Get commits between previous tag and current tag
COMMIT_LOG=$(git log --pretty=format:"* %s (%h)" "$PREV_TAG..${{ inputs.version }}")
fi
# Update release notes with commit log
echo "## Changes" > /tmp/release_notes.md
echo "" >> /tmp/release_notes.md
echo "$COMMIT_LOG" >> /tmp/release_notes.md
gh release edit "${{ inputs.version }}" --notes-file /tmp/release_notes.md
echo "Release notes updated with commit log."
else
echo "Release notes already contain content from Release Drafter."
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
build-and-publish:
name: Build and Publish
needs: create-release
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v5
with:
ref: ${{ inputs.version }}
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v6
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install build dependencies
run: >-
pip install build tomli tomli-w
- name: Set Python project version
shell: python
run: |-
import tomli
import tomli_w
with open("pyproject.toml", "rb") as f:
pyproject = tomli.load(f)
pyproject["project"]["version"] = "${{ inputs.version }}"
with open("pyproject.toml", "wb") as f:
tomli_w.dump(pyproject, f)
- name: Build package
run: |
python3 -m build
- name: Publish release to PyPI
uses: pypa/[email protected]
with:
user: __token__
password: ${{ secrets.PYPI_TOKEN }}
- name: Upload release assets
run: |
gh release upload "${{ inputs.version }}" dist/*.whl dist/*.tar.gz
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
server-repo-pr:
name: Server repo PR
needs: build-and-publish
runs-on: ubuntu-latest
steps:
- name: Wait for PyPI availability
run: |
VERSION="${{ inputs.version }}"
echo "Waiting for music-assistant-models==$VERSION to be available on PyPI..."
# Wait up to 5 minutes (30 attempts * 10 seconds)
MAX_ATTEMPTS=30
ATTEMPT=1
while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do
echo "Attempt $ATTEMPT/$MAX_ATTEMPTS: Checking PyPI..."
# Check if the package version is available on PyPI
if pip index versions music-assistant-models 2>/dev/null | grep -q "$VERSION"; then
echo "✅ Package music-assistant-models==$VERSION is now available on PyPI!"
exit 0
fi
if [ $ATTEMPT -lt $MAX_ATTEMPTS ]; then
echo "Package not yet available, waiting 10 seconds..."
sleep 10
fi
ATTEMPT=$((ATTEMPT + 1))
done
echo "⚠️ Warning: Package not found on PyPI after 5 minutes, proceeding anyway..."
exit 0
- name: Get release notes
id: release_notes
run: |
VERSION="${{ inputs.version }}"
# Get the release notes from the models release
RELEASE_DATA=$(gh release view "$VERSION" --repo music-assistant/models --json body)
RELEASE_NOTES=$(echo "$RELEASE_DATA" | jq -r '.body')
# Fix relative links in release notes to point to models repo
# Convert models#123 or #123 to [#123](https://github.com/music-assistant/models/pull/123)
RELEASE_NOTES=$(echo "$RELEASE_NOTES" | sed -E 's/(^|[^[])(models)?#([0-9]+)/\1[#\3](https:\/\/github.com\/music-assistant\/models\/pull\/\3)/g')
# Set multiline output
{
echo 'notes<<EOF'
echo "$RELEASE_NOTES"
echo EOF
} >> $GITHUB_OUTPUT
env:
GH_TOKEN: ${{ secrets.PRIVILEGED_GITHUB_TOKEN }}
- name: Checkout server repository
uses: actions/checkout@v5
with:
repository: music-assistant/server
token: ${{ secrets.PRIVILEGED_GITHUB_TOKEN }}
path: .
- name: Update models version
run: |
VERSION="${{ inputs.version }}"
# Update pyproject.toml
sed -i.bak "s/music-assistant-models==.*/music-assistant-models==$VERSION\",/" pyproject.toml
# Update requirements_all.txt
sed -i.bak "s/music-assistant-models==.*/music-assistant-models==$VERSION/" requirements_all.txt
# Remove backup files
rm -f pyproject.toml.bak requirements_all.txt.bak
- name: Create Pull Request
id: create_pr
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.PRIVILEGED_GITHUB_TOKEN }}
commit-message: "⬆️ Update music-assistant-models to ${{ inputs.version }}"
branch: "auto-update-models-${{ inputs.version }}"
delete-branch: true
title: "⬆️ Update music-assistant-models to ${{ inputs.version }}"
body: |
Update music-assistant-models to version [${{ inputs.version }}](https://github.com/music-assistant/models/releases/tag/${{ inputs.version }})
${{ steps.release_notes.outputs.notes }}
labels: |
dependencies