CI / lint (push) Failing after 21s
CI / test (push) Skipped
CI / build (push) Skipped
CI / e2e (push) Skipped
CI / security (push) Successful in 27s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
Corrige l'incoherence d'affichage (page principale 0.0.0-dev vs a propos v2.0.0-dev) et unifie la gestion de version sur une seule source de verite. Backend: - version.py: get_version() retourne toujours le tag x.y.z propre du dernier tag (plus de 0.0.0-dev / -N-gHASH dans l'UI). Ajout get_git_describe() et get_git_commit() pour le detail. - main.py: /api/health expose git_describe + git_commit (nouveaux champs). Frontend: - modale A propos (populateAbout): version + commit lus depuis /api/health, suppression du hardcode v2.0.0-dev dans index.html. - section config A propos (loadAbout): lit health.version, plus de state. APP_VERSION obsolete. - badge header: fetch /api/health (fallback neutre '...'). - state.js: suppression de APP_VERSION (1.5.0) devenu mort; imports nettoyes. Script: - scripts/bump_version.sh: incrementation SemVer selon les commits (breaking->major, feat->minor, sinon patch), creer/pousser le tag vX.Y.Z. Tests: 507 passed, frontend validators OK.
101 lines
3.0 KiB
Python
101 lines
3.0 KiB
Python
"""
|
|
Version management for ObsiGate.
|
|
|
|
The canonical version is the numeric SemVer of the LATEST release tag
|
|
(MAJOR.MINOR.PATCH). get_version() always returns a clean "x.y.z" string so
|
|
the UI never shows "-dev", "0.0.0-dev" or a "-N-gHASH" suffix — the same
|
|
number appears in the header badge, the About modal and the API health.
|
|
|
|
Examples:
|
|
tag v2.0.0 -> "2.0.0"
|
|
HEAD 31 commits after -> "2.0.0" (release version, no dev clutter)
|
|
no git / no VERSION -> "0.0.0"
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
_ROOT = Path(__file__).resolve().parent.parent # ObsiGate repo root
|
|
_VERSION_FILE = Path(__file__).resolve().parent / "VERSION"
|
|
|
|
|
|
def _run_git(args: list[str]) -> str:
|
|
"""Run a git command in the repo root; return stdout (stripped) or ''."""
|
|
try:
|
|
result = subprocess.run(
|
|
["git", *args],
|
|
cwd=str(_ROOT),
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=5,
|
|
check=False,
|
|
)
|
|
if result.returncode == 0:
|
|
return result.stdout.strip()
|
|
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
|
|
pass
|
|
return ""
|
|
|
|
|
|
def _clean_base(raw: str) -> str:
|
|
"""Reduce a version string to its numeric MAJOR.MINOR.PATCH base.
|
|
|
|
Handles "v2.0.0", "2.0.0-31-gabc1234", "2.0.0-dev", "0.0.0-dev".
|
|
Returns "" if no numeric triplet can be parsed.
|
|
"""
|
|
s = (raw or "").strip().lstrip("vV")
|
|
base = s.split("-")[0] # strip -N-gHASH / -dev / -dirty suffixes
|
|
parts = base.split(".")
|
|
nums = []
|
|
for p in parts[:3]:
|
|
if not p.isdigit():
|
|
break
|
|
nums.append(str(int(p)))
|
|
if len(nums) != 3:
|
|
return ""
|
|
return ".".join(nums)
|
|
|
|
|
|
def get_git_describe() -> str:
|
|
"""Full `git describe` string (e.g. "2.0.0-31-gabc1234") or '' if no git."""
|
|
return _run_git(["describe", "--tags", "--dirty=-dirty"]).lstrip("v")
|
|
|
|
|
|
def get_git_commit() -> str:
|
|
"""Short HEAD commit hash (e.g. "abc1234") or '' if unavailable."""
|
|
return _run_git(["rev-parse", "--short", "HEAD"])
|
|
|
|
|
|
def get_version() -> str:
|
|
"""Return the clean release version x.y.z (latest tag) — never a -suffix.
|
|
|
|
Priority: latest git tag -> backend/VERSION file -> "0.0.0".
|
|
"""
|
|
# 1) Latest tag from git (works even with commits beyond the tag)
|
|
tag = _run_git(["describe", "--tags", "--abbrev=0"])
|
|
base = _clean_base(tag)
|
|
if base:
|
|
return base
|
|
|
|
# 2) backend/VERSION file (baked at build time by build.sh / CI / Docker)
|
|
if _VERSION_FILE.exists():
|
|
base = _clean_base(_VERSION_FILE.read_text(encoding="utf-8"))
|
|
if base:
|
|
return base
|
|
|
|
# 3) Nothing available
|
|
return "0.0.0"
|
|
|
|
|
|
def get_version_tuple() -> tuple[int, int, int]:
|
|
"""Return (major, minor, patch) tuple for programmatic use."""
|
|
raw = get_version()
|
|
base = _clean_base(raw) or "0.0.0"
|
|
major, minor, patch = (int(p) for p in base.split("."))
|
|
return (major, minor, patch)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print(get_version())
|