#!/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 creates and pushes the new tag: # # 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] # # --dry-run : only print the computed next version, do not tag. # --push : also `git push origin` the new tag (default: local only). # --major|--minor|--patch : force the increment, overriding commit detection. # # Examples: # scripts/bump_version.sh --dry-run # preview next version # scripts/bump_version.sh # tag locally per commits # 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 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 ;; *) 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 if [ "$DRY_RUN" = "1" ]; then echo "── DRY RUN ─────────────────────────────" echo " Prochain tag : v${NEXT}" echo " (aucun tag créé)" exit 0 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 "v${NEXT}" echo "✓ Tag poussé : v${NEXT}" else echo "ℹ Tag local uniquement. Poussez avec: git push origin v${NEXT}" fi echo "Done."