From e93c6986d1d69bc3f9b3be84f1777d7693c2ab67 Mon Sep 17 00:00:00 2001 From: Timour Koupeev Date: Thu, 13 Aug 2026 13:36:41 +0300 Subject: [PATCH] AB#3142 Release: gate the PyPI publish on the exe verification publish-pypi depended on `build` alone. build-windows-exe - which packs the single-file exe and asserts it answers an MCP initialize handshake - has no artifact the publish consumes, so it ran in PARALLEL with the publish rather than before it. A PyInstaller or packaging break would have been reported by a red job while the release was already on PyPI. That asymmetry matters because publishing is the only irreversible step in this workflow: a PyPI version number can never be reused, so a bad publish costs a version and a follow-up release, whereas a bad tag is just `git tag -d`. publish-pypi now needs [build, build-windows-exe]. The graph stays a DAG and github-release still runs last; the cost is a few minutes of Windows-runner time before the publish, which is the right trade for an action that cannot be undone. Found while releasing v0.0.31: the mcp 2.0 migration swapped the SDK underneath a frozen binary, so the exe was packed and smoke-tested locally before tagging to compensate for exactly this gap. That manual step is no longer load-bearing. Verified by parsing release.yml and asserting the job graph: deps all resolve, no cycles, publish-pypi gated on both build and build-windows-exe, trigger unchanged (push on v* tags). Co-Authored-By: Claude Opus 5 --- .github/workflows/release.yml | 10 +++++++++- CHANGELOG.md | 10 ++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 266bb43..42b3752 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -80,7 +80,15 @@ jobs: path: dist/modelchoice-mcp.mcpb publish-pypi: - needs: build + # Gated on build-windows-exe as well as build, even though it consumes only + # the `dist` artifact. Publishing to PyPI is the one irreversible step in + # this workflow - a version number can never be reused - so it must run + # after every verification, not alongside them. With `needs: build` alone, + # build-windows-exe raced the publish and a packaging break (its MCP + # initialize handshake failing) still shipped. Costs a few minutes; buys + # the ability to fix a bad build by deleting a tag instead of burning a + # version. (AB#3142) + needs: [build, build-windows-exe] runs-on: ubuntu-latest environment: pypi permissions: diff --git a/CHANGELOG.md b/CHANGELOG.md index 76bc619..cc76164 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to `modelchoice-mcp`. Versions are tag-driven; pushing a `vX.Y.Z` tag publishes to PyPI via the release workflow. +## Unreleased +- **The PyPI publish now waits for the Windows exe verification (AB#3142).** `publish-pypi` + depended on `build` alone, so `build-windows-exe` — which packs the single-file exe and + asserts it answers an MCP `initialize` handshake — raced the publish instead of gating + it, and a packaging break would still have reached PyPI. Publishing is the one + irreversible step in the workflow (a version number can never be reused), so it now runs + after every verification. Costs a few minutes per release; buys the ability to fix a bad + build by deleting a tag rather than burning a version. Release-workflow only — no change + to the package. + ## 0.0.31 - **Migrated to the mcp 2.0 SDK (AB#3134).** mcp 2.0 removed `mcp.server.fastmcp` and renamed the high-level server class `FastMCP` → **`MCPServer`** (`from mcp.server import