From 41161841ecbded155a63dd46301c7ff59c5f15c1 Mon Sep 17 00:00:00 2001 From: sunjin12 Date: Sun, 19 Apr 2026 23:44:32 +0900 Subject: [PATCH] fix(version): resolve __version__ from importlib.metadata, not literal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both cli/ossmate and mcp/ossmate_mcp had `__version__ = "0.0.1"` literals in their __init__.py — bump_version.py never touched these so `ossmate version` printed "0.0.1" while pip showed 0.1.0. Now both read from importlib.metadata.version() and agree with the installed wheel. Adds invariant test test_init_modules_dont_hardcode_versions to forbid the hardcoded form. Caught one day after v0.1.0 shipped — will roll into the next release rather than burning a 0.1.1 cycle. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 3 +++ cli/ossmate/src/ossmate/__init__.py | 7 ++++++- mcp/ossmate_mcp/src/ossmate_mcp/__init__.py | 7 ++++++- tests/test_versioning.py | 22 +++++++++++++++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af348d4..f886b18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed +- `ossmate version` and `ossmate_mcp.__version__` resolved to the hardcoded literal `"0.0.1"` instead of the installed package version. Both `__init__.py` modules now read from `importlib.metadata.version()` so they always agree with what `pip show` reports. New invariant test [tests/test_versioning.py](tests/test_versioning.py)::`test_init_modules_dont_hardcode_versions` forbids the hardcoded form going forward — caught the day after v0.1.0 shipped to PyPI + ## [0.1.0] - 2026-04-19 First public release. Reference implementation of every Claude Code extension surface, packaged as both a plugin and a standalone CLI. diff --git a/cli/ossmate/src/ossmate/__init__.py b/cli/ossmate/src/ossmate/__init__.py index 5b81ee4..e829409 100644 --- a/cli/ossmate/src/ossmate/__init__.py +++ b/cli/ossmate/src/ossmate/__init__.py @@ -1,3 +1,8 @@ """Ossmate — Claude-powered co-maintainer CLI.""" -__version__ = "0.0.1" +from importlib.metadata import PackageNotFoundError, version as _pkg_version + +try: + __version__ = _pkg_version("ossmate") +except PackageNotFoundError: + __version__ = "0.0.0+source" diff --git a/mcp/ossmate_mcp/src/ossmate_mcp/__init__.py b/mcp/ossmate_mcp/src/ossmate_mcp/__init__.py index ff6c0cd..bcbe141 100644 --- a/mcp/ossmate_mcp/src/ossmate_mcp/__init__.py +++ b/mcp/ossmate_mcp/src/ossmate_mcp/__init__.py @@ -1,3 +1,8 @@ """Ossmate MCP server — OSS maintainer tools exposed via Model Context Protocol.""" -__version__ = "0.0.1" +from importlib.metadata import PackageNotFoundError, version as _pkg_version + +try: + __version__ = _pkg_version("ossmate-mcp") +except PackageNotFoundError: + __version__ = "0.0.0+source" diff --git a/tests/test_versioning.py b/tests/test_versioning.py index 573d268..5d52d9a 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -115,6 +115,28 @@ def test_marketplace_manifest_has_two_version_locations(self): assert "metadata" in data and "version" in data["metadata"] assert data["plugins"] and "version" in data["plugins"][0] + def test_init_modules_dont_hardcode_versions(self): + """`__init__.py` files MUST resolve __version__ from importlib.metadata + rather than hardcoding the literal — otherwise `bump_version.py` + misses them and `ossmate version` lies to users (regression caught + in v0.1.0 post-release).""" + for init in ( + REPO_ROOT / "cli" / "ossmate" / "src" / "ossmate" / "__init__.py", + REPO_ROOT / "mcp" / "ossmate_mcp" / "src" / "ossmate_mcp" / "__init__.py", + ): + text = init.read_text(encoding="utf-8") + assert "importlib.metadata" in text, ( + f"{init.relative_to(REPO_ROOT)} must read __version__ from " + f"importlib.metadata — hardcoding drifts on every release" + ) + # Belt-and-suspenders: forbid the literal `__version__ = "X.Y.Z"` form. + import re as _re + + assert not _re.search(r'^__version__\s*=\s*"\d', text, _re.MULTILINE), ( + f"{init.relative_to(REPO_ROOT)} hardcodes __version__ — use " + f"importlib.metadata.version() instead" + ) + def test_cli_dep_pin_matches_mcp_version(self, bump): """The CLI declares `ossmate-mcp>=X` — X must equal the MCP package version, otherwise users get an unsolvable resolver state on first