Skip to content

Publish to PyPI with trusted publishing - #95

Merged
mihiarc merged 2 commits into
mainfrom
ci/pypi-trusted-publishing
Aug 14, 2026
Merged

mihiarc merged 2 commits into
mainfrom
ci/pypi-trusted-publishing

Conversation

@mihiarc

@mihiarc mihiarc commented Aug 14, 2026 •

Copy link
Copy Markdown
Collaborator

pypi.yml still uploads with twine using the PYPI_USERNAME / PYPI_PASSWORD secrets. This switches it to PyPI trusted publishing (OIDC via pypa/gh-action-pypi-publish), so no long-lived PyPI credential is needed to publish.

Build and publish are split into separate jobs, as PyPA recommends: id-token: write is scoped to the publish job, and the build job — the one that resolves and executes build dependencies — runs with contents: read and a checkout that doesn't persist credentials.

Draft until the PyPI side is configured. On https://pypi.org/manage/project/forestspot/settings/publishing/, add a GitHub publisher:

field value
owner taraskiba
repository forestspot
workflow pypi.yml
environment (leave blank)

Once that's registered I'll mark this ready. Merging it before then would break the next release.

Notes for review

  • No skip-existing. The old workflow passed twine --skip-existing. Keeping it would let a re-release of an already-published version skip the upload and exit 0, so the Actions tab and the release page both go green while PyPI keeps serving the old files. PyPA advises against it for PyPI for exactly this reason; a duplicate upload now fails loudly.
  • Attestations. gh-action-pypi-publish generates and uploads PEP 740 attestations by default for trusted-publishing projects, which twine upload never did. Left at the default (on) — worth knowing it's in the release path if a future upload fails verification.
  • No environment:. PyPA's example gates publishing behind a GitHub environment with required reviewers. Creating one needs admin, which I don't have, so this is left off and the PyPI publisher must be registered with the environment field blank to match. Worth adding later, @taraskiba — it's a two-sided change (workflow and the PyPI publisher config) or the OIDC claim won't match.
  • After merge: the PYPI_USERNAME / PYPI_PASSWORD secrets stay live and valid until they're deleted in repo settings. That needs your access, @taraskiba.

Verified: python -m build succeeds from a clean checkout and produces the sdist and wheel the publish job expects; actionlint and pre-commit clean.

Chris Mihiar added 2 commits August 14, 2026 16:15
Replaces the twine upload driven by the PYPI_USERNAME/PYPI_PASSWORD secrets
with pypa/gh-action-pypi-publish over OIDC, so no long-lived PyPI credential
is stored in the repository. Build and publish are split into separate jobs
as PyPA recommends, keeping the id-token permission off the job that runs
build-time dependencies.

Requires a trusted publisher for taraskiba/forestspot with workflow
pypi.yml to be registered on PyPI before the next release.
- skip-existing made a failed release report green: re-releasing an already
  published version would skip the upload and exit 0 while PyPI kept serving
  the old files. PyPA advises against it for PyPI for that reason, so let a
  duplicate upload fail loudly instead.
- Add contents: read at the workflow level and persist-credentials: false on
  the checkout, so the job that resolves and executes build dependencies does
  not carry a writable GITHUB_TOKEN. The publish job overrides this with the
  id-token permission it needs.
- overwrite: true lets "Re-run all jobs" on a failed release work; v4
  artifacts are immutable per run, so the second attempt would otherwise 409
  in the build job before publish is reached.
- if-no-files-found: error reports an empty build where it happens instead of
  as a missing artifact one job later.
- The header comment claimed no PyPI secret is stored; the secrets stay until
  Tara deletes them, so say the workflow no longer uses them.
@mihiarc
mihiarc force-pushed the ci/pypi-trusted-publishing branch from c4b0874 to a6427e8 Compare August 14, 2026 20:15
@mihiarc
mihiarc marked this pull request as ready for review August 14, 2026 20:21
@mihiarc
mihiarc merged commit 3ecb5e2 into main Aug 14, 2026
9 checks passed
@mihiarc
mihiarc deleted the ci/pypi-trusted-publishing branch August 14, 2026 20:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant