Skip to content

Instantly share code, notes, and snippets.

@emrekgn
Created November 27, 2025 13:13
Show Gist options
  • Select an option

  • Save emrekgn/53b29e9ee8779f1a6c81fa0d25afdd8c to your computer and use it in GitHub Desktop.

Select an option

Save emrekgn/53b29e9ee8779f1a6c81fa0d25afdd8c to your computer and use it in GitHub Desktop.
How to publish a Python package from Github to pypi ("Trusted Publishers" method)

Publishing to PyPI from GitHub with Trusted Publishers

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.


1. Prerequisites

  1. A PyPI account with two-factor authentication enabled.
  2. Maintainer/Owner access to the PyPI project (or ability to create a new one).
  3. Admin or maintain permissions on the GitHub repository hosting the package.
  4. A project configured with standard packaging metadata (e.g., pyproject.toml using setuptools, hatchling, flit, etc.).

2. Prepare the Repo

  1. Ensure the project builds wheels/sdists locally:
    python -m pip install build
    python -m build
  2. Add/confirm the following files:
    • pyproject.toml (or setup.cfg) describing the package metadata.
    • README, LICENSE, and other distribution files.
    • src/ or package modules.
  3. Commit and push everything to GitHub. Tagging releases is optional at this stage.

3. Configure PyPI Trusted Publisher

  1. Log into https://pypi.org, open your project’s Manage page (or create a new project if needed).
  2. Navigate to Publishing → Trusted Publishers → Add a new publisher.
  3. Choose GitHub Actions as the workflow provider.
  4. 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.
  5. Submit the form. PyPI now lists this repository/workflow as authorized but in a “never used” state until the first publish.

4. Create the GitHub Actions Workflow

  1. In the GitHub repo, create .github/workflows/publish.yml.
  2. 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
  3. Commit and push the workflow. The workflow path must match what you entered on PyPI.

5. Link Releases to PyPI

  1. When you are ready to release, bump the version in pyproject.toml, commit, and create a tag vX.Y.Z. OR, alternatively, you can directly create a new release in the Releases page.
  2. Push the tag:
    git push origin vX.Y.Z
  3. GitHub Actions triggers the workflow. During the pypa/gh-action-pypi-publish step, GitHub exchanges an OIDC token with PyPI. PyPI validates the owner/repo/workflow tuple against the Trusted Publisher you configured and grants a short-lived publishing token automatically.
  4. If the build succeeds, the package appears on PyPI within seconds.

6. First-Time Verification

  1. On PyPI’s project page, the Trusted Publisher entry now shows the workflow as “last used” with a timestamp.
  2. Download/install the uploaded version to verify:
    python -m pip install --upgrade my-package==X.Y.Z
  3. If the workflow fails because PyPI rejected the upload (e.g., version already exists), fix the issue, bump the version, tag again, and rerun.

7. Additional Options

  • 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.

8. Summary Checklist

  1. ✅ Repo builds local distributions with python -m build.
  2. ✅ PyPI Trusted Publisher configured for owner/repo/workflow.
  3. ✅ GitHub workflow requests id-token: write and uses pypa/gh-action-pypi-publish.
  4. ✅ Version tag pushed to trigger release.
  5. ✅ 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment