Fonctionnement avancé de composer dans vos projets web

Dans un article précèdent, j’énumérais quelques avantages à utiliser composer dans vos projets web.

La plupart des CMS, et probablement tous, proposent une initialisation via composer.

Je vais prendre ici l’exemple de Drupal, mais l’idée générale qui en découlera sera sensiblement la même d’un CMS à l’autre. De plus, une procédure sera généralement fournie sur les documentations officielles. N’hésitez pas à vous documenter et faire vos propres tests. Ça ne marchera probablement pas du premier coup, mais croyez-moi, vous y gagnerez au change !

Si ce n’est pas déjà fait, je vous invite à lire cet article qui évoque les commandes les plus communes lorsque vous utiliserez composer.

Et composer créa mon site

Pour Drupal, tout se passe ici. Je vais partir du principe que git et composer sont installés sur votre machine et que vous utilisez une distribution Linux digne de ce nom, sinon déso 😬 je ne vais pas évoquer les éventuelles différences entre les SE. Ensuite, et toujours en suivant la documentation officielle, il suffira de lancer une petite commande pour démarrer l’initialisation de votre projet web :

composer create-project drupal/recommended-project mon_projet_web

Il faut savoir que, par défaut, composer utilise le repository de packagist.org pour récupérer ses paquets.

Un repository est un annuaire dans lequel bon nombre de paquets sont référencés par leurs auteurs. Vous remarquerez que le paquet drupal/recommendedproject s’y trouve et affiche un certain nombre d’informations :

  • Il possède plusieurs dépendances vers d’autres paquets
    • composer/installers: ^1.9
    • drupal/core-composer-scaffold: ^9
    • etc…
  • A l’heure où je rédige cet article, il est disponible en version 9.0.1
    • A noter, les versions antérieures restent accessibles
  • L’endroit où sont hébergées et versionnées les sources

Si vous êtes curieux, vous noterez que les sources techniques ne contiennent que 2 fichiers: composer.json et composer.lock 😵.

Donc si je résume, composer create-projet va :

  • Créer le répertoire mon_projet_web
  • Y cloner (ou presque) le contenu de https://github.com/drupal/recommended-project (un template de projet, il en existe d’autres)
  • Et lancer un composer install

L’assemblage d’un puzzle géant

Durant cette phase d’initialisation, vous remarquerez qu’un bon nombre de paquets sont téléchargés. Durant cette étape, composer install analyse le contenu du fichier composer.json pour y récupérer ce dont il a besoin.

Ici, nous nous attarderons uniquement sur 3 sections de ce fichier.

Les repositories

"repositories": [ { "type": "composer", "url": "https://packages.drupal.org/8" } ]

Comme indiqué plus haut, par défaut, composer récupère les paquets (ou bundles) qui se trouvent sur packagist.org.

Dans notre cas, pour simplifier, nous distinguerons 2 types de paquets :

  • Les bundles de la couche framework
    • Ils sont directement placés dans le répertoire vendor
    • Nous y trouverons notamment le très célèbre Symfony et toutes ses dépendances
  • Les paquets de type « Module Drupal »
    • Ils sont placés dans un répertoire dédié et reconnu du CMS (web/modules/contrib)

Drupal utilise ce mode opératoire et dispose de son propre repository. Donc, lorsqu’un paquet ne se trouve pas sur packagist.org, composer ira le chercher sur https://packages.drupal.org/8. Notamment pour ces propres paquets/modules communautaires.

Les dépendances

"require": {
    "composer/installers": "^1.2",
    "drupal/core-composer-scaffold": "^8.8",
    "drupal/core-project-message": "^8.8",
    "drupal/core-recommended": "^8.8"
}
"require-dev": {
    "drupal/core-dev": "^8.8"
}

Ici est indiqué l’ensemble des paquets nécessaires au fonctionnement du CMS.

Pour ce template de projet Drupal, ils sont peu nombreux, pour la simple et bonne raison que chacun d’entre eux possède ses propres dépendances vers d’autres paquets, qui eux-mêmes possèdent encore des dépendances, et ainsi de suite.

Composer va donc réaliser une résolution « intelligente » des dépendances avant de toutes les télécharger !

La compatibilité de version est assurée grâce à la manière dont les bundles sont déclarés dans le fichier composer.json. Il convient de respecter une certaine nomenclature qui évitera bon nombre de désagrément, c’est là qu’entre en jeu la sémantique de version, exemple :

  • « composer/installers »: « ^1.2 » : télécharge au minimum la vers 1.2.x sans jamais passer à la version 2
  • « drupal/core-composer-scaffold »: « ^8.8 »: télécharge au minimum la version 8.8.x sans jamais passer à la version 9

Dans les grandes lignes, les paquets existent sous plusieurs versions et de la forme x.y.z

  • x: pour version majeure
    • Changement profond dans le code source, la rétrocompatibilité n’est pas assurée
  • y: pour version mineure
    • Ajout de nouvelles fonctionnalités
  • z: pour patch
    • Correction de bugs, failles de sécurité

Ainsi, vous pourrez mettre à jour n’importe quel paquet (ou module), dans n’importe quel ordre, sans risquer de compromettre la stabilité de votre projet web. Dans sa résolution de dépendances, si composer détecte le besoin de mettre à jour un autre paquet, il le fera automatiquement pour vous, fort appréciable !

La résolution complète est stockée dans le fichier composer.lock. Je vous invite à parcourir rapidement le contenu de ce fichier, il contient l’intégralité des dépendances nécessaires à l’initialisation de votre projet, et verrouillées dans des versions bien précises.

En effet, dès lors que vous initialisez un projet, tous les paquets existent dans une version stable et identifiée. Le développement d’un projet web peut durer plusieurs semaines, durant ce laps de temps, il n’est pas impossible que des paquets soient mis à jour. Si un autre développeur vous rejoint en cours de route (travail collaboratif à l’aide de git), il devra lui aussi initialiser le projet. Les sources techniques devront être strictement identiques aux vôtres. En verrouillant les versions dans le composer.lock, vous assurerez un travail collaboratif cohérent et sans risques pour la stabilité du projet.

En poussant la réflexion jusqu’au déploiement en recette, préproduction et production, le projet devra être réinstallé sur ces différentes infrastructures. Il devra être identique à votre version de développement sur laquelle vous avez travaillé, et donc eu l’occasion de tester sous toutes ses coutures !

Répertoires d’installation

"installer-paths": {
    "web/core": ["type:drupal-core"],
    "web/libraries/{$name}": ["type:drupal-library"],
    "web/modules/contrib/{$name}": ["type:drupal-module"],
    "web/profiles/contrib/{$name}": ["type:drupal-profile"],
    "web/themes/contrib/{$name}": ["type:drupal-theme"],
    "drush/Commands/contrib/{$name}": ["type:drupal-drush"],
    "web/modules/custom/{$name}": ["type:drupal-custom-module"],
    "web/themes/custom/{$name}": ["type:drupal-custom-theme"]
}

Plus haut j’expliquais qu’en fonction du type de paquet, ils pouvaient être installés, soit dans vendor (destination par défaut), soit dans un répertoire dédié. C’est dans cette section que tout est expliqué à composer.

Si j’analyse le fichier composer.json du module drupal/token dont les sources se trouvent ici, vous noterez la présence de la ligne type:drupal-module. Notre paquet est donc identifié comme un module Drupal. Après téléchargement, il sera automatiquement déplacé dans le répertoire web/modules/contrib/{$name} conformément à la définition installer-paths ci-dessus. Malin !

L’installation de modules

Lorsque vous aurez besoin d’installer un module (méthode identique sur tous les CMS initialisés avec composer), il vous suffira de lancer la commande :

composer require drupal/xxx

exemple :

composer require drupal/token

Composer va donc s’occuper de télécharger les sources techniques en suivant la méthodologie détaillée précédemment, dans une version compatible avec votre CMS, et déplacer le répertoire de vendor vers web/modules/contrib.

Il répétera l’opération avec toutes les dépendances en téléchargeant aussi bien des paquets de la couche framework (dans vendor) que des paquets de la couche CMS (dans web/modules/contrib), s’il détecte que cela s’avère nécessaire ! Le tout, en une seule ligne de commande.

Une fois fait, il vous suffira de lancer une commande pour installer/activer le module sur le CMS. Avec Drupal, nous utiliserons Drush.

Si votre projet se trouve déjà en production, l’opération de téléchargement et d’installation sera là aussi automatisée.

Lorsqu’on commence à se renseigner sur le déploiement continu sous Drupal, on se rend bien compte que tout a été pensé pour vous simplifier la vie, si tant est que vous utilisiez tous les outils mis à votre disposition et que j’aborderai prochainement.