PyPI trusted publishing lets a GitHub Actions workflow publish a Python package without a long-lived PyPI API token. For OpenAPI-generated Python SDKs, that is the right default.
The safer workflow is:
- Generate the Python SDK from OpenAPI.
- Build wheel and sdist artifacts.
- Install the wheel in a clean environment.
- Run import and smoke tests.
- Show package metadata and compatibility report.
- Wait for approval.
- Publish through the PyPI trusted publisher.
Nothing in that sequence requires handing a broad PyPI token to a generator.
What PyPI needs
PyPI trusted publishing connects a specific project to a specific GitHub repository, workflow file, environment, and owner. The usual checklist:
- PyPI project exists or the first publish is approved for that name.
- You have added a Trusted Publisher to an existing PyPI project, or configured a pending publisher before the first publish for a new project name.
- GitHub repository is the intended source.
- Workflow file path is stable, for example
.github/workflows/publish-python.yml. - If PyPI has an environment configured, the GitHub Actions job uses the same
environment:. - The workflow has
id-token: write. - Package metadata in
pyproject.tomlis correct. - The distribution builds locally.
A minimal publish job looks like this:
name: Publish Python SDK
on:
workflow_dispatch:
inputs:
version:
required: true
type: string
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: python -m pip install build twine
- run: python -m build
- run: python -m twine check dist/*
- uses: pypa/gh-action-pypi-publish@release/v1
With trusted publishing configured, PyPI trusts the OIDC identity of that workflow.
Why generated Python SDKs need extra checks
Python package failures often show up after publish:
- module name differs from package name
- wheel installs but imports fail
- async client imports optional dependencies incorrectly
pyproject.tomlmetadata is incomplete- generated models require a newer Python than advertised
- README renders poorly on PyPI
Sourced's PyPI readiness flow catches these before publish. It builds the package, installs it, imports it, and reports what is missing from trusted publisher setup.
What should be free
Previewing a generated Python SDK should be free. So should seeing whether the package builds and whether docs can be reviewed.
In Sourced, the free tier includes unlimited previews and up to 2 hosted noindex docs review sites. Upgrade to Launch when you want repo sync, registry readiness/approved publishing, production docs on Sourced-controlled URLs, team controls, or longer retention; use Scale when you need production custom domains and more project capacity.
That keeps the user decision clean: review output first, pay when the release becomes production.
Common trusted publisher errors
When PyPI publishing fails, search for:
- "trusted publisher not configured"
- "invalid-publisher"
- "id-token: write missing"
- "project name already taken"
- "twine check failed"
- "No module named"
Most of these are setup issues, not generator issues. A good release readiness page should tell the user exactly which one is missing and link to the place to fix it.
What Sourced does
Sourced generates the Python SDK preview, builds and checks the package, and reports what is missing before PyPI publishing. Free covers evaluation: unlimited previews and up to 2 hosted noindex docs review sites. Launch is $99/month per API project for repo sync, registry readiness/approved publishing, production docs on Sourced-controlled URLs, and team review. Scale is $349/month and adds more API projects, more hosted docs sites, more team capacity, and production custom domains.