This guide walks through the full workflow for publishing a Python package to PyPI using GitHub Actions and PyPI's Trusted Publishers (OIDC) feature. No long‑lived API tokens are required—PyPI issues temporary credentials to GitHub at publish time.
- A PyPI account with two-factor authentication enabled.
- Maintainer/Owner access to the PyPI project (or ability to create a new one).
- Admin or maintain permissions on the GitHub repository hosting the package.
- A project configured with standard packaging metadata (e.g.,
pyproject.tomlusingsetuptools,hatchling,flit, etc.).
- Ensure the project builds wheels/sdists locally:
python -m pip install build python -m build
- Add/confirm the following files:
pyproject.toml(orsetup.cfg) describing the package metadata.README,LICENSE, and other distribution files.src/or package modules.
- Commit and push everything to GitHub. Tagging releases is optional at this stage.
- Log into https://pypi.org, open your project’s Manage page (or create a new project if needed).
- Navigate to Publishing → Trusted Publishers → Add a new publisher.
- Choose GitHub Actions as the workflow provider.
- Fill the form with the GitHub repo coordinates and workflow path:
- Owner: e.g.,
my-org - Repository name: e.g.,
awesome-lib - Workflow name/path: default is
.github/workflows/publish.yml - Environment: optional, only needed if the workflow requires a specific GitHub Environment.
- Owner: e.g.,
- Submit the form. PyPI now lists this repository/workflow as authorized but in a “never used” state until the first publish.
- In the GitHub repo, create
.github/workflows/publish.yml. - Example workflow:
name: Publish to PyPI on: push: tags: - "v*.*.*" # publish only when version tags are pushed jobs: build-and-publish: runs-on: ubuntu-latest permissions: id-token: write # required for OIDC contents: read steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.11" # Or whatever the version the package needs - name: Install build backend run: python -m pip install --upgrade pip build - name: Build dists run: python -m build - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: print_hash: true
- Commit and push the workflow. The workflow path must match what you entered on PyPI.
- When you are ready to release, bump the version in
pyproject.toml, commit, and create a tagvX.Y.Z. OR, alternatively, you can directly create a new release in the Releases page. - Push the tag:
git push origin vX.Y.Z
- GitHub Actions triggers the workflow. During the
pypa/gh-action-pypi-publishstep, GitHub exchanges an OIDC token with PyPI. PyPI validates theowner/repo/workflowtuple against the Trusted Publisher you configured and grants a short-lived publishing token automatically. - If the build succeeds, the package appears on PyPI within seconds.
- On PyPI’s project page, the Trusted Publisher entry now shows the workflow as “last used” with a timestamp.
- Download/install the uploaded version to verify:
python -m pip install --upgrade my-package==X.Y.Z
- If the workflow fails because PyPI rejected the upload (e.g., version already exists), fix the issue, bump the version, tag again, and rerun.
- TestPyPI: Repeat the same process on https://test.pypi.org if you want to dry-run publishes. Configure a second workflow or parametrize the existing one with an input that switches repository URLs.
- Manual approval: Use GitHub Environments requiring approval before the publish job proceeds.
- Multi-package repos: Add multiple
<owner>/<repo>/<workflow>entries on PyPI, each referencing different workflow files or environment names. - Security: Since no static token exists, rotating secrets is unnecessary. Keep branch protection/tag protection in place so only trusted maintainers can push release tags.
- ✅ Repo builds local distributions with
python -m build. - ✅ PyPI Trusted Publisher configured for
owner/repo/workflow. - ✅ GitHub workflow requests
id-token: writeand usespypa/gh-action-pypi-publish. - ✅ Version tag pushed to trigger release.
- ✅ Verify published files on PyPI (and optionally TestPyPI).
Following these steps gives you a passwordless, auditable publishing pipeline from GitHub to PyPI using Trusted Publishers.