How to Set Up Automated PyPI Releases
Task: Configure automated package publishing to PyPI
This guide helps you set up automated PyPI package releases for FailExtract using GitHub Actions. The setup follows progressive enhancement principles with minimal configuration and security best practices.
Prerequisites
GitHub repository with FailExtract source code
Repository admin access for secrets configuration
PyPI account with API token
Basic familiarity with GitHub Actions and Python packaging
Overview
The PyPI release workflow follows the same progressive enhancement approach as documentation:
Start Simple: Package build and upload only
Release-Triggered: Automatic on GitHub release publication
Single Purpose: Each workflow does one thing well
Foundation: Provides base for future CI/CD enhancements
PyPI Release Workflow
The PyPI workflow builds and publishes packages automatically when GitHub releases are published.
Trigger: GitHub release publication
Output: Package available on PyPI via pip install failextract
PyPI Token Setup
Create PyPI API Token:
Navigate to “API tokens” section
Click “Add API token”
Name: “FailExtract GitHub Actions”
Scope: “Entire account” (or project-specific if preferred)
Copy the generated token (starts with
pypi-)
Add to GitHub Secrets:
Go to repository Settings → Secrets and variables → Actions
Click “New repository secret”
Name:
PYPI_API_TOKENValue: Paste the PyPI token
Click “Add secret”
⚠️ Security Note: The token is sensitive and should never be exposed in logs or code.
GitHub Actions Configuration
Create the workflow file:
# .github/workflows/pypi-release.yml
name: PyPI Release
on:
release:
types: [published]
jobs:
build-and-publish:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install build tools
run: |
python -m pip install --upgrade pip
pip install build twine
- name: Validate version consistency
run: |
# Extract version from git tag (remove 'v' prefix if present)
GIT_TAG=${GITHUB_REF#refs/tags/}
GIT_VERSION=${GIT_TAG#v}
# Extract version from pyproject.toml
PYPROJECT_VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
echo "Git tag version: $GIT_VERSION"
echo "pyproject.toml version: $PYPROJECT_VERSION"
if [ "$GIT_VERSION" != "$PYPROJECT_VERSION" ]; then
echo "Version mismatch: git tag ($GIT_VERSION) != pyproject.toml ($PYPROJECT_VERSION)"
exit 1
fi
echo "Version validation passed: $GIT_VERSION"
- name: Build package
run: |
python -m build
- name: Validate package
run: |
# Check that the package can be built and contains expected files
python -m twine check dist/*
# List built artifacts
echo "Built artifacts:"
ls -la dist/
# Verify wheel can be installed and imported
pip install dist/*.whl
python -c "import failextract; print(f'FailExtract version: {failextract.__version__}')"
- name: Publish to PyPI
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
python -m twine upload dist/*
Key Design Decisions:
Python 3.11: Stable version for build consistency
Industry Standard Tools:
build+twinefor reliabilityVersion Validation: Ensures git tag matches
pyproject.tomlPackage Validation: Multiple checks before upload
Secure Token Handling: Uses GitHub secrets
Package Configuration
Ensure pyproject.toml is properly configured:
[project]
name = "failextract"
version = "1.0.0" # Must match git tag
description = "Test failure extraction and reporting library"
# ... other project metadata
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
Critical Requirements:
Version in
pyproject.tomlmust exactly match git tagAll required metadata fields must be present
Package structure must be valid
Testing the Workflow
Local Testing:
Before creating a release, test package building locally:
# Install build tools
pip install build twine
# Build package
python -m build
# Validate package
python -m twine check dist/*
# Test installation
pip install dist/*.whl
python -c "import failextract; print('Success!')"
Test Workflow (optional):
For safer testing, use the test workflow:
# .github/workflows/test-pypi-release.yml
name: Test PyPI Release
on:
workflow_dispatch: # Manual trigger
jobs:
test-build-and-publish:
# Same steps as main workflow but without PyPI upload
Live Release Testing:
Create version tag:
git tag v1.0.0Push tag:
git push origin v1.0.0Create GitHub release from the tag
Monitor workflow execution in Actions tab
Verify package appears on PyPI
Release Process
Standard Release Workflow:
Update Version: Modify
versioninpyproject.tomlCommit Changes:
git commit -am "Release v1.0.0"Create Tag:
git tag v1.0.0Push Changes:
git push && git push --tagsCreate Release: Use GitHub web interface to create release from tag
Monitor Workflow: Check Actions tab for successful execution
Verify Publication: Confirm package is available on PyPI
Version Validation:
The workflow automatically validates that:
Git tag version matches
pyproject.tomlversionPackage builds successfully
Package passes Twine validation
Package can be imported after installation
Expected Workflow Output
Successful Build:
* Creating isolated environment: venv+pip...
* Installing packages in isolated environment:
- hatchling
* Building sdist...
* Building wheel from sdist
Successfully built failextract-1.0.0.tar.gz and failextract-1.0.0-py3-none-any.whl
Package Validation:
Checking dist/failextract-1.0.0-py3-none-any.whl: PASSED
Checking dist/failextract-1.0.0.tar.gz: PASSED
PyPI Upload:
Uploading distributions to https://upload.pypi.org/legacy/
Uploading failextract-1.0.0-py3-none-any.whl
Uploading failextract-1.0.0.tar.gz
Troubleshooting
Version Mismatch Errors:
Version mismatch: git tag (1.0.1) != pyproject.toml (1.0.0)
Solution: Update pyproject.toml version to match git tag or create new tag.
Build Failures:
Check
pyproject.tomlsyntax and configurationVerify all required files are included
Test build locally before release
Upload Failures:
Verify PyPI token is correctly set in GitHub secrets
Check for version conflicts (version already exists on PyPI)
Ensure package metadata is complete
Import Failures:
Check package structure and
__init__.pyfilesVerify dependencies are correctly specified
Test installation in clean environment
Common Issues
Token Authentication:
403 Client Error: Invalid or non-existent authentication information
Solution:
- Verify PYPI_API_TOKEN secret is set correctly
- Ensure token has sufficient permissions
- Check token hasn’t expired
Version Already Exists:
400 Client Error: File already exists
Solution:
- PyPI doesn’t allow overwriting existing versions
- Create new version tag and update pyproject.toml
- Use pre-release versions for testing (e.g., 1.0.0rc1)
Monitoring and Maintenance
Regular Checks:
Monitor workflow execution on each release
Verify packages install correctly:
pip install failextractCheck PyPI project page for correct metadata
Review download statistics and user feedback
Token Management:
Rotate PyPI tokens periodically
Use project-scoped tokens when possible
Monitor token usage in PyPI account settings
Dependency Updates:
Update workflow dependencies periodically:
# Update action versions
uses: actions/checkout@v4 # → v5 when available
uses: actions/setup-python@v4 # → v5 when available
Future Enhancements
Following progressive enhancement, consider adding:
Pre-release Testing:
- name: Run tests before release
run: pytest tests/
Multi-Python Testing:
strategy:
matrix:
python-version: ['3.11', '3.12', '3.13']
TestPyPI Integration:
- name: Upload to TestPyPI first
run: python -m twine upload --repository testpypi dist/*
Security Best Practices
Token Management:
Use project-scoped tokens when possible
Rotate tokens regularly (every 6-12 months)
Monitor token usage in PyPI account settings
Never commit tokens to version control
Workflow Security:
Pin action versions for reproducibility
Use official GitHub actions when possible
Review third-party action permissions
Monitor workflow logs for sensitive information exposure
Release Security:
Validate all packages before upload
Use signed commits for release tags
Require pull request reviews for version changes
Monitor PyPI package for unauthorized changes
Success Checklist
.github/workflows/pypi-release.ymlpyproject.toml properly configured with versionpip install failextractNext Steps: Monitor release process and consider progressive enhancements like automated testing or multi-environment validation.