From 49216e60258542a93ffe3bedcc5063516aef1109 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Sun, 30 Aug 2026 09:41:41 -0700 Subject: [PATCH] ci: publish threadplane-middleware with uv, ending the twine metadata skew MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The real-release path used the pinned pypa/gh-action-pypi-publish image, whose bundled twine rejects the Metadata-Version 2.5 that current uv build emits: 'InvalidDistribution: 2.5 is not a valid metadata version'. The dry-run path already used uv publish and passed — the two paths disagreed about the artifact between them. Observed on the first real 0.0.2 publish attempt (dry-run green, real release red, nothing uploaded). Build, dry-run, and publish now share one toolchain. PyPI trusted publishing binds to repo + workflow filename, not the action, so the same OIDC grant works; uv consumes it via --trusted-publishing always. Trade-off, stated: the pypa action also uploaded PEP 740 attestations. uv publish does not yet; losing them is acceptable against a publish path that cannot ship at all. Co-Authored-By: Claude Opus 5 --- .github/workflows/publish-middleware-python.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish-middleware-python.yml b/.github/workflows/publish-middleware-python.yml index 3a30e3de9..2fa6a257b 100644 --- a/.github/workflows/publish-middleware-python.yml +++ b/.github/workflows/publish-middleware-python.yml @@ -76,9 +76,14 @@ jobs: working-directory: packages/threadplane-middleware run: uv publish --dry-run dist/* + # uv publish, not the pypa action: the pinned action image's twine + # rejected the Metadata-Version 2.5 that current `uv build` emits + # ("InvalidDistribution: '2.5' is not a valid metadata version"), while + # the dry-run path — which already uses uv — passed. One toolchain for + # build, dry-run, and publish removes that skew permanently. PyPI + # trusted publishing is bound to repo + workflow filename, so the same + # OIDC token works; uv picks it up via --trusted-publishing. - name: Publish to PyPI (real release — OIDC trusted publishing) if: ${{ inputs.dry_run == false }} - uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1 - with: - packages-dir: packages/threadplane-middleware/dist - attestations: true + working-directory: packages/threadplane-middleware + run: uv publish --trusted-publishing always dist/*