Comment bien versionner son site WordPress avec Git et Github

Installé sur plus de 23 % des sites web dans le monde (selon W3Techs), WordPress est aujourd’hui le système de gestion de contenu le plus utilisé.

Même s’il n’est pas toujours très bien vu auprès des développeurs, l’écosystème de cet outil fait de lui un allié de choix pour concevoir un projet web évolutif. C’est un outil open source qui possède une communauté active et une bibliothèque de plugins très riche apportant toute sorte de fonctionnalités en mode « Plug’n’Play » (e‑commerce, générateur de formulaire, …).

En octobre, lors du dernier Forum PHP, une conférence a même été réalisée pour montrer son potentiel et sa fiabilité sur tout type de projets.

Aujourd’hui, j’utilise régulièrement ce CMS pour construire les projets de mes clients. Pour fiabiliser mon travail, j’ai créé une méthode utilisant un système de contrôle de version, Git. Elle me permet de gérer mes projets, les déploiements et les mises à jour du CMS.

Un système de contrôle de version, pourquoi ?

Pour le développement de ce genre de projet, je travaille généralement seul. L’intérêt d’un outil de versioning aurait pu être discutable dans ce contexte où il n’y a pas de travail collaboratif sur le code source.

Toutefois, mes expériences précédentes m’ont montré d’autres avantages :

  • Sauvegarde du code source dans un espace décentralisé et historisé pour éviter de perdre le détail des projets ;
  • Suivi des modifications et retour en arrière, très important au quotidien pour éviter de faire et défaire sans arrêt ;
  • Utilisation des branches et tags pour mettre des marqueurs dans l’historique du projet, ce qui me permet de livrer selon des versions figées ;
  • Utilisation de trois environnements qui doivent être synchronisés facilement (développement sur ma machine locale, préproduction pour valider les évolutions et production pour le site en ligne).

Le versioning est donc un allié de taille que j’ai choisi d’intégrer dans mon workflow.

La structure du projet WordPress

Une structure WordPressPar défaut, WordPress héberge les contenus et les thèmes à l’intérieur du répertoire wp-content qui se trouve au coeur de l’installation du CMS.

Pour pouvoir gérer au mieux la procédure d’installation, j’ai préféré utiliser une architecture légèrement différente.

Il est possible depuis WordPress 2.2.6 de déplacer le répertoire wp-content grâce à deux constantes dans le fichier wp-config.php. J’ai choisi de le mettre à la racine du projet :

  • WP_CONTENT_DIR pour définir l’emplacement ;
  • WP_CONTENT_URL pour définir l’URL permettant d’y accéder.

J’ai placé WordPress dans son propre espace nommé wp-cms. Ce répertoire séparé me permet de pouvoir mettre à jour le CMS très facilement en remplaçant la totalité de son contenu.

J’ai déplacé le fichier wp-config.php dans le répertoire parent de l’installation du CMS, ce qui évite de l’écraser lors des mises à jour.

Cette structure de répertoires un peu particulière me permet d’avoir une séparation nette entre les différentes sections du projet : outils, contenu et configuration.

Ensuite, pour que les modifications apportées n’affectent pas l’expérience utilisateur, il est nécessaire de faire des ajustements dans le fichier .htaccess (pour faire disparaître le répertoire wp-cms des URLs de l’administration) et dans le fichier index.php à la racine du projet.

Mise en place du versioning

Création du dépôt Git

La première étape consiste à créer un dépôt qui va contenir le code source du projet.

Pour cela, il faut exécuter la série de commandes suivante :

Le répertoire contenant le projet est créé et le dépôt initialisé. Ensuite, il faut créer les fichiers index.php et .htaccess basés sur les exemples précédents et les versionner dans le dépôt.

Pour ne pas intégrer dans le dépôt des fichiers qui n’ont pas besoin d’être versionnés, il faut créer un fichier .gitignore avec le contenu suivant :

Intégration de WordPress dans le projet

J’utilise les submodules, une fonctionnalité de Git qui permet d’intégrer au sein d’un dépôt existant un autre dépôt externe. L’intérêt est de pouvoir choisir quelle version utiliser simplement en faisant un checkout du tag correspondant.

Pour configurer ce module, en utilisant le dépôt GitHub de WordPress, il faut lancer la commande suivante à la racine du projet :

Le message « Cloning into ‘wp-cms’… » s’affiche alors avec un indicateur de progression. Le projet GitHub hébergeant WordPress est en train d’être téléchargé.

Une fois terminé, un nouveau fichier est créé à la racine du projet appelé .gitmodules. Il référence les modules intégrés et doit être versionné dans le dépôt.

Pour utiliser une version stable, il faut faire un checkout de cette version dans le répertoire contenant l’installation. La liste des versions est disponible sur GitHub.

Par exemple pour utiliser la version 4.0.1 qui est actuellement la dernière stable :

Un message validant l’exécution de la dernière commande s’affiche. Il doit confirmer que le tag en cours est bien 4.0.1.

Création de la configuration du projet

Le squelette de base du projet est maintenant en place. Si vous faites pointer votre serveur web dessus, le processus d’installation de WordPress doit s’afficher.

À ce niveau, il y a deux possibilités pour gérer la configuration :

  • Suivre la procédure d’installation qui va générer le fichier wp-config.php dans le dossier wp-cms ;
  • Créer une configuration personnalisée grâce à un générateur type Phing configuré en conséquence (exemple).

La seconde solution a l’avantage d’optimiser complètement la chaîne d’installation du projet. Cependant, elle demande de bien connaître la structure de la configuration de WordPress et ses possibilités.

Dans tous les cas, il faut que le fichier wp-config.php soit créé ou déplacé dans le répertoire racine du projet. Ensuite, il faut s’assurer que les deux lignes permettant de référencer le nouveau répertoire wp-content sont bien présentes :

Les chemins et URLs doivent correspondre aux besoins spécifiques de votre projet.

Création du thème et développement spécifique

Nous arrivons à la partie la plus intéressante, la création du thème spécifique pour votre client.

Vous pouvez créer un répertoire dans le dossier wp-content/themes/ de votre nouveau projet et démarrer l’intégration. Si vous partez d’un thème existant, je vous conseille d’utiliser la technique des thèmes enfants décrite sur le codex WordPress.

Une fois intégrées dans votre workflow, les étapes décrites précédemment permettent d’automatiser la mise en place du projet pour plus d’efficacité.

Déploiement sur un serveur client

Un dépôt git central et accessible depuis le serveur client

Pour mettre en ligne les développements, je mets en place un dépôt central dans lequel je push toutes les modifications apportées au projet. Vous pouvez utiliser des outils en ligne (BitBucket, GitHub, Beanstalk) ou alors maintenir votre propre serveur (Gitlab, ou solution adhoc). Pour ma part, j’ai opté pour BitBucket pour sa gratuité sur les projets privés.

Ces outils permettent de suivre le code des projets sans pour autant les avoir à disposition. Certains intègrent même un système de bugtracker, très pratique pour la communication et la gestion de la maintenance.

Un processus d’installation identique à la machine de développement

L’installation sur le serveur du client doit se dérouler de la même façon que sur la machine d’un développeur. Une série de commandes est exécutée pour récupérer le code source et initialiser la configuration. L’automatisation du processus d’installation permet d’éviter les erreurs de déploiement. La configuration de chaque environnement doit être connue et versionnée dans le dépôt de source.

Par exemple en utilisant Phing, l’installation pourrait être réalisée ainsi :

La dernière commande utilise le paramètre -Dbuild.env=PREPROD pour utiliser la configuration de preproduction et initialiser l’environnement en cours (mise à jour du fichier wp-config.php par exemple).

Gestion des contenus

Les contenus sont très importants dans un projet utilisant un CMS. La plateforme de production doit être traitée avec soin et ses contenus sauvegardés par une procédure spécifique (médias et base de données). Cette sauvegarde doit être automatisée, mais doit être gérée au niveau serveur et pas dans le projet de développement.

Lors des migrations de base de données entre les différents environnements, il est conseillé d’utiliser cette procédure. Elle permet de s’assurer que tous les liens internes au site utilisent bien le bon domaine.

La sauvegarde et la restauration des données entre les environnements peuvent être automatisées pour faciliter l’utilisation des contenus les plus à jour. Un exemple avec Phing est disponible ici (voir les targets env:restore et env:backup).

Gestion des mises à jour

Le projet est maintenant complètement intégré avec Git, ce qui permet d’offrir des possibilités de gestion des mises à jour.

L’installation des mises à jour par les clients eux-mêmes peut être source de problèmes. En effet, il arrive qu’une mise à jour déclenche des erreurs dans le site web. Même si la communauté communique très bien et assure une rétro-compatibilité exemplaire, j’ai toujours quelques inquiétudes…

C’est pourquoi je préfère limiter l’installation des mises à jour à certains utilisateurs seulement. Ainsi, on peut gérer le moment de l’installation de celles-ci, anticiper les éventuels problèmes qui en découlent et les appliquer sur le dépôt Git.

Pour aller plus loin

La méthodologie présentée ici est une ébauche concernant l’industrialisation des projets WordPress. D’autres problématiques sont pour le moment effleurées comme la sauvegarde des contenus et demandent encore à être développées.

Pour plus d’efficacité, il est intéressant d’automatiser les actions redondantes nécessaires au fonctionnement d’un site. Pour cela, l’utilisation d’outils comme Phing est tout à fait adaptée.

C’est un premier pas vers l’intégration continue d’un projet utilisant WordPress.

Il serait également possible de normaliser le dépôt de code pour qu’il pilote les déploiements en préproduction et production. Pour cela, il existe Git Flow, qui répond exactement à ce besoin.

Pour vous faciliter la vie, j’ai mis sur GitHub une architecture projet telle qu’elle est décrite dans cet article. Vous pouvez y accéder ici.

17 commentaires sur cet article

  1. @GIRARDEYFlorian, le dimanche 21 décembre 2014 à 21:45

    Excellent article, vraiment très intéressant. L’utilisation de submodule pour maintenir à jour WordPress en permanence et ainsi pouvoir tester nos plugins/themes avec les futures versions.

  2. Julien, le mardi 23 décembre 2014 à 10:22

    J’ai mis en place à peu près le même système en utilisant composer plutôt que les submodules et capistrano pour le déploiement.
    Ça tourne bien.

  3. Kevin, le mardi 23 décembre 2014 à 16:47

    Très bon article, je travaille également en ce moment sur la mise en place d’une structure modifiée de WordPress permettant l’utilisation de Git et Composer. En revanche, j’ai peur d’être confronté à l’avenir à des problèmes de compatibilité avec certains plugins (NextGen par exemple) qui utiliseraient des chemins codés en dur au lieu des fonctions WordPress.
    Qu’en est-il réellement ? Avez-vous eu ce genre de souci ?
    Aussi, à l’usage, l’utilisation de tous ces outils n’est-il pas plus lourd que les gains apportés ?

  4. Stéphane, le mercredi 24 décembre 2014 à 08:26

    Merci pour vos retours !

    @Kevin je ne pense pas que l’utilisation de ces outils est trop lourde par rapport au gain apporté. Pour ma part j’utilise ce genre de workflow pour tous mes projets et pas uniquement wordpress. C’est vraiment confortable au quotidien :)

    Après concernant les plugins je n’ai pas été confronté au problème des chemins en dur. Pour moi c’est une mauvaise pratique, WordPress propose une api très riche et je trouve dommage de ne pas l’utiliser.
    Je dirais donc que ça permet de valider que les plugins utilisent bien les pratiques de programmation attendues sur ce genre d’outils…
    A voir quand même il existe sûrement à solution ;)

  5. Jean-Baptiste, le vendredi 26 décembre 2014 à 12:28

    Hello,
    Petite question, pourquoi mettre dans le gitignore les plugins ?

    En effet la plupart du temps tu construits ton dev avec des plugins perso et publics sur lesquels il faut également gérer les mises à jour.

  6. @GIRARDEYFlorian, le mardi 30 décembre 2014 à 09:04

    Effectivement, comme le dit Jean-Baptiste, le principal intérêt serait également d’avoir des submodules dans les plugins, n’y aurait-il pas un moyen de faire cohabiter des submodules venant du repo SVN de WordPress et d’autres venant de différents repo git ?

  7. Stéphane, le dimanche 4 janvier 2015 à 17:58

    Hello, une remarque intéressante !

    Il est tout à fait possible de gérer les plugins grâce à des submodules. Dans ce cas il faut avoir l’adresse d’un dépôt contenant le plugin.

    Personnellement je me sers de plugins publics car je trouve un peu galère de créer un thème et un plugin pour construire un site et je n’ai pas de plugins outils que j’ai créé. Ils ne disposent donc pas de dépôt (ou en tout cas pas directement) et les mises à jour sont très bien gérées par WordPress.
    Lorsque j’ai besoin de déployer, j’envoi mes fichiers plugins ou je les réinstalle directement dans le nouvel environnement.

    Si par contre vous créez un plugin spécifique au site (ou pas) et qu’il dispose d’un dépôt Git, vous pouvez tout à fait utiliser la technique des submodules pour gérer son versionning. Il suffit de rajouter dans le fichier .gitignore la ligne :
    !/wp-content/plugins/mon-plugin-en-submodule

    Le « ! » au début de la ligne ajoute une exception dans les ignores et permet de versionner quand même un répertoire dans plugins…

  8. Manumanu, le dimanche 19 avril 2015 à 19:36

    Merci pour ces explications. J’aime bien l’idée de séparer wp-content du reste du CMS.

    Ceci dit, n’y a‑t-il pas un problème avec la page wp-login ? En effet sur mes tests, la partie front est ok, la partie back est gérée, mais la page wp-login.php ne trouve aucune ressource externe (css).

  9. Stéphane, le vendredi 22 mai 2015 à 17:13

    Hello @Manumanu,

    Je viens de publier quelques petits ajustements sur le dépôt GitHub, normalement les problèmes liés à la page de connexion doivent être corrigés…

    N’hésites pas à me tenir au courant si d’autres problèmes apparaissent ! Ou alors laisse une issue sur le dépôt :)

  10. reeslo, le dimanche 27 septembre 2015 à 12:25

    Super article, j’ai une question concernant la migration, serait il possible de prendre en compte la migration des urls les données sérialisées ?

  11. Laurent Perroteau, le samedi 21 novembre 2015 à 16:31

    Génial le tuto, ça a fonctionné du premier coup ! Petite précision tout de même, il faut ajouter les configs « WP_CONTENT_DIR » et « WP_CONTENT_URL » après « WP_DEBUG » dans wp-config.php (et non pas à la fin du fichier)

  12. Stéphane, le mardi 2 février 2016 à 13:14

    @reeslo, merci pour le commentaire, je pense pense avancer un peu sur le projet et passer par WP Cli pour pouvoir gérer ça plus « proprement ». C’est juste que je n’ai pas encore eu le temps de m’y mettre :)

    @Laurent, merci bien ! Je crois avoir avancé un peu sur le projet depuis l’article, je me note de regarder tout ça… Pour référence : https://github.com/CHStudio/wordpress-project

  13. Raphael, le mercredi 11 janvier 2017 à 09:25

    Bonjour, j’ai bien suivi le tuto, mais je n’arrive pas a supprimer wp-cms de l’url… une idée ?

  14. Phracktale, le vendredi 3 mars 2017 à 11:19

    Bonjour,

    Il y a un problème avec le .htaccess quand le site est dans un sous-répertoire.

    Voila mon .htaccess modifié, j’ai enlevé quelques / initiaux :

    RewriteEngine On

    RewriteBase /MCSI/

    #Fix issue #1 invalid wp-admin redirect
    RewriteRule ^wp\-admin$ wp-admin/ [L,R=301]

    #If the URL start with public stop redirect
    RewriteRule wp\-cms\/index\.php$ – [L]

    #If you request an URL it does not start with wp-cms
    #Else the HTACCESS have done the redirect, you can go to the index.php to execute code
    RewriteCond %{REQUEST_URI} ^wp-cms
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ wp-cms/index.php [L]

    #wp-cms is not here so we add it :)
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ wp-cms/$1 [L]

  15. Stéphane, le dimanche 16 avril 2017 à 20:05

    @Raphael, @Phracktale effectivement il y a quelques lacunes sur ce qui a été décrit dans cet article à propos de la réécriture d’URL.

    C’est maintenant corrigé sur la version Github du projet qui en découle : https://github.com/CHStudio/wordpress-project

  16. Nicolas, le jeudi 29 août 2019 à 10:32

    Bonjour,

    Il y a un point qui porte à confusion (je précise que je suis novice dans l’utilisation des outils de versioning).

    Vous utilisez à la fois GitHub et Bitbucket ? Vos extraits de codes sont issus de GitHub mais vous utlisez bitbucket pour le déploiement sur le serveur client.

    N’y a‑t-il pas des risques de confusion et de doublons ?

    Merci d’avance

  17. gydee, le mardi 31 mars 2020 à 14:05

    Bonjour,
    Est-ce que cela fonctionne avec local by flywheel ?