From 15c4d82bcf99f539c7abf2f5aa7563451d91c020 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 12 Sep 2026 23:29:33 +0100 Subject: [PATCH] ci: stop attesting when the release workflow publishes to PyPI 0.20.0 failed to upload with "Certificate's Build Config URI ... does not match expected Trusted Publisher". Authentication was fine: trusted publishing matches the called workflow, pypi-publish.yml, which is what PyPI is configured with. The PEP 740 attestation the publish action attaches by default records the workflow that STARTED the run instead, backend-release.yml, and PyPI verifies that against the same publisher entry. The two claims can never agree while the publish runs as a called workflow, and no publisher configuration satisfies both. Attest only when this file is the entry point, which covers the manual dispatch and a release published by hand. The release chain uploads unattested rather than failing. The earlier rehearsal went to TestPyPI and passed, so this only appeared on the real index. --- .github/workflows/pypi-publish.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index 9037f758..2ad7c716 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -6,6 +6,13 @@ name: Publish to PyPI # creates with GITHUB_TOKEN never fires the release trigger); a release created # by hand publishes through that trigger; a manual run publishes to TestPyPI # by default. +# +# Attestations are on only when this file is the workflow that STARTED the run. +# A PEP 740 attestation records the entry workflow in its certificate, while +# trusted publishing matches the called workflow, so when backend-release calls +# this one the two disagree and PyPI rejects the upload with "Certificate's +# Build Config URI ... does not match expected Trusted Publisher". No publisher +# configuration satisfies both, so the release chain publishes unattested. on: release: @@ -116,6 +123,9 @@ jobs: path: dist/ - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2 + with: + # Only when this workflow is the entry point: see the note at the top. + attestations: ${{ contains(github.workflow_ref, '/pypi-publish.yml@') }} publish-testpypi: if: inputs.target == 'testpypi' @@ -134,6 +144,8 @@ jobs: - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2 with: + # Only when this workflow is the entry point: see the note at the top. + attestations: ${{ contains(github.workflow_ref, '/pypi-publish.yml@') }} repository-url: https://test.pypi.org/legacy/ # TestPyPI is a rehearsal target: a version that is already there # must not fail the run.