Publishing to PyPI
Automatically publish your wheels to PyPI using GitHub Actions with trusted publishing.
Trusted Publishing (Recommended)
PyPI Trusted Publishing is a secure way to publish without storing API tokens in your GitHub repository.
Setup
- Configure PyPI Trusted Publishing
Go to https://pypi.org/manage/account/publishing/ and add a new publisher:
- PyPI Project Name: Your package name
- Owner: Your GitHub username/organization
- Repository name: Your repository name
- Workflow name:
publish.yml(or your workflow filename) -
Environment name: (optional) Leave blank for all environments
-
Create Publish Workflow
Create .github/workflows/publish.yml:
name: Publish to PyPI
on:
push:
tags:
- "v*"
jobs:
build_wheels:
name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- name: Build wheels
uses: martineastwood/nuwa-build-action@297c4d536f3e4154431dedac55593a96b6b84df2 # reviewed v1-compatible commit
- uses: actions/upload-artifact@v4
with:
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
path: ./wheelhouse/*.whl
publish:
name: Publish to PyPI
needs: [build_wheels]
runs-on: ubuntu-latest
permissions:
id-token: write # Required for trusted publishing
steps:
- uses: actions/download-artifact@v4
with:
pattern: cibw-wheels-*
path: dist
merge-multiple: true
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
- Tag and Push
git tag v1.0.0
git push origin v1.0.0
The workflow will automatically build and publish your package.
Manual Publishing
If you prefer to publish manually or use an API token:
Using API Token
- Create PyPI Token
Go to https://pypi.org/manage/account/token/ and create a new API token.
-
Add to GitHub Secrets
-
Go to your repository Settings → Secrets and variables → Actions
- Create a new secret named
PYPI_API_TOKEN -
Paste your PyPI API token
-
Update Workflow
publish:
name: Publish to PyPI
needs: [build_wheels]
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
pattern: cibw-wheels-*
path: dist
merge-multiple: true
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
TestPyPI Publishing
To test your publishing workflow, use TestPyPI first:
publish:
name: Publish to TestPyPI
needs: [build_wheels]
runs-on: ubuntu-latest
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
pattern: cibw-wheels-*
path: dist
merge-multiple: true
- name: Publish to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
Setup TestPyPI Trusted Publishing
- Go to https://test.pypi.org/manage/account/publishing/
- Add a new publisher (same as PyPI, but for TestPyPI)
- Use
repository-urlin your workflow
Source Distributions
Include source distributions for completeness:
build_sdist:
name: Build source distribution
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Install nuwa-build
run: pip install nuwa-build
- name: Build sdist
run: python -m build --sdist
- uses: actions/upload-artifact@v4
with:
name: cibw-sdist
path: dist/*.tar.gz
publish:
needs: [build_wheels, build_sdist]
# ... rest of publish job
Complete Workflow Example
name: Build and Publish
on:
push:
tags:
- "v*"
jobs:
build_wheels:
name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- name: Build wheels
uses: martineastwood/nuwa-build-action@297c4d536f3e4154431dedac55593a96b6b84df2 # reviewed v1-compatible commit
with:
nim-version: "2.2.10"
env:
CIBW_BUILD: "cp310-* cp311-* cp312-* cp313-* cp314-*"
CIBW_SKIP: "*-musllinux_*"
CIBW_ARCHS_MACOS: "x86_64 arm64"
- uses: actions/upload-artifact@v4
with:
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
path: ./wheelhouse/*.whl
build_sdist:
name: Build source distribution
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Install nuwa-build
run: pip install nuwa-build
- name: Build sdist
run: python -m build --sdist
- uses: actions/upload-artifact@v4
with:
name: cibw-sdist
path: dist/*.tar.gz
publish:
name: Publish to PyPI
needs: [build_wheels, build_sdist]
runs-on: ubuntu-latest
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
pattern: cibw-*
path: dist
merge-multiple: true
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
Release Checklist
Before pushing a release tag:
- [ ] Version updated in
pyproject.toml - [ ] Changelog updated
- [ ] Tests passing locally
- [ ] Trusted publishing configured on PyPI
- [ ] Workflow file created and tested
- [ ] Tag created:
git tag v1.0.0 - [ ] Tag pushed:
git push origin v1.0.0