diff --git a/CHANGELOG.md b/CHANGELOG.md index 28a09a9..55b3ec5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,17 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +### Fixed + +- **`__version__` reported `0.1.0` for the whole of `0.1.1`.** The release + bumped the literal in `pyproject.toml` and left the second one in + `claude_batch_runner/__init__.py` behind, so anything reading the package + attribute saw the previous release. `__version__` is now *derived* from + installed metadata, leaving `pyproject.toml` as the single source of truth + (which is what `release.yml` already cross-checks against the git tag), and + `tests/test_version.py` fails if the two ever disagree again. A source + checkout that was never pip-installed reports `0.0.0.dev0`. + ### Added - **`cwd` and `permission_mode` on `call_agent`** — two optional keyword-only diff --git a/claude_batch_runner/__init__.py b/claude_batch_runner/__init__.py index b686ff3..925c41d 100644 --- a/claude_batch_runner/__init__.py +++ b/claude_batch_runner/__init__.py @@ -10,6 +10,9 @@ Public API is re-exported here. """ +from importlib.metadata import PackageNotFoundError +from importlib.metadata import version as _installed_version + from claude_batch_runner.driver import ( DEFAULT_AGENT_TIMEOUT_S, AgentError, @@ -52,4 +55,10 @@ "verify_unit", ] -__version__ = "0.1.0" +# Derived, never written by hand. `pyproject.toml` is the single source of +# truth — release.yml already cross-checks it against the git tag — and a +# second literal here drifted the moment 0.1.1 bumped one and not the other. +try: + __version__ = _installed_version("claude-batch-runner") +except PackageNotFoundError: # source checkout, never pip-installed + __version__ = "0.0.0.dev0" diff --git a/tests/test_version.py b/tests/test_version.py new file mode 100644 index 0000000..1cc9911 --- /dev/null +++ b/tests/test_version.py @@ -0,0 +1,35 @@ +"""`__version__` agrees with pyproject.toml. Never spawns a real `claude` process.""" + +from __future__ import annotations + +import tomllib +from pathlib import Path + +import pytest + +import claude_batch_runner + +REPO_ROOT = Path(__file__).resolve().parents[1] +PYPROJECT = REPO_ROOT / "pyproject.toml" + + +def _declared_version() -> str: + with PYPROJECT.open("rb") as fh: + return tomllib.load(fh)["project"]["version"] + + +def test_version_matches_pyproject(): + """0.1.1 bumped pyproject and left `__version__` at 0.1.0 for a whole release. + + `__version__` is now derived from installed metadata rather than a second + literal, so the only way this can disagree is a stale editable install -- + which is itself worth failing on, because every other check in the repo + (release.yml's tag cross-check included) trusts pyproject. + """ + assert claude_batch_runner.__version__ == _declared_version() + + +def test_version_is_not_the_uninstalled_fallback(): + """The PackageNotFoundError branch must not be what the suite exercises.""" + if claude_batch_runner.__version__ == "0.0.0.dev0": + pytest.fail("package is not installed; run `pip install -e .[dev]` before pytest")