Skip to content

Commit

Permalink
Publish to PyPI with OIDC trusted publisher
Browse files Browse the repository at this point in the history
This commit will update Python package publishing to the newest format
recommended by PyPI. This project previously published packages with a
project-scoped PyPI API token (token only valid for this project) stored
in GitHub Secrets and the `hatch publish` command. The project will now
publish packages using an OIDC (OpenID Connect) trusted publisher with
the pypa/gh-action-pypi-publish action. This is the method that Hatch
itself uses (Hatch does not "dogfood" its own `hatch publish` feature).

The advantage to OIDC is that authentication is performed with temporary
API tokens (only valid for 15 minutes) instead of persistent tokens that
must be manually generated on PyPI and pasted into GitHub Secrets. The
disadvantage is that authentication is more complicated.

To use PyPI OIDC, a trusted publisher was set up for the PyPI project
as shown in the [PyPI docs](https://docs.pypi.org/trusted-publishers/).
Next, a dedicated GitHub Actions deployment environment was created for
PyPI, with protection rules that only allow use of the environment with
workflow runs triggered by Git tags. The environment protection rules
combine with tag protection rules in existing GitHub rulesets to ensure
PyPI packages can only be published when a maintainer pushes a Git tag.
The GitHub Actions workflows will be updated to use the deployment
environment. Deployment environments must be selected at the job level
before the job begins, so a setup job will be added that selects the
appropriate deployment environment and passes it to the PyPI job.
Finally, after `hatch build` outputs the package build files to the
`dist/` directory, pypa/gh-action-pypi-publish will be used to publish
the package to PyPI. The pypa/gh-action-pypi-publish action only appears
to support exact version tags like pypa/[email protected],
which means the action version requires an update for every release.
The Git commit ID (SHA) will be used instead of the exact version tag
because it is more specific. Releases can be modified and republished,
but commit IDs cannot, so a commit ID is more reliable.

https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-pypi
https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment
https://docs.github.com/en/actions/learn-github-actions/finding-and-customizing-actions
https://docs.pypi.org/trusted-publishers/
https://github.com/pypa/gh-action-pypi-publish
  • Loading branch information
br3ndonland committed Apr 8, 2024
1 parent b33e84e commit 14a2b09
Showing 1 changed file with 28 additions and 2 deletions.
30 changes: 28 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,36 @@ on:
branches: [develop, main]
tags: ["[0-9]+.[0-9]+.[0-9]+*"]
workflow_dispatch:
environment:
description: GitHub Actions deployment environment
required: false
type: environment

jobs:
setup:
runs-on: ubuntu-latest
outputs:
environment: ${{ steps.set-env.outputs.environment }}
steps:
- uses: actions/checkout@v4
- name: Set GitHub Actions deployment environment
id: set-env
run: |
if [[ ${{ github.event_name == 'workflow_dispatch' }} ]]; then
environment=${{ inputs.environment }}
elif [[ ${{ github.ref }} == refs/tags/* ]]; then
environment="PyPI"
else
environment=""
fi
echo "environment=$environment" >>"$GITHUB_OUTPUT"
- name: Create annotation for deployment environment
if: steps.set-env.outputs.environment != ''
run: echo "::notice::Deployment environment ${{ steps.set-env.outputs.environment }}"
ci:
runs-on: ubuntu-latest
needs: [setup]
environment: ${{ needs.setup.outputs.environment }}
permissions:
id-token: write
strategy:
Expand Down Expand Up @@ -97,8 +123,8 @@ jobs:
- name: Build Python package
run: hatch build
- name: Publish Python package to PyPI
if: github.ref_type == 'tag' && matrix.python-version == '3.11'
run: hatch publish -n -u __token__ -a ${{ secrets.PYPI_TOKEN }}
if: inputs.environment == 'PyPI' && github.ref_type == 'tag' && matrix.python-version == '3.11'
uses: pypa/gh-action-pypi-publish@81e9d935c883d0b210363ab89cf05f3894778450
changelog:
if: github.ref_type == 'tag'
needs: [ci]
Expand Down

0 comments on commit 14a2b09

Please sign in to comment.