release.ymlโข9.66 kB
name: Release
on:
push:
tags:
- 'v*.*.*'
permissions:
contents: write
packages: write
jobs:
test:
name: Run Tests
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"
- name: Run tests
run: |
pytest tests/ -v --cov=src/mockloop_mcp --cov-report=xml --cov-report=term
- name: Upload coverage to Codecov
if: matrix.python-version == '3.11'
uses: codecov/codecov-action@v4
with:
file: ./coverage.xml
fail_ci_if_error: false
security:
name: Security Checks
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 dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"
- name: Run security checks
run: |
bandit -r src/ -f json -o bandit-report.json || true
safety check --json --output safety-report.json || true
pip-audit --format=json --output=pip-audit-report.json || true
build:
name: Build Distribution
runs-on: ubuntu-latest
needs: [test, security]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install build dependencies
run: |
python -m pip install --upgrade pip
pip install build twine
- name: Extract version from tag
id: version
run: |
echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- name: Debug version output
run: |
echo "GITHUB_REF is: $GITHUB_REF"
echo "VERSION is: ${{ steps.version.outputs.VERSION }}"
echo "TAG is: ${{ steps.version.outputs.TAG }}"
- name: Fail if version is not set
run: |
if [ -z "${{ steps.version.outputs.VERSION }}" ]; then
echo "VERSION was not set!"
exit 1
fi
- name: Verify version consistency
run: |
# Check that the tag version matches the version in pyproject.toml
PYPROJECT_VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
if [ "${{ steps.version.outputs.VERSION }}" != "$PYPROJECT_VERSION" ]; then
echo "Version mismatch: tag=${{ steps.version.outputs.VERSION }}, pyproject.toml=$PYPROJECT_VERSION"
exit 1
fi
- name: Build source distribution and wheel
run: |
python -m build
- name: Check distribution
run: |
twine check dist/*
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist-${{ steps.version.outputs.VERSION }}
path: dist/
release:
name: Create GitHub Release
runs-on: ubuntu-latest
needs: [build]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Extract version from tag
id: version
run: |
echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: dist-${{ steps.version.outputs.VERSION }}
path: dist/
- name: Extract changelog for version
id: changelog
run: |
# Extract the changelog section for this version
python -c "
import re
import sys
with open('CHANGELOG.md', 'r') as f:
content = f.read()
# Find the section for this version
version = '${{ steps.version.outputs.VERSION }}'
pattern = rf'## \[{re.escape(version)}\].*?\n(.*?)(?=\n## \[|\n\[.*?\]:|\Z)'
match = re.search(pattern, content, re.DOTALL)
if match:
changelog_text = match.group(1).strip()
# Remove any trailing links section
changelog_text = re.sub(r'\n\[.*?\]:.*$', '', changelog_text, flags=re.MULTILINE)
print(changelog_text)
else:
print(f'Release notes for version {version}')
" > release_notes.txt
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.TAG }}
name: Release ${{ steps.version.outputs.VERSION }}
body_path: release_notes.txt
files: |
dist/*.tar.gz
dist/*.whl
draft: false
prerelease: ${{ contains(steps.version.outputs.VERSION, '-') }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
publish-testpypi:
name: Publish to TestPyPI
runs-on: ubuntu-latest
needs: [release]
environment: testpypi
permissions:
contents: read
id-token: write # Required for trusted publishing to TestPyPI
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Extract version from tag
id: version
run: |
echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: dist-${{ steps.version.outputs.VERSION }}
path: dist/
- name: Install twine
run: |
python -m pip install --upgrade pip twine
- name: Validate package for PyPI
run: |
twine check dist/*
- name: Publish to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
- name: Wait for TestPyPI propagation
run: sleep 60
- name: Test installation from TestPyPI
run: |
python -m pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ mockloop-mcp==${{ steps.version.outputs.VERSION }}
mockloop-mcp --version
verify-testpypi:
name: Verify TestPyPI Installation
runs-on: ${{ matrix.os }}
needs: [publish-testpypi]
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ["3.10", "3.11", "3.12"]
steps:
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Extract version from tag
id: version
run: |
echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
- name: Install from TestPyPI
run: |
python -m pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ mockloop-mcp==${{ steps.version.outputs.VERSION }}
- name: Verify installation
run: |
mockloop-mcp --version
python -c "import mockloop_mcp; print('โ
Package import successful')"
publish-pypi:
name: Publish to PyPI
runs-on: ubuntu-latest
needs: [verify-testpypi]
environment: pypi
permissions:
contents: read
id-token: write # Required for trusted publishing to PyPI
steps:
- uses: actions/checkout@v4
- name: Extract version from tag
id: version
run: |
echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: dist-${{ steps.version.outputs.VERSION }}
path: dist/
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
- name: Create PyPI release summary
run: |
echo "## ๐ PyPI Release Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Package Details" >> $GITHUB_STEP_SUMMARY
echo "- **Version:** ${{ steps.version.outputs.VERSION }}" >> $GITHUB_STEP_SUMMARY
echo "- **PyPI URL:** https://pypi.org/project/mockloop-mcp/${{ steps.version.outputs.VERSION }}/" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Installation" >> $GITHUB_STEP_SUMMARY
echo '```bash' >> $GITHUB_STEP_SUMMARY
echo 'pip install mockloop-mcp==${{ steps.version.outputs.VERSION }}' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
verify-pypi:
name: Verify PyPI Installation
runs-on: ${{ matrix.os }}
needs: [publish-pypi]
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ["3.10", "3.11", "3.12"]
steps:
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Extract version from tag
id: version
run: |
echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
- name: Wait for PyPI propagation
run: sleep 120
- name: Install from PyPI
run: |
python -m pip install mockloop-mcp==${{ steps.version.outputs.VERSION }}
- name: Verify installation
run: |
mockloop-mcp --version
python -c "import mockloop_mcp; print('โ
Package import successful')"