name: Semantic Release
on:
push:
branches:
- main
workflow_dispatch:
inputs:
force_release:
description: 'Force a release even without conventional commits'
required: false
type: boolean
default: false
permissions:
contents: write
id-token: write
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Install dependencies
run: uv pip install --system -e ".[dev]"
- name: Run tests
run: pytest tests/ -v
- name: Lint
run: |
uv pip install --system ruff
ruff check .
ruff format --check .
release:
needs: test
runs-on: ubuntu-latest
concurrency: release
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install python-semantic-release
run: pip install python-semantic-release
- name: Semantic Release - Determine Version
id: version
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Get the next version (dry run)
NEXT_VERSION=$(semantic-release version --print 2>/dev/null || echo "")
if [ -n "$NEXT_VERSION" ]; then
echo "next_version=$NEXT_VERSION" >> $GITHUB_OUTPUT
echo "should_release=true" >> $GITHUB_OUTPUT
echo "📦 Next version will be: $NEXT_VERSION"
else
echo "should_release=false" >> $GITHUB_OUTPUT
echo "ℹ️ No release needed based on commits"
fi
- name: Semantic Release - Version & Tag
if: steps.version.outputs.should_release == 'true' || inputs.force_release == true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Configure git
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# Run semantic-release to bump version, update changelog, commit, and tag
semantic-release version
- name: Sync versions to manifest.json and server.json
if: steps.version.outputs.should_release == 'true' || inputs.force_release == true
run: |
# Extract version from pyproject.toml (source of truth)
VERSION=$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/')
echo "Syncing version $VERSION to manifest.json and server.json"
# Update manifest.json
jq --arg v "$VERSION" '.version = $v' manifest.json > manifest.tmp.json
mv manifest.tmp.json manifest.json
# Update server.json
jq --arg v "$VERSION" '.version = $v' server.json > server.tmp.json
mv server.tmp.json server.json
# Also update version in packages array
jq --arg v "$VERSION" '.packages[0].version = $v' server.json > server.tmp.json
mv server.tmp.json server.json
# Commit the synced versions
git add manifest.json server.json
git commit --amend --no-edit
# Re-tag with the updated commit
git tag -f "v$VERSION"
- name: Push changes and tags
if: steps.version.outputs.should_release == 'true' || inputs.force_release == true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git push origin main --follow-tags --force
# The release.yml workflow will be triggered by the new tag
# It handles: MCPB packing, GitHub Release creation, MCP Registry publishing