Maintenir un thème WordPress propre, versionné et documenté est une bonne pratique à la fois pour les projets professionnels et personnels. L’un des éléments clés de cette bonne hygiène de code est le changelog.

Pourquoi tenir un changelog ?

Un changelog est un fichier (souvent nommé changelog.md) qui liste l’historique des modifications apportées à un projet, version par version. Il permet :

  • De suivre l’évolution du projet dans le temps.
  • De communiquer les modifications aux utilisateurs, aux clients ou à d’autres développeurs.
  • De mieux gérer les releases, les correctifs, les ajouts de fonctionnalités, etc.

Dans le cas d’un thème WordPress, le fichier style.css contient un champ Version: qui indique la version active du thème. Il semble assez naturel que cette version soit synchronisée avec votre changelog.

Modalités d’incrémentation de version

Nous prendrons comme référence les spécifications du Semantic Versioning pour tenir des comptes des différents niveaux de mises à jour : major, minor, ou patch ;

  • major : version majeure, pour des changements incompatibles de l’API ou de gros refactors ;
  • minor : version mineure, pour des ajouts de fonctionnalités compatibles ;
  • patch : correctif mineur ou correction de bug.

Si vous mettez en place ce changelog au début du projet, il est recommandé de commencer par le numéro de version 0.1.0. Je vous laisse le soin de reporter ce numéro dans votre fichier style.css.

Le script de mise à jour de changelog

Contraintes pour le script

Nous allons commencer par un script minimaliste qui permettra simplement :

  • D’incrémenter la version courante ;
  • De mettre à jour le champ Version: dans le fichier style.css de votre thème ;
  • D’ajouter une nouvelle entrée dans changelog.md avec les informations suivantes : numéro de version, date de la version, nom et description de la mise à jour.

Contenu du script bash : changelog.sh

Ce script vous demandera interactivement :

  • ???? Un titre pour la mise à jour (par exemple : « Ajout d’un composant Footer »)
  • ????️ Une description multilignes (par exemple : liste des modifications, comportements corrigés, etc.). La description s’arrête lorsque vous appuyez sur Ctrl+D.
#!/bin/bash

STYLE_FILE="style.css"
CHANGELOG_FILE="changelog.md"

current_version=$(grep -E "^Version:" "$STYLE_FILE" | awk '{print $2}')
IFS='.' read -r major minor patch <<< "$current_version"

# Lire l'argument de ligne de commande pour le niveau
arg=${1:--patch}

case "$arg" in
  -major)
    major=$((major + 1)); minor=0; patch=0;;
  -minor)
    minor=$((minor + 1)); patch=0;;
  -patch|"")
    patch=$((patch + 1));;
  *)
    echo "Usage: $0 [-major | -minor | -patch]"
    exit 1;;
esac

new_version="$major.$minor.$patch"

# Modifier la version dans style.css
sed -i -E "s/^Version:.*$/Version:    $new_version/" "$STYLE_FILE"

# Demander les informations pour le changelog
read -p "???? Titre de la mise à jour : " title
echo "????️ Description (finir par Ctrl+D) :"
description=$(cat)

today=$(date +"%Y-%m-%d")
tmp_file=$(mktemp)
{
  echo "## [$new_version] - $today"
  echo "### $title"
  echo "$description"
  echo ""
  cat "$CHANGELOG_FILE"
} > "$tmp_file"
mv "$tmp_file" "$CHANGELOG_FILE"

echo "✅ Version mise à jour vers $new_version"

Sous macOS, la commande sed n’accepte pas les options de la même façon. Vous devez remplacer la ligne :

sed -i -E "s/^Version:.*$/Version:    $new_version/" "$STYLE_FILE"

Par

sed -i '' -E "s/^Version:.*$/Version:    $new_version/" "$STYLE_FILE"

Exécution du script

Après avoir créé le fichier changelog.sh, pensez à le rendre exécutable avec la commande suivante :

chmod +x changelog.sh

Et voici les différentes façon d’utiliser ensuite le script

  • ./changelog.sh (par défaut : patch)
  • ./changelog.sh -minor
  • ./changelog.sh -major

Evolution du script et gestion de release Git

Modalités

Une fois cette base posée, on peut enrichir le script pour qu’il s’intègre pleinement dans un flux de travail avec Git :

Ce script inclut toutes les fonctionnalités du script minimaliste, mais ajoute en plus :

  • une vérification que le projet est bien dans un dépôt Git avec une source renseignée (remote origin) ;
  • la gestion d’une branche Git nommée selon le type de release et la version (ex : release/minor-1.2.0) ;
  • un commit automatique des fichiers modifiés ;
  • la création d’un tag Git (ex : v1.2.0) ;
  • un push vers le repository Git (facultatif) ;
  • une option --dry-run pour simuler sans rien modifier.

Contenu du script bash : changelog.sh

#!/bin/bash

STYLE_FILE="style.css"
CHANGELOG_FILE="changelog.md"

# ✅ Vérifie que style.css existe
if [[ ! -f "$STYLE_FILE" ]]; then
  echo "❌ Fichier style.css introuvable"
  exit 1
fi

# ✅ Vérifie que le script est bien exécuté avec bash
if [ -z "$BASH_VERSION" ]; then
  echo "❌ Ce script doit être lancé avec bash, pas sh"
  exit 1
fi

# === DRY RUN SUPPORT ===
dry_run=false
if [[ "$1" == "--dry-run" ]]; then
  dry_run=true
  shift
fi

# === Détection de fins de lignes Windows (CRLF)
if file "$STYLE_FILE" | grep -q "CRLF"; then
  echo "⚠️  Le fichier $STYLE_FILE contient des fins de ligne Windows (CRLF)"
  echo "➡️  Cela peut empêcher la détection de la version"
  echo "???? Corrige avec : dos2unix $STYLE_FILE"
  exit 1
fi

# === Lire la version depuis style.css ===
current_version=$(grep -E "^Version:" "$STYLE_FILE" | awk '{print $2}')

if [[ -z "$current_version" || ! "$current_version" =~ ^[0-9]+(\.[0-9]+){0,2}$ ]]; then
  echo "⚠️  Ligne 'Version:' introuvable ou mal formatée. Utilisation de 0.0.0"
  current_version="0.0.0"
fi

IFS='.' read -r major minor patch <<< "$current_version"
major=${major:-0}
minor=${minor:-0}
patch=${patch:-0}

# Sécurité : forcer des entiers
if ! [[ "$major" =~ ^[0-9]+$ ]]; then major=0; fi
if ! [[ "$minor" =~ ^[0-9]+$ ]]; then minor=0; fi
if ! [[ "$patch" =~ ^[0-9]+$ ]]; then patch=0; fi

# === Choix du bump
arg=${1:--revision}
case "$arg" in
  -major)
    major=$((major + 1)); minor=0; patch=0;;
  -minor)
    minor=$((minor + 1)); patch=0;;
  -revision|-patch|"")
    patch=$((patch + 1));;
  *)
    echo "Usage: $0 [--dry-run] [-major | -minor | -revision]"
    exit 1
    ;;
esac

new_version="${major}.${minor}.${patch}"
branch_type="${arg#-}"
branch_type=${branch_type:-revision}
branch_name="release/${branch_type}-${new_version}"

if $dry_run; then
  echo "???? DRY RUN ACTIVÉ"
  echo "➡️  Ancienne version : $current_version"
  echo "➡️  Nouvelle version : $new_version"
  echo "➡️  Branche prévue    : $branch_name"
  echo "➡️  Tag prévu         : v$new_version"
fi

# === Modifier style.css
if ! $dry_run; then
  unameOut="$(uname)"
  if [[ "$unameOut" == "Darwin" ]]; then
    sed -i '' -E "s/^Version:.*$/Version:    $new_version/" "$STYLE_FILE"
  else
    sed -i -E "s/^Version:.*$/Version:    $new_version/" "$STYLE_FILE"
  fi

  if ! grep -q "Version:    $new_version" "$STYLE_FILE"; then
    echo "❌ Échec de la mise à jour dans style.css"
    exit 1
  fi
else
  echo "???? [dry-run] style.css serait modifié avec : Version:    $new_version"
fi

# === Changelog
read -p "???? Titre de la mise à jour : " title
echo "????️ Description (finir par Ctrl+D) :"
description=$(cat)
today=$(date +"%Y-%m-%d")
new_entry=$'\n'"## [$new_version] - $today"$'\n'"### $title"$'\n'"$description"$'\n'

if ! $dry_run; then
  touch "$CHANGELOG_FILE"
  tmp_file=$(mktemp)
  { echo "$new_entry"; cat "$CHANGELOG_FILE"; } > "$tmp_file"
  mv "$tmp_file" "$CHANGELOG_FILE"
else
  echo "???? [dry-run] changelog.md serait modifié avec :"
  echo "$new_entry"
fi

# === Git (sur demande)
if git rev-parse --is-inside-work-tree >/dev/null 2>&1 && ! $dry_run; then
  echo ""
  read -p "???? Souhaitez-vous créer une release pour Git (branche, commit, tag) ? (y/N) " git_action
  if [[ "$git_action" =~ ^[Yy]$ ]]; then

    if ! git rev-parse --verify --quiet "$branch_name"; then
      git checkout -b "$branch_name"
      echo "???? Branche créée : $branch_name"
    else
      git checkout "$branch_name"
      echo "???? Branche existante : $branch_name"
    fi

    for file in "$STYLE_FILE" "$CHANGELOG_FILE"; do
      if git check-ignore "$file" >/dev/null 2>&1; then
        echo "⚠️  Le fichier '$file' est ignoré par .gitignore. Forçage de l'ajout..."
        git add -f "$file"
      else
        git add "$file"
      fi
    done

    git commit -m "release $new_version" >/dev/null 2>&1 && echo "✅ Commit créé"
    git tag "v$new_version" -m "Version $new_version" && echo "????️  Tag v$new_version créé"

    echo ""
    # Vérification du remote 'origin'
    if git remote | grep -q "^origin$"; then
      read -p "???? Souhaitez-vous pousser la branche et le tag ? (y/N) " push_answer
      if [[ "$push_answer" =~ ^[Yy]$ ]]; then
        git push --set-upstream origin "$branch_name" >/dev/null 2>&1 \
          && echo "???? Branche poussée" || echo "⚠️ Échec du push (branche)"
        git push --tags >/dev/null 2>&1 \
          && echo "????️  Tag poussé" || echo "⚠️ Échec du push (tag)"
      else
        echo "???? Push annulé"
      fi
    else
      echo "❗ Aucun remote Git nommé 'origin' n’est défini. Impossible de pousser."
      echo "???? Ajoutez-en un avec : git remote add origin <url>"
    fi

  else
    echo "???? Partie Git ignorée — aucun commit/branche/tag"
  fi
elif ! $dry_run; then
  echo "ℹ️ Aucun dépôt Git ici — la partie Git est ignorée"
fi

if $dry_run; then
  echo "✅ DRY RUN terminé (aucune modification)"
fi

Ce script permet une gestion rigoureuse des évolutions de votre thème WordPress. Vous pouvez également faire une release propre, taguée, documentée et versionnée en une seule commande.