fix(version): BUG-047 la version suit chaque livraison (source unique VERSION + bump SemVer automatique)
CI / lint (push) Successful in 1m27s
CI / security (push) Successful in 57s
CI / test (push) Successful in 3m11s
CI / build (push) Successful in 54s
CI / e2e (push) Successful in 10m59s

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:
2026-09-16 11:02:47 -04:00
parent 3261917d62
commit d98c0c2f33
24 changed files with 1423 additions and 249 deletions
+20 -151
View File
@@ -1,165 +1,34 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# ObsiGate — version bump helper (SemVer MAJOR.MINOR.PATCH)
# ObsiGate — version bump helper (SemVer MAJEUR.MINEUR.CORRECTIF)
#
# The canonical version is the latest release tag. This tool inspects the
# commits since that tag and decides the next version from their type
# (Conventional Commits), then:
# 1. synchronise la version desktop (tauri.conf.json, Cargo.toml, Cargo.lock) ;
# 2. commit ce bump (`chore(release): vX.Y.Z`) ;
# 3. crée le tag annoté (et pousse branche + tag avec --push).
# Wrapper historique : toute la logique vit dans `scripts/bump_version.py`
# (source unique de vérité : `./VERSION`, incrément inspiré des
# Conventional Commits).
#
# breaking change / `!:` / "BREAKING CHANGE" -> MAJOR bump (x.0.0)
# feat / feature -> MINOR bump (x.y.0)
# anything else (fix, perf, refactor, ...) -> PATCH bump (x.y.z)
# breaking (`!:` / BREAKING CHANGE) -> MAJEUR (x.0.0)
# `feat` -> MINEUR (x.y.0)
# tout le reste (fix, perf, …) -> CORRECTIF (x.y.z)
#
# Usage:
# scripts/bump_version.sh [--major|--minor|--patch] [--dry-run] [--push] [--no-files]
# Usage :
# scripts/bump_version.sh --dry-run # prévisualiser
# scripts/bump_version.sh # bump + synchro des fichiers
# scripts/bump_version.sh --minor --commit --tag --push
# scripts/bump_version.sh --print-version
#
# --dry-run : affiche la prochaine version sans rien modifier ni tagger.
# --push : pousse la branche courante ET le nouveau tag vers origin.
# --no-files : ne met pas à jour les fichiers desktop (tag seul).
# --major|--minor|--patch : force l'incrément, ignore la détection.
#
# Examples:
# scripts/bump_version.sh --dry-run # preview next version
# scripts/bump_version.sh # bump + commit + tag (local)
# scripts/bump_version.sh --push # idem + push branche & tag
# scripts/bump_version.sh --patch --push # force patch + push
# Voir `scripts/bump_version.py --help` pour la liste complète des options.
# -----------------------------------------------------------------------------
set -euo pipefail
cd "$(git rev-parse --show-toplevel)" # run from repo root regardless of cwd
BRANCH="$(git branch --show-current)"
root="$(git rev-parse --show-toplevel)"
# ── Parse args ────────────────────────────────────────────────────────────
FORCE=""; DRY_RUN=0; DO_PUSH=0; DO_FILES=1
for a in "$@"; do
case "$a" in
--major) FORCE=major ;;
--minor) FORCE=minor ;;
--patch) FORCE=patch ;;
--dry-run) DRY_RUN=1 ;;
--push) DO_PUSH=1 ;;
--no-files) DO_FILES=0 ;;
*) echo "✗ Argument inconnu: $a"; exit 2 ;;
esac
py=""
for cand in python3 python; do
if command -v "$cand" >/dev/null 2>&1; then py="$cand"; break; fi
done
# ── Current version = latest tag base ─────────────────────────────────────
CURRENT="$(git describe --tags --abbrev=0 2>/dev/null || echo "0.0.0")"
CURRENT="${CURRENT#v}"
IFS='.' read -r MAJ MIN PAT <<< "$CURRENT"
MAJ="${MAJ:-0}"; MIN="${MIN:-0}"; PAT="${PAT:-0}"
echo "Version actuelle : $CURRENT (branche: $BRANCH)"
# ── Detect bump type from commits since last tag ──────────────────────────
if [ -z "$FORCE" ]; then
# Use HEAD~1..HEAD when no tag exists yet (all commits "since" nothing).
RANGE="${CURRENT#0.0.0}" # if 0.0.0 fallback there may be no tag at all
if git rev-parse "v${CURRENT}" >/dev/null 2>&1; then
RANGE="v${CURRENT}..HEAD"
else
RANGE="HEAD~10..HEAD"
echo "⚠ Aucun tag v${CURRENT} trouvé — analyse des 10 derniers commits."
fi
LOG="$(git log --format='%s%n%b' "$RANGE" 2>/dev/null || true)"
BUMP="patch" # default
if echo "$LOG" | grep -qE '^[a-zA-Z]+\([^)]*\)!:|\!:\s|BREAKING CHANGE:'; then
BUMP="major"
elif echo "$LOG" | grep -qiE '^(feat|feature)(\(|:|\s)'; then
BUMP="minor"
fi
# If nothing meaningful in range, keep patch
echo "Détection (${RANGE}) : changement ${BUMP}"
else
BUMP="$FORCE"
echo "Incrément forcé : ${BUMP}"
fi
# ── Compute next version ──────────────────────────────────────────────────
case "$BUMP" in
major) MAJ=$((MAJ + 1)); MIN=0; PAT=0 ;;
minor) MIN=$((MIN + 1)); PAT=0 ;;
*) PAT=$((PAT + 1)) ;;
esac
NEXT="${MAJ}.${MIN}.${PAT}"
# ── Guard: tag already exists? ────────────────────────────────────────────
if git rev-parse "v${NEXT}" >/dev/null 2>&1; then
echo "✗ Le tag v${NEXT} existe déjà. Corrigez l'incrément ou retirez le tag."
if [ -z "$py" ]; then
echo "✗ Aucun interpréteur Python trouvé (python3 / python)." >&2
exit 1
fi
# ── Fichiers desktop à synchroniser ───────────────────────────────────────
DESKTOP_CONF="desktop/tauri.conf.json"
DESKTOP_CARGO="desktop/Cargo.toml"
DESKTOP_LOCK="desktop/Cargo.lock"
# Met à jour la version dans les fichiers desktop (in-place, portable GNU sed).
sync_desktop_version() {
local ver="$1"
if [ -f "$DESKTOP_CONF" ]; then
sed -i -E 's/^([[:space:]]*"version"[[:space:]]*:[[:space:]]*")[^"]*(")/\1'"$ver"'\2/' "$DESKTOP_CONF"
fi
if [ -f "$DESKTOP_CARGO" ]; then
# Première ligne `version = "..."` = version du [package].
sed -i -E '0,/^version = "[^"]*"/s//version = "'"$ver"'"/' "$DESKTOP_CARGO"
fi
if [ -f "$DESKTOP_LOCK" ]; then
# Bloc `name = "obsigate-desktop"` -> ligne `version` suivante.
sed -i -E '/^name = "obsigate-desktop"$/{n;s/^version = "[^"]*"/version = "'"$ver"'"/;}' "$DESKTOP_LOCK"
fi
}
if [ "$DRY_RUN" = "1" ]; then
echo "── DRY RUN ─────────────────────────────"
echo " Prochain tag : v${NEXT}"
if [ "$DO_FILES" = "1" ]; then
echo " Fichiers desktop à synchroniser :"
echo " - $DESKTOP_CONF"
echo " - $DESKTOP_CARGO"
echo " - $DESKTOP_LOCK (obsigate-desktop)"
fi
echo " (aucune modification, aucun tag créé)"
exit 0
fi
# ── Sync + commit des versions desktop ────────────────────────────────────
if [ "$DO_FILES" = "1" ]; then
echo "─ Synchronisation de la version desktop → ${NEXT}"
sync_desktop_version "$NEXT"
CHANGED_FILES=()
for f in "$DESKTOP_CONF" "$DESKTOP_CARGO" "$DESKTOP_LOCK"; do
[ -f "$f" ] && CHANGED_FILES+=("$f")
done
if git diff --quiet -- "${CHANGED_FILES[@]}"; then
echo "ℹ Aucun changement de version desktop à committer."
else
git add -- "${CHANGED_FILES[@]}"
git commit -m "chore(release): v${NEXT}" \
-m "Synchronise la version desktop (tauri.conf.json, Cargo.toml, Cargo.lock)."
echo "✓ Commit de release créé : v${NEXT}"
fi
else
echo "ℹ --no-files : fichiers desktop inchangés."
fi
# ── Create tag ────────────────────────────────────────────────────────────
git tag -a "v${NEXT}" -m "Release v${NEXT} (${BUMP} bump depuis v${CURRENT})"
echo "✓ Tag créé localement : v${NEXT}"
if [ "$DO_PUSH" = "1" ]; then
git push origin "$BRANCH"
git push origin "v${NEXT}"
echo "✓ Branche '$BRANCH' et tag v${NEXT} poussés."
else
echo "ℹ Tag local uniquement. Poussez avec:"
echo " git push origin $BRANCH && git push origin v${NEXT}"
fi
echo "Done."
exec "$py" "$root/scripts/bump_version.py" "$@"