Skip to content

Publish to PyPI / Github #2

Publish to PyPI / Github

Publish to PyPI / Github #2

name: Publish to PyPI / Github
on:
push:
tags:
- "v*.*.*"
workflow_dispatch:
inputs:
test_only:
description: 'Only publish to TestPyPI (for testing)'
required: false
default: false
type: boolean
version:
description: 'Version to publish (e.g., 0.1.0) - required for manual testing'
required: true
type: string
jobs:
build-and-publish-pypi:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install uv
run: pip install uv
- name: Setup virtual environment
run: uv venv .venv --python=3.10
- name: Extract version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
VERSION="${{ inputs.version }}"
else
VERSION=$(echo $GITHUB_REF | sed -n 's/refs\/tags\/v//p')
fi
echo "VERSION=$VERSION" >> $GITHUB_ENV
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+.*$ ]]; then
echo "Invalid version format: $VERSION"
exit 1
fi
- name: Update version in pyproject.toml
run: sed -i "s/version = \".*\"/version = \"$VERSION\"/" pyproject.toml
- name: Build package
run: uv build
- name: Publish to TestPyPI
if: github.event_name == 'workflow_dispatch' && inputs.test_only == 'true'
env:
UV_PUBLISH_TOKEN: ${{ secrets.TEST_PYPI_API_KEY }}
run: uv publish --publish-url https://test.pypi.org/legacy/
- name: Publish to PyPI
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
env:
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_KEY }}
run: uv publish --publish-url https://upload.pypi.org/legacy/
- name: Publish with twine (fallback)
if: failure()
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ github.event_name == 'push' && secrets.PYPI_API_KEY || secrets.TEST_PYPI_API_KEY }}
TWINE_REPOSITORY_URL: ${{ github.event_name == 'push' && 'https://upload.pypi.org/legacy/' || 'https://test.pypi.org/legacy/' }}
run: |
source .venv/bin/activate
uv pip install twine
twine upload dist/*
- name: Upload built artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist
create-release:
name: Create GitHub Release
runs-on: ubuntu-latest
needs: build-and-publish-pypi
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download built artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist
- name: Check tag for pre-release
id: prerelease_check
run: |
if [[ "${{ github.ref_name }}" == *"alpha"* ]] || [[ "${{ github.ref_name }}" == *"beta"* ]] || [[ "${{ github.ref_name }}" == *"rc"* ]]; then
echo "prerelease=true" >> $GITHUB_OUTPUT
else
echo "prerelease=false" >> $GITHUB_OUTPUT
fi
- name: Release to GitHub
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
draft: false
prerelease: ${{ steps.prerelease_check.outputs.prerelease }}
files: dist/*