Skip to content

Single-source the version, package GRAPH.md, harden the publish gate - #41

Draft
Deep-Sixed wants to merge 1 commit into
mainfrom
claude/pensive-feynman-wyv0sm
Draft

Deep-Sixed wants to merge 1 commit into
mainfrom
claude/pensive-feynman-wyv0sm

Conversation

@Deep-Sixed

Copy link
Copy Markdown
Owner

Fixes five findings from a review of #39 (the 0.1.1 PyPI release).

What was wrong

  • Wrong version at runtime. The contextmesh-graph 0.1.1 wheel on PyPI reports contextmesh.__version__ == contextmesh_mcp.__version__ == "0.1.0". The version was typed by hand in three places, and the publish check compared the tag against pyproject.toml only.
  • GRAPH.md installed loose in site-packages. The package-data entry contextmesh = ["../GRAPH.md"] puts the file at site-packages/GRAPH.md, outside any package. Another distribution's GRAPH.md can overwrite or delete it, and the ontology then fails to load.
  • GitHub pre-releases published as final releases. A GitHub pre-release tagged with a final version, such as v0.1.2, would have gone to PyPI as the release pip install contextmesh-graph picks up.
  • Pre-release tags rejected. The filename checks used the raw version, so a tag like v1.0.0-rc.1 (files are named 1.0.0rc1) could never publish.
  • Checks written as assert. Python skips assert under python -O or PYTHONOPTIMIZE, so every check would pass silently.

Changes

  • pyproject.toml: dynamic = ["version"], read from contextmesh.__version__. That constant is now the only place to bump the version, and contextmesh_mcp re-exports it.

  • setup.py (new): a build_py hook that copies the repository-root GRAPH.md into contextmesh/ in the wheel. It skips editable installs. MANIFEST.in keeps GRAPH.md in the sdist. contextmesh/ontology.py reads the packaged copy first and falls back to the repository root, which covers source checkouts and editable installs. GRAPH.md stays at the repository root as the one copy.

  • publish.yml: one post-build step checks release identity against the built wheel's own METADATA instead of pyproject.toml:

    • the distribution name;
    • that the ref is a tag;
    • tag and version compared as PEP 440 versions;
    • the GitHub pre-release flag against Version.is_prerelease;
    • normalized filenames.

    It exits explicitly instead of using assert.

  • release-candidate.yml: the build job fails unless the wheel contains contextmesh/GRAPH.md and no other GRAPH.md. The core-wheel job (Linux and Windows) fails if contextmesh.__version__, contextmesh_mcp.__version__ and the installed metadata disagree.

  • RELEASE_NOTES.md: an Unreleased section describing these fixes.

The version stays at 0.1.1. Getting a correct __version__ onto PyPI needs a 0.1.2 release, which this PR does not cut.

Verification

  • python -m unittest discover -s tests from the checkout: 989 tests, OK. ruff check . is clean, and ops/leak-check.sh origin/main reports clean.

  • A wheel built from a clean checkout contains only contextmesh/GRAPH.md. Installed in a fresh venv, all three version strings are 0.1.1, the ontology loads from inside the package, there is no loose site-packages/GRAPH.md, the MCP schema resource reads the file, and contextmesh --help works.

  • With pip install -e ., the versions agree, the ontology reads the root GRAPH.md, and no copy is written into the package.

  • I ran the publish identity script, extracted from the YAML, against real builds:

    Build Tag GitHub pre-release flag Result
    0.1.1 v0.1.1 false passes
    0.1.1 v0.1.1 true fails
    0.1.1 v0.1.2 false fails
    0.1.1 v0.1.2 under PYTHONOPTIMIZE=2 false still fails
    0.1.1 non-v tag false fails
    0.1.1 branch ref false fails
    1.0.0rc1 v1.0.0-rc.1 true passes
    1.0.0rc1 v1.0.0-rc.1 false fails
  • The new release-candidate checks fail against the old 0.1.1 wheel layout (top-level GRAPH.md) and the old installed versions (0.1.0 against 0.1.1), and pass on the new build.

Note: a checkout that still has the old, git-ignored contextmesh_graph.egg-info/ can carry the stale contextmesh/../GRAPH.md entry into a local build. Delete it before building locally. CI starts from a fresh checkout, and the new wheel-layout check catches it if it ever happens.

Not in this PR

These are left for separate decisions:

  • the name clash with the unrelated contextmesh PyPI package;
  • the corrected history in the v0.1.0 release notes;
  • pinning actions to commit SHAs and pinning build tools;
  • running the release-candidate gate from publish.yml;
  • absolute links in the README for the PyPI page.

🤖 Generated with Claude Code


Generated by Claude Code

The 0.1.1 wheel on PyPI reports __version__ "0.1.0" from both packages
because the version was written by hand in three places and the publish
check compared the tag against only pyproject.toml. The version now lives
once, in contextmesh/__init__.py; pyproject.toml reads it as dynamic
metadata and contextmesh_mcp re-exports it.

GRAPH.md was shipped through package data "../GRAPH.md", which installs it
loose at the top of site-packages where any other distribution's GRAPH.md
can overwrite or delete it. A build_py hook in setup.py now copies it into
contextmesh/, MANIFEST.in keeps it in the sdist, and the ontology loader
prefers the packaged copy, falling back to the repository root for source
checkouts and editable installs.

The publish workflow now verifies release identity against the built
wheel's metadata: tag and version are compared as PEP 440 versions and
filenames use the normalized form, so v1.0.0-rc.1 publishes 1.0.0rc1. A
GitHub pre-release must carry a pre-release version and a full release
must not. The checks exit explicitly instead of using assert, which
python -O strips. The release-candidate gate now fails a wheel without
exactly contextmesh/GRAPH.md, and an installed wheel whose version
strings disagree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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