Mettre à jour Drupal simplement

Lorsque vient le temps de mettre à jour Drupal ou ses modules, c'est un peu la panique. Il faut aller vite car la faille de sécurité est généralement là, mais il ne faut pour autant pas se précipiter sous peine de finir avec un beau plantage. Voici dons mes recettes de cuisine pour mettre Drupal à jour sans risque avec un minimum d'efforts.

Tout ce qui suit est basé sur l'utilisation d'une plate-forme UNIX standard avec les outils par défaut (perl, diff, patch, rsync, etc.). Pour toute autre plate-forme, ces outils sont généralement disponibles à travers le projet autonomes (ex. cygwin pour Windows).

Se tenir informé

Pour commencer, être à jour c'est avant tout être informé. Pour cela un client RSS comme liferea ou Akregator est un outil indispensable sur lequel il faut ajouter le flux http://drupal.org/security/rss.xml ou de manière plus générale http://secunia.com/information_partner/anonymous/o.rss qui annonce toutes les failles pour tous les logiciels connus.

Le coeur modifié

Dans un monde idéal, la mise à jour de Drupal consiste juste à décompresser la nouvelle archive, et lancer la commande magique :

# On vérifie déjà ce qui change (notez le --dry-run )
gaston$rsync --dry-run -av --progress --delete --exclude .svn --exclude /sites --exclude /files --exclude /tmp drupal_6.666/ chemin/vers/mon_drupal/
bla bla...
bla.
 
# Et lorsque l'on a exhaustivement vérifié que tout est bon, on relance "pour de vrai"
gaston$rsync -av --progress --delete --exclude .svn --exclude /sites --exclude /files --exclude /tmp drupal_6.666/ chemin/vers/mon_drupal/
bla bla...
bla.
gaston$ 
synchronisation des sources

Mais il faut être un peu honnête, garder sur un site un tant soit peu évolué avec une version de Drupal pur-jus, sans aucune modification du coeur, est pour le moins chimérique. On a tous, à un moment ou à un autre, besoin d'adapter la gestion des commentaires, d'assouplir le cache de page ou que sais-je encore, en fonction des besoins du client.

Mais comme nous sommes des gens prudents et organisés, toutes ces modifications ont été soigneusement documentée avec nom de l'auteur du patch, explication et ancien code.

/* Gaston: Modification de cette fonctionnalité en faisant bla bla
 * parce que bla bla.
 * if ( $comments ) {
 */

 if ( $comments && $condition_magique ) {
 // Gaston: Fin du patch
documentation d'un patch

Les plus valeureux pourront garder dans un dossier spécial tous les patchs isolés en mettant une modification par fichier, mais là cela devient presque excessif.

Toujours est-il qu'à se stade, synchroniser les sources à la mode "Monde idéal" n'est pas une option. C'est ici qu'intervient la technique de génération de patch.

Génération d'un patch de mise à jour

Le principe est en soit très simple. Il s'agit de créer un fichier .patch qui contiendra toutes les différences entre la version courante de officielle Drupal et la version cible. Attention, il ne s'agit pas de faire un patch entre les sources de votre site et ceux de la nouvelle version mais bien de récupérer les sources officiels sur drupal.org correspondant à la version qui est actuellement sur votre site.

De manière générale cette technique peut être aussi utilisée sur un coeur non modifié, car le patch présente l'immense avantage de servir de mémoire. Vous pouvez ainsi sauvegarder chacun des patchs appliqués, les étudier pour comprendre ce qui a changé (très instructif pour les mise à jour de sécurité) et aussi pouvoir revenir en arrière car un patch peut être appliqué dans les deux sens.

Prenons un exemple. Vous êtes actuellement sur Drupal 6.8 (c'est très mal !!) que vous avez copieusement modifiée et vous allez faire évoluer vers la version 6.12. Vous allez donc récupérer la version 6.12 ET 6.8 sur drupal.org et faire votre patch avec ces deux versions. Ensuite ce patch va être appliqué à votre version 6.8 modifiée pour la mettre à jour vers une 6.12 elle aussi modifiée. Si vous avez de la chance, les mises à jour ne porteront pas sur du code que vous avez modifié, et l'application du patch se fera sans encombre, dans le cas contraire, il faudra gérer chaque confit à la main, mais nous verrons cela plus tard.

Pour l'heure commençons par créer notre patch en utilisant la commande diff :

gaston$cd ~
gaston$mkdir patchs
gaston$cd patchs
 
# récupération de la version actuelle non modifiée et décompression
gaston$wget http://ftp.drupal.org/files/projects/drupal-6.8.tar.gz
gaston$tar -zxvf drupal-6.8.tar.gz
drupal-6.8/
drupal-6.8/includes/
drupal-6.8/includes/actions.inc
...
 
# récupération de la nouvelle version
gaston$wget http://ftp.drupal.org/files/projects/drupal-6.12.tar.gz
gaston$tar -zxvf drupal-6.12.tar.gz
drupal-6.12/
drupal-6.12/includes/
drupal-6.12/includes/actions.inc
...
 
# Création d'un patch entre la 6.8 et la 6.12
diff -Naur drupal-6.8/ drupal-6.12/ > drupal-6.8-6.12.patch
gaston$ 

Le patch est maintenant créé, il ne reste plus qu'à l'appliquer sur le code.

gaston$cd /chemin/vers/drupal
 
# D'abord on vérifie que tout passe bien avec l'option --dry-run
gaston$patch --dry-run -p1 < ~/patchs/drupal-6.8-6.12.patch
 
# Si l'on a AUCUN "FAILLED", on peut appliquer le patch "pour de vrai"
gaston$patch -p1 < ~/patchs/drupal-6.8-6.12.patch
gaston$ 
application du patch

Gestion des conflits

Si vous avez obtenu des "FAILLED" lors de l'application du patch en mode "dry-run", vos modifications du coeur sont donc en confit avec les mises à jour.

Mon approche approche dans ce cas consiste à :
  1. Commencer par sauvegarder le fichier incriminé dans un coin.
  2. Avec un outil de comparaison de fichier comme meld , comparer la version "canonique" du fichier qui se trouve dans le dossier ~/tmp/drupal-6.8/chemin/vers/monfichier.php avec le fichier modifié qui pose problème.
  3. Si la modification n'est sans intérêt comme de simples retours chariots ou un mauvais formatage, il faut noter dans un coin la modification
  4. Lorsque la modification a été notée, écraser le fichier avec la version originale.
  5. Appliquer à nouveau le patch en mode dry-run et recommencer à l'étape (1) si ça coince autre part.
  6. Lorsque tout passe, appliquer le patch sans dry-run
  7. Enfin, ré-écrire toutes modifications qui ont été notées.

Généralement cette dernière étape est un bon moment pour se demander s'il est intelligent de faire cette modification à cet endroit, ce qui finir 9 fois sur 10 par le constat d'une grosse flemme un vendredi soir à 19h alors que la même modification faite à 8h un lundi matin aurait donné lieu à la création d'un module....

Mise à jour de la base

Voilà, si tout c'est bien passé, il ne reste plus qu'à mettre à jour la base de données (update.php), tester exhaustivement, puis synchroniser avec la production.

Mise à jour des modules

La mise à jour des modules peut passer par la même procédure de génération de patch, avec les mêmes bénéfices que pour le coeur, que ces modules aient été modifiés ou pas. Cependant, autant mettre à jour le coeur n'arrive pas, et heureusement, tous les quatre matins, il n'en est pas de même des modules. Plus il y en a, plus les mises à jours sont régulières.

Pour régler cela, j'ai écrit un script perl qui permet :

  • De vérifier le niveau de mise à jour d'un ensemble de modules. A ce titre il fonctionne comme le module "Module update" mais sans vous casser les pied à chaque fois que vous allez sur l'administration des modules (personnellement je déteste ce truc que je désactive systématiquement). Dans ce mode, vous pouvez par exemple mettre le script dans une tâche CRON et ainsi être notifié par courriel lorsqu'une mise à jour est disponible.
  • De sélectionner la stratégie de recherche de nouvelle version. Vous pouvez ainsi spécifier que vous acceptez les mises à jour vers une version majeure (option --allow-major-release), une mise à jour vers une version instable (option --allow-unstable) ou, mais là je ne le conseille pas, une mise à jour vers une version de développement (option --allow-dev).
  • De créer un patch pour migrer le module qui sera stocké dans le dossier du module. Le patch créé est automatiquement testé en mode dry-run pour vous indiquer le nombre de fichier mis à jour, de mise à jour qui ne passent pas (FAILLED) et de mise à jour qui passe de manière flou (FUZZY).
  • De spécifier un version de Drupal cible. Par défaut les versions des modules sont recherchées à partir de la version de Drupal courante (ex. 5.x-1.1 vers 5.x-1.2) mais en spécifiant l'option --api 6 vous pouvez créer le patch qui passe d'une version 5.x à une version 6.x pour un même module. Cette option est aussi très pratique pour avoir la liste des modules qui ne sont pas encore migrés en version 6.x.

Quelques exemples d'utilisation :

# mise à jour sans appliquer de patch du module admin_menu
gaston$drupal-module-update admin_menu/
Title : Administration menu
Name : admin_menu
Home : http://drupal.org/project/admin_menu
Current Version : 6.x-1.1
Stability : stable
 
Searching for new releases...
[15/5/2009] => 6.x-1.4 (http://drupal.org/node/464048)
[24/1/2009] => 6.x-1.3 (http://drupal.org/node/363483)
[20/1/2009] => 6.x-1.2 (http://drupal.org/node/361251)
 
Best Release : 6.x-1.4
Stability : stable
Security Risk : No
Patch # : 20
hacked : 0
Failed : 0
Applied : no
 
# mise à jour avec application du patch
gaston$drupal-module-update --apply admin_menu/
...
Applied : yes
 
# On relance sans application pour vérifier
gaston$drupal-module-update admin_menu/
Title : Administration menu
Name : admin_menu
Home : http://drupal.org/project/admin_menu
Current Version : 6.x-1.4
Stability : stable
 
Searching for new releases...
No new release found
 
# on recherche maintenant une mise à jour instable avec
# changement de version majeur
gaston$drupal-module-update--allow-unstable --allow-major-release admin_menu
...
Searching for new releases...
[11/6/2009] => 6.x-3.0-alpha1 (http://drupal.org/node/488202)
 
Best Release : 6.x-3.0-alpha1
Stability : unstable
Security Risk : No
Patch # : 16
hacked : 0
Failed : 0
Applied : no
 
# La même chose avec en plus les versions de dev
gaston$drupal-module-update --allow-unstable --allow-major-release --allow-dev admin_menu
...
Searching for new releases...
[11/6/2009] => 6.x-3.0-alpha1 (http://drupal.org/node/488202)
[12/6/2009] => 6.x-3.x-dev (http://drupal.org/node/373515)
 
# La ligne typique à mettre dans un CRON, au format CSV, juste les mises à jour, et sans génération de patch
gaston$drupal-module-update --csv --allow-major-release --just-updates --no-patch *
"Gravatar integration":"gravatar":"http://drupal.org/project/gravatar":"6.x-1.3":"stable":"6.x-1.7":"stable":"No"
"Translation template extractor":"potx":"http://drupal.org/project/potx":"6.x-2.1":"stable":"6.x-2.2":"stable":"No"
...
gaston$ 
Exemples d'utilisation du script

En espérant que cela puisse servir, le script est disponible ici. Tout avis ou suggestion sont bien évidement les bienvenus.

Je décline bien évidement toute responsabilité si ce script vous torpille définitivement le site de votre meilleur client. A vous de faire des sauvegardes, ou mieux d'utiliser un dépôt de version (subversion, cvs, etc) et de travailler sur une machine de développement et pas sur le serveur...
Vus : 477
Publié par arNuméral : 54