publish-to-pypi.yml•2.8 kB
name: Release
on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: 'Release tag (vX.Y.Z) to re-run publish flow'
required: true
type: string
concurrency:
group: release-${{ github.event_name == 'workflow_dispatch' && format('refs/tags/{0}', github.event.inputs.tag) || github.ref }}
cancel-in-progress: false
jobs:
verify-and-build:
runs-on: ubuntu-latest
env:
RELEASE_REF: ${{ github.event_name == 'workflow_dispatch' && format('refs/tags/{0}', github.event.inputs.tag) || github.ref }}
RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref_name }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ env.RELEASE_REF }}
- name: Ensure tag points to default branch
run: |
git fetch origin
TARGET_BRANCH=$(git remote show origin | awk '/HEAD branch/ {print $NF}')
if [ -z "$TARGET_BRANCH" ]; then
TARGET_BRANCH=master
fi
if ! git merge-base --is-ancestor "$(git rev-parse HEAD)" "origin/${TARGET_BRANCH}"; then
echo "::error::Release tag must point to a commit reachable from ${TARGET_BRANCH}"
exit 1
fi
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install uv
run: python -m pip install --upgrade pip uv
- name: Cache uv environments
uses: actions/cache@v4
with:
path: |
.venv
.uv-cache
key: uv-${{ runner.os }}-${{ hashFiles('uv.lock') }}
restore-keys: |
uv-${{ runner.os }}-
- name: Install dependencies
run: uv sync --frozen
- name: Install build tooling
run: uv pip install build twine
- name: Build distributions
run: uv run python -m build
- name: Twine check
run: uv run twine check dist/*
- name: Upload dist artifacts
uses: actions/upload-artifact@v4
with:
name: dist-${{ env.RELEASE_TAG }}
path: dist/*
retention-days: 7
publish:
needs: verify-and-build
runs-on: ubuntu-latest
environment:
name: production
env:
RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref_name }}
steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: dist-${{ env.RELEASE_TAG }}
path: dist
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist
password: ${{ secrets.PYPI_API_TOKEN }}