"""One release version, everywhere it's hand-maintained. Several of these are genuinely independent sources (pyproject.toml files, package.json, two hand-written SDK version constants that each carry their own "kept in sync by hand" comment) -- nothing generates them from a single source today, so this is the guard against them silently drifting apart. """ from __future__ import annotations import json import re import tomllib from pathlib import Path import yaml REPO_ROOT = Path(__file__).resolve().parents[2] def _toml_version(path: Path) -> str: with path.open("rb") as handle: document = tomllib.load(handle) return str(document["project"]["version"]) def _package_json_version(path: Path) -> str: document = json.loads(path.read_text(encoding="utf-8")) return str(document["version"]) def _package_lock_versions(path: Path) -> dict[str, str]: """Both places an npm lockfile records the package's own version. A lockfile is generated, but only by a command someone has to run: editing ``package.json`` by hand leaves it behind, which is exactly what happened (``package.json`` said 1.2.0 while the lockfile still said 0.1.0 -- the version this package carried before its first release). It states the version twice, at the document root and in the root entry of ``packages``, and both are what a consumer or a CI cache reads, so both are checked. """ document = json.loads(path.read_text(encoding="utf-8")) return { "sdk/typescript/package-lock.json": str(document["version"]), 'sdk/typescript/package-lock.json packages[""]': str(document["packages"][""]["version"]), } def _uv_lock_versions(path: Path) -> dict[str, str]: """The workspace members' versions as the resolved Python lockfile has them. ``uv`` rewrites this whenever it syncs, so it normally heals itself -- which is exactly why a stale entry is worth asserting on: it means the committed lockfile was produced before the version bump and nobody has run the project's own tooling since. """ with path.open("rb") as handle: document = tomllib.load(handle) members = {"ragcore", "ragcore-sdk"} return { f"uv.lock [{package['name']}]": str(package["version"]) for package in document["package"] if package["name"] in members } def _openapi_version(path: Path) -> str: document = yaml.safe_load(path.read_text(encoding="utf-8")) return str(document["info"]["version"]) def _constant_version(path: Path, pattern: str) -> str: match = re.search(pattern, path.read_text(encoding="utf-8")) assert match is not None, f"could not find a version constant in {path}" return match.group(1) def test_every_hand_maintained_version_string_matches() -> None: versions = { "pyproject.toml": _toml_version(REPO_ROOT / "pyproject.toml"), "src/ragcore/__init__.py": _constant_version( REPO_ROOT / "src" / "ragcore" / "__init__.py", r'__version__ = "([^"]+)"' ), "sdk/python/pyproject.toml": _toml_version(REPO_ROOT / "sdk" / "python" / "pyproject.toml"), "sdk/python/src/ragcore_sdk/_version.py": _constant_version( REPO_ROOT / "sdk" / "python" / "src" / "ragcore_sdk" / "_version.py", r'__version__ = "([^"]+)"', ), "sdk/typescript/package.json": _package_json_version( REPO_ROOT / "sdk" / "typescript" / "package.json" ), **_package_lock_versions(REPO_ROOT / "sdk" / "typescript" / "package-lock.json"), "sdk/typescript/src/client.ts": _constant_version( REPO_ROOT / "sdk" / "typescript" / "src" / "client.ts", r'_SDK_VERSION = "([^"]+)"', ), **_uv_lock_versions(REPO_ROOT / "uv.lock"), # The binding REST contract carries the release version too; it used # to lag behind (1.0.0 while the code was 1.1.0). "docs/contracts/openapi.yaml": _openapi_version( REPO_ROOT / "docs" / "contracts" / "openapi.yaml" ), } distinct = set(versions.values()) assert len(distinct) == 1, f"version strings have diverged: {versions}" def test_the_shared_version_is_the_current_release() -> None: with (REPO_ROOT / "pyproject.toml").open("rb") as handle: document = tomllib.load(handle) assert document["project"]["version"] == "1.2.1"