From 64ff1f9cc0f866e61a2eeec1ae2f020dd5d9555e Mon Sep 17 00:00:00 2001 From: Phil Date: Sun, 26 Jul 2026 10:44:53 -0400 Subject: [PATCH] ci(release): publish to PyPI with trusted publishing The stored PyPI API token has been deleted, but the publish step still passed `password: ${{ secrets.PYPI_API_TOKEN }}`. A missing secret resolves to an empty string rather than failing, so the next release would have reached the upload with no credential at all, and no OIDC token either, because the workflow did not request `id-token: write`. It would have failed late: with the steps reordered, the version bump is pushed to main before the upload runs, so main would carry a version that never reached PyPI, and no tag or release would exist for it. Request `id-token: write` and drop the password input. The action then falls back to trusted publishing, exchanging a short-lived OIDC token for an upload token against the publisher configured on PyPI for this repository, this workflow and the build_and_publish environment. Nothing durable is stored, so there is no token left to expire, leak or rotate. Every remaining secret reference now resolves: RELEASE_DEPLOY_KEY, NAME_GITHUB, MAIL_GITHUB and ANACONDA_API_TOKEN all exist in that environment. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/python-publish.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 01f53de..1d6f4c0 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -14,6 +14,10 @@ on: permissions: contents: write + # Lets the runner mint the short-lived OIDC token that PyPI trusted publishing + # exchanges for an upload token. Without it the publish step has no credential + # at all, now that no API token is stored. + id-token: write jobs: deploy: @@ -82,10 +86,10 @@ jobs: run: git fetch - name: push code to main run: git push + # No password: the action falls back to trusted publishing, authenticating as + # this workflow in this repository against the publisher configured on PyPI. - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 - with: - password: ${{ secrets.PYPI_API_TOKEN }} # Tag and release only once PyPI has accepted the upload. A version cannot be # re-uploaded to PyPI even after deletion, so the release must not advertise a # version that never made it there.