fix(version): BUG-047 la version suit chaque livraison (source unique VERSION + bump SemVer automatique)
VERSION (racine) devient la source unique de verite MAJEUR.MINEUR.CORRECTIF, incrementee a chaque commit par le hook prepare-commit-msg (BREAKING -> majeur, feat -> mineur, sinon correctif) ; post-commit rattache les fichiers derives au commit et cree le tag vX.Y.Z, publie au push (push.followTags). Tous les derives sont resynchronises : package.json, desktop Tauri, README.md/README.fr.md, docs/ROADMAP.md, CHANGELOG.md. Backend, image Docker et desktop lisent le meme fichier (plus de numero code en dur). Outils : scripts/bump_version.py, scripts/install-hooks.sh. Tests : tests/test_version.py (46, garde-fou de coherence globale).
This commit is contained in:
+51
-21
@@ -1,23 +1,34 @@
|
||||
"""
|
||||
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.
|
||||
Source unique de vérité : le fichier `VERSION` à la racine du dépôt, au format
|
||||
`MAJEUR.MINEUR.CORRECTIF` (incrémenté à chaque livraison par
|
||||
`scripts/bump_version.py`, hook git `commit-msg`). get_version() retourne
|
||||
toujours une chaîne propre "x.y.z" — jamais "-dev", "0.0.0-dev" ou "-N-gHASH" :
|
||||
le même numéro s'affiche dans le badge d'en-tête, la boîte À propos et /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"
|
||||
Les sources sont consultées dans cet ordre :
|
||||
1. variable d'environnement OBSIGATE_VERSION (surcharge explicite / tests) ;
|
||||
2. `VERSION` à la racine du dépôt (source unique, copiée dans l'image Docker) ;
|
||||
3. `backend/VERSION` (ancien emplacement, encore produit par certains builds) ;
|
||||
4. dernier tag git (`git describe --tags --abbrev=0`) ;
|
||||
5. "0.0.0".
|
||||
|
||||
Exemples :
|
||||
VERSION = 2.3.0 -> "2.3.0"
|
||||
tag v2.3.0, 4 commits -> "2.3.0"
|
||||
aucun VERSION / git -> "0.0.0"
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
_ROOT = Path(__file__).resolve().parent.parent # ObsiGate repo root
|
||||
_VERSION_FILE = Path(__file__).resolve().parent / "VERSION"
|
||||
_ROOT = Path(__file__).resolve().parent.parent # racine du dépôt ObsiGate
|
||||
VERSION_FILE = _ROOT / "VERSION" # source unique de vérité
|
||||
LEGACY_VERSION_FILE = Path(__file__).resolve().parent / "VERSION" # backend/VERSION
|
||||
_ENV_VAR = "OBSIGATE_VERSION"
|
||||
|
||||
|
||||
def _run_git(args: list[str]) -> str:
|
||||
@@ -57,6 +68,16 @@ def _clean_base(raw: str) -> str:
|
||||
return ".".join(nums)
|
||||
|
||||
|
||||
def _read_version_file(path: Path) -> str:
|
||||
"""Clean base of a VERSION file, or '' when absent/unreadable/invalid."""
|
||||
try:
|
||||
if not path.exists():
|
||||
return ""
|
||||
return _clean_base(path.read_text(encoding="utf-8", errors="replace"))
|
||||
except OSError:
|
||||
return ""
|
||||
|
||||
|
||||
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")
|
||||
@@ -68,23 +89,32 @@ def get_git_commit() -> str:
|
||||
|
||||
|
||||
def get_version() -> str:
|
||||
"""Return the clean release version x.y.z (latest tag) — never a -suffix.
|
||||
"""Return the clean release version x.y.z — never a -suffix.
|
||||
|
||||
Priority: latest git tag -> backend/VERSION file -> "0.0.0".
|
||||
Priority: OBSIGATE_VERSION -> ./VERSION -> backend/VERSION -> latest git tag
|
||||
-> "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)
|
||||
# 1) Explicit override (docker-compose, tests, builds hors dépôt)
|
||||
override = _clean_base(os.environ.get(_ENV_VAR, ""))
|
||||
if override:
|
||||
return override
|
||||
|
||||
# 2) Source unique de vérité : VERSION à la racine du dépôt
|
||||
base = _read_version_file(VERSION_FILE)
|
||||
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) Ancien emplacement (backend/VERSION, baké par certains builds)
|
||||
base = _read_version_file(LEGACY_VERSION_FILE)
|
||||
if base:
|
||||
return base
|
||||
|
||||
# 3) Nothing available
|
||||
# 4) Dernier tag git
|
||||
base = _clean_base(_run_git(["describe", "--tags", "--abbrev=0"]))
|
||||
if base:
|
||||
return base
|
||||
|
||||
# 5) Rien d'exploitable
|
||||
return "0.0.0"
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user