#!/usr/bin/env bash # ----------------------------------------------------------------------------- # ObsiGate — version bump helper (SemVer MAJOR.MINOR.PATCH) # # 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). # # 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) # # Usage: # scripts/bump_version.sh [--major|--minor|--patch] [--dry-run] [--push] [--no-files] # # --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 # ----------------------------------------------------------------------------- set -euo pipefail cd "$(git rev-parse --show-toplevel)" # run from repo root regardless of cwd BRANCH="$(git branch --show-current)" # ── 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 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." 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."