Skip to content

PyPI Publishing Setup Guide

This guide provides step-by-step instructions for setting up automated PyPI publishing using GitHub Actions with Trusted Publishing.

Overview

The publishing workflow (.github/workflows/publish.yml) automatically publishes to PyPI when a GitHub release is created. It uses PyPI's Trusted Publishing feature, which eliminates the need for API tokens.

Prerequisites

  • Repository owner/admin access on GitHub
  • PyPI account with project ownership rights
  • Package name available on PyPI (or already registered to you)

Setup Instructions

Step 1: Configure PyPI Trusted Publishing

  1. Log in to PyPI

  2. Go to https://pypi.org/

  3. Sign in with your account

  4. Navigate to Publishing Settings

The project must exist on PyPI before you can add a publisher. Use twine to upload a local build (dist/*) to testpypi, examine the create test project, make changes as need, bump version and repeat until satisfied.

twine upload --repository testpypi dist/*

When you are satisfied with the test project, you can publish it to PyPI.

twine upload --repository pypi dist/*

Now you can add a publisher to the existing project. I know, the directions say the project doesn't have to exist, but it does. You cannot add a publisher to a non-existent project no matter how many times you push the Add button (it just doesn't work).

Once the project exists, you can add a publisher to it.

Click "Add a new pending publisher" and fill in:

  • PyPI Project Name: appimage-updater
  • Owner: royw (your GitHub username)
  • Repository name: appimage-updater
  • Workflow name: publish.yml
  • Environment name: pypi

Click "Add" to save.

  1. Important Notes

  2. This creates a "pending" publisher that will be activated on first successful publish

  3. After the first publish, the pending publisher becomes a permanent trusted publisher
  4. You can add this before or after creating the first release

Step 2: Configure GitHub Environment

  1. Navigate to Repository Settings

  2. Go to your repository: https://github.com/royw/appimage-updater

  3. Click "Settings" tab
  4. Click "Environments" in the left sidebar

  5. Create PyPI Environment

  6. Click "New environment"

  7. Name: pypi
  8. Click "Configure environment"

  9. Configure Environment Protection Rules (Recommended)

Add protection rules to prevent accidental publishes:

  • Required reviewers: Add yourself or trusted maintainers
    • This requires manual approval before publishing
  • Wait timer: Optional delay before deployment (e.g., 5 minutes)
  • Deployment branches: Select "Selected branches"
    • Add rule: main (only allow publishes from main branch)

Click "Save protection rules"

  1. Environment Secrets (Not needed for Trusted Publishing)

  2. With Trusted Publishing, you don't need to add any secrets

  3. GitHub automatically provides authentication to PyPI

Step 3: Verify Workflow Configuration

The workflow file .github/workflows/publish.yml should contain:

environment:
  name: pypi
  url: https://pypi.org/p/appimage-updater
permissions:
  id-token: write  # Required for trusted publishing

This is already configured correctly in the repository.

Step 4: Remove Old Publishing Job from CI

The publish-to-pypi job should be removed from .github/workflows/ci.yml since we now have a dedicated publishing workflow.

Publishing Process

Creating a Release

  1. Ensure Version is Updated
# Version should be updated in pyproject.toml
grep "version =" pyproject.toml
  1. Commit and Push Changes
git add .
git commit -m "release: prepare v0.6.0"
git push origin main
  1. Create Git Tag
git tag -a v0.6.0 -m "Release v0.6.0"
git push origin 0.6.0
  1. Create GitHub Release

  2. Go to https://github.com/royw/appimage-updater/releases/new

  3. Choose tag: v0.6.0
  4. Release title: v0.6.0
  5. Description: Copy relevant section from CHANGELOG.md
  6. Check "Set as the latest release"
  7. Click "Publish release"

  8. Monitor Publishing

  9. Go to Actions tab: https://github.com/royw/appimage-updater/actions

  10. Watch the "Publish to PyPI" workflow
  11. If environment protection is enabled, approve the deployment
  12. Verify successful publish to PyPI

Manual Publishing (Testing)

For testing the workflow without creating a release:

  1. Navigate to Actions

  2. Go to https://github.com/royw/appimage-updater/actions

  3. Click "Publish to PyPI" workflow

  4. Run Workflow

  5. Click "Run workflow" button

  6. Select branch: main
  7. Click "Run workflow"

  8. Note: Manual runs will still publish to PyPI, so use with caution!

Troubleshooting

Common Issues

1. "Trusted publishing exchange failure"

Cause: PyPI trusted publisher not configured correctly

Solution:

  • Verify all fields in PyPI publishing settings match exactly:
  • Owner: royw
  • Repository: appimage-updater
  • Workflow: publish.yml
  • Environment: pypi
  • Check that the workflow is running from the correct repository

2. "Environment protection rules failed"

Cause: Deployment waiting for approval or branch restrictions

Solution:

  • Check the Actions tab for pending approvals
  • Verify the workflow is running from an allowed branch
  • Review environment protection rules in Settings → Environments

3. "Package name already exists"

Cause: First-time publish with existing package name

Solution:

  • If you own the package: Add trusted publisher to existing project
  • If you don't own it: Choose a different package name

4. "Permission denied: id-token"

Cause: Missing or incorrect permissions in workflow

Solution:

  • Verify permissions: id-token: write is in the workflow
  • Check that the workflow has not been modified incorrectly

Viewing Logs

  1. GitHub Actions Logs

  2. Go to Actions tab

  3. Click on the workflow run
  4. Click on job name to see detailed logs

  5. PyPI Activity

  6. Go to https://pypi.org/project/appimage-updater/

  7. Check "Release history" for published versions

Security Best Practices

  1. Use Environment Protection

  2. Always require manual approval for production publishes

  3. Restrict to main branch only

  4. Review Before Publishing

  5. Check CHANGELOG.md is updated

  6. Verify version number is correct
  7. Run tests locally: uv run pytest
  8. Build and inspect package: uv build && ls -lh dist/

  9. Monitor Published Packages

  10. Review PyPI project page after each publish

  11. Verify package metadata is correct
  12. Test installation: pip install appimage-updater==0.6.0

  13. Trusted Publishing Benefits

  14. No API tokens to manage or leak

  15. Automatic credential rotation
  16. Audit trail through GitHub Actions
  17. Reduced attack surface

Additional Resources

Quick Reference

PyPI URLs

GitHub URLs

Commands

# Build package locally
uv build

# Check package
twine check dist/*

# Test installation
pip install --user appimage-updater==0.6.0

# Create and push tag
git tag -a v0.6.0 -m "Release v0.6.0"
git push origin v0.6.0