""" Version management for ObsiGate. Uses git describe to generate a SemVer-compatible version string. Format: MAJOR.MINOR.PATCH[-commits-since-tag-gHASH] Examples: v1.7.0 (exact tag) v1.7.0-3-gabc1234 (3 commits after v1.7.0) v1.7.0-dev (fallback when Git unavailable) """ from __future__ import annotations import subprocess from pathlib import Path _ROOT = Path(__file__).resolve().parent.parent # ObsiGate repo root def get_version() -> str: """Get the current version from Git tags.""" try: result = subprocess.run( ["git", "describe", "--tags", "--dirty=-dirty"], cwd=str(_ROOT), capture_output=True, text=True, timeout=5, check=False, ) if result.returncode == 0: tag = result.stdout.strip() # git describe returns v1.7.0 or v1.7.0-3-gabc1234 # Remove leading 'v' if present tag = tag.removeprefix("v") return tag except (FileNotFoundError, subprocess.TimeoutExpired, OSError): pass # Fallback: try reading from a VERSION file next to this module version_file = Path(__file__).parent / "VERSION" if version_file.exists(): return version_file.read_text().strip() return "0.0.0-dev" def get_version_tuple() -> tuple[int, int, int]: """Return (major, minor, patch) tuple for programmatic use.""" raw = get_version() # Strip -suffix for parsing base = raw.split("-")[0] try: parts = base.split(".") major = int(parts[0]) if len(parts) > 0 else 0 minor = int(parts[1]) if len(parts) > 1 else 0 patch = int(parts[2]) if len(parts) > 2 else 0 return (major, minor, patch) except (ValueError, IndexError): return (0, 0, 0) if __name__ == "__main__": print(get_version())