Skip to content

Production PyPI upload moves to the caller (Trusted Publishing can't match a reusable workflow) - #53

Merged
bdbarnett merged 1 commit into
mainfrom
caller-side-pypi-publish
Sep 25, 2026
Merged

bdbarnett merged 1 commit into
mainfrom
caller-side-pypi-publish

Conversation

@bdbarnett

@bdbarnett bdbarnett commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

audiodsp v0.6.1 built and reached TestPyPI, then failed at publish-to-pypi with invalid-publisher (run 36131566669). Its PyPI Trusted Publisher is set up right. The problem is where the upload ran: PyPI matches the publisher against job_workflow_ref, the workflow that runs the job, and the log shows that was PyDevices/.github/.github/workflows/reusable-publish-release-packages.yml@refs/tags/publishing-v8. No repository's publisher can name that (pypa/gh-action-pypi-publish#166, PyPI's note).

So the upload moves to the caller. The coordinator's publish-to-pypi job becomes pypi-gate. It makes the same decision (opted in, final release, build and TestPyPI succeeded) and uploads nothing. The workflow exposes three outputs, pypi-publish, dist-artifact and version, and the caller's own pypi job (environment pypi, id-token: write) downloads the artifact and uploads. The caller's side is audiodsp#151.

TestPyPI isn't affected. It uses TESTPYPI_API_TOKEN, not Trusted Publishing, so its upload stays in the coordinator.

Release Health keeps its order. The coordinator's report still goes out when its jobs finish, and for a release headed to PyPI it now says pypi: pending. The caller's report-pypi job calls the new reusable-report-pypi-result.yml once the upload ends (or is rejected), and that sends a partial report. release-health.yml folds it into the same row when the version matches. Three new tests cover that, and the merge test fails when the merge line is removed.

Only audiodsp sets pypi-publish: true. audiocomponents (v8) and palettes, pdwidgets, pygraphics, lvgl-python and mpftp (v6) don't, and pydevices (v11) doesn't either, so nobody else needs a caller change.

Order

  1. Merge this PR.
  2. Cut the next publishing tag from a clean, level main: scripts/cut_publishing_tag.sh 12 (or the next free number). That tag also carries .github#52's pydevices publishing changes. Cutting it moves nobody: pydevices and mip stay on v11 until their pins move, so pydevices publishing: own-package MIP packages and PyPI extras (for bledev) #52's own rollout is still for another time.
  3. On audiodsp#151, replace both publishing-vNEXT refs with that tag, then merge it. Until then that PR's publish workflow fails at startup, so don't cut an audiodsp release from it before the refs are real.

For audiodsp the jump from v8 to the new tag also brings cibuildwheel 4.2.0 → 4.2.1, setup-java v5 → v6, and sibling refs that name their own tag. Nothing else in its path changes.

v0.6.1 itself won't reach PyPI through this fix. The pypi environment only deploys from v* tags, and a dispatch on the v0.6.1 tag runs the caller as it was at that tag. Either the next audiodsp release goes up this way, or you twine upload v0.6.1's Release assets by hand, the way 0.6.0 was parked.

Checked

actionlint 1.7.12 is clean on every workflow. The caller's jobs were also linted against these reusables resolved locally, and a misspelt output there is caught. The YAML parses. The sibling-refs check still sees one tag. ruff check scripts/ tests/ passes, and all unit tests pass. Nothing ran on GitHub: no tag, no publish.

On this PR, generator-idempotency fails, and the cause isn't here. The generator lists pydevices 0.5.4, but the mip repository's published page still says 0.5.3, so the two have drifted since the 0.5.4 release. This PR doesn't touch data/ or the generator. The other five checks pass.

… match a reusable workflow

PyPI matches a Trusted Publisher against job_workflow_ref, which inside a
called workflow is this repository's coordinator at a publishing tag. No
repository's publisher can name that, so audiodsp v0.6.1's upload failed with
invalid-publisher.

The coordinator's publish-to-pypi job becomes pypi-gate, which only decides.
The workflow exposes pypi-publish, dist-artifact and version outputs, and the
caller runs the upload in its own pypi job. Release Health gets pypi=pending
from the coordinator and the outcome from the new
reusable-report-pypi-result.yml, which release-health.yml folds into the same
row. TestPyPI keeps its token upload in the coordinator.
@bdbarnett
bdbarnett merged commit 2cdbc9d into main Sep 25, 2026
11 of 12 checks passed
@bdbarnett
bdbarnett deleted the caller-side-pypi-publish branch September 25, 2026 23:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant