Skip to content

AB#3143 Release: install and boot the wheel before publishing it - #5

Merged
vosesoftware merged 1 commit into
mainfrom
fix/3143-smoke-test-the-wheel
Aug 13, 2026
Merged

vosesoftware merged 1 commit into
mainfrom
fix/3143-smoke-test-the-wheel

Conversation

@vosesoftware

Copy link
Copy Markdown
Owner

The wheel is what PyPI serves and what pip install gives users — and nothing ever installed it. AB#3142 made the publish wait for the exe verification, but that gates the PyInstaller binary built from source: a different artifact, resolved differently. A wheel-only fault — a missing packages entry, a bad hatch build glob, an undeclared dependency — would still have reached PyPI, where a version number can never be reused.

Change

The build job now installs the freshly-built wheel into a clean virtualenv and makes it answer a real MCP initialize handshake. It runs from /tmp so the import resolves to the installed package and can never fall through to ./src. publish-pypi already needs build, so this gates the publish for free.

It also asserts the version the server reports matches the tag being released — a forgotten version bump now fails the build instead of permanently burning a PyPI version.

Was it even viable on Linux?

Worth settling before adding a gate to the release path: modelchoice-mcp depends on xlwings unconditionally and the server import chain pulls it in, so a check that couldn't pass would have blocked every future release. No Docker or WSL on the dev box, so I verified it on a real ubuntu runner with a throwaway push-triggered workflow on a scratch branch (since deleted):

modelchoice-mcp 0.0.31 installed
xlwings OK 0.36.14
server OK MCPServer 0.0.31
serverInfo {"name":"modelchoice-mcp","version":"0.0.31"}   PASS

Does the gate actually fail?

A gate that can't fail isn't a gate, so the assertion logic was exercised on all three paths against a real handshake:

scenario result
tag v0.0.31, wheel reports 0.0.31 exit 0
tag v0.0.99, wheel reports 0.0.31 exit 1 — wheel reports 0.0.31 but the tag is v0.0.99
unparseable response exit 1 — could not parse serverInfo

Version extraction parses the JSON rather than grepping for a literal, so a future formatting change in the SDK can't turn this into a spurious release blocker.

Note

release.yml only fires on v* tags, so merging doesn't exercise it. The ubuntu probe and the failure-path tests above are the pre-merge evidence; the next tag is the first real run.

The wheel is what PyPI serves and what `pip install` gives users, and nothing
ever installed it. AB#3142 made the publish wait for the exe verification, but
that gates the PyInstaller binary built from source - a different artifact,
resolved differently. A wheel-only fault (a missing packages entry, a bad hatch
build glob, an undeclared dependency) would still have reached PyPI, where a
version number can never be reused.

The build job now installs the freshly-built wheel into a clean virtualenv and
makes it answer a real MCP initialize handshake, running from /tmp so the import
resolves to the installed package and can never fall through to ./src. It also
asserts the version the server reports matches the tag being released, so a
forgotten version bump fails the build instead of burning a version.
publish-pypi already needs this job, so this gates the publish for free.

Whether the check was even viable on Linux was an open question worth settling
before adding it to the release path: modelchoice-mcp depends on xlwings
unconditionally and the server import chain pulls it in, and a gate that cannot
pass would have blocked every future release. There is no Docker or WSL on the
dev box, so it was verified on a real ubuntu runner via a throwaway
push-triggered workflow on a scratch branch, since deleted:

    modelchoice-mcp 0.0.31 installed
    xlwings OK 0.36.14
    server OK MCPServer 0.0.31
    serverInfo {"name":"modelchoice-mcp","version":"0.0.31"}   PASS

The assertion logic was then exercised on all three paths against a real
handshake - matching tag passes, mismatched tag fails with a clear message,
unparseable response fails - because a gate that cannot fail is not a gate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vosesoftware
vosesoftware merged commit 3945c24 into main Aug 13, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants