Skip to content

publish-to-pypi

publish-to-pypi #3

name: publish-to-pypi
# Manual activation
on:
workflow_dispatch:
inputs:
release_tag:
description: "Release tag, e.g. 2025-12-10"
required: true
type: string
jobs:
# -- Publish a new Apio release
publish:
runs-on: ubuntu-22.04
steps:
- name: Validate release tag is stable
run: |
# Get release metadata JSON.
TAG="${{ inputs.release_tag }}"
URL="https://api.github.com/repos/${{ github.repository }}/releases/tags/$TAG"
echo "URL: $URL"
RELEASE_JSON=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" "$URL")
# Check if release exists
if echo "$RELEASE_JSON" | grep -q "Not Found"; then
echo "::error ::Release tag $TAG does not exist"
exit 1
fi
# Dump RELEASE_JSON for debugging.
# echo $RELEASE_JSON | jq .
# Extract draft and prerelease flags
DRAFT=$(echo "$RELEASE_JSON" | jq -r '.draft')
PRERELEASE=$(echo "$RELEASE_JSON" | jq -r '.prerelease')
if [ "$DRAFT" = "true" ]; then
echo "::error ::Release $TAG is a draft, must be stable."
exit 1
fi
if [ "$PRERELEASE" = "true" ]; then
echo "::error ::Release $TAG is a prerelease, must be stable."
exit 1
fi
echo "Release $TAG is stable (non pre-release or draft)"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# -- Checkout the main branch
- name: Checkout at release ${{ inputs.release_tag }}
uses: actions/checkout@v4
with:
# Checks out the exact tag entered by the user
ref: ${{ inputs.release_tag }}
# -- Install and and configure python
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"
# -- Install dependencies.
- name: Install dependencies
run: python -m pip install --upgrade pip build flit
- name: Build package distributions
run: |
# Creates dist/*.tar.gz and dist/*.whl from pyproject.toml
python -m build
# -- Publish to Pypi!!
- name: Publish to PyPi
env:
# TODO: As of Dec 27 2025, PYPI_USERNAME and PYPI_PASSWORD
# contain a token rather than the actual username and
# password (good security). We copied PYPI_PASSWORD to
# PYPI_API_TOKEN and use a literal "__token__" instead
# of PYPI_USERNAME. Once this new configuration publishes
# successfully, the two secrets PYPI_USERNAME and PYPI_PASSWORD
# should be deleted for clarity.
#
# FLIT_USERNAME: ${{ secrets.PYPI_USERNAME }}
# FLIT_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
FLIT_USERNAME: __token__
FLIT_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
# invoke publish
flit publish