Chapitre 6.7 — Cycle de vie et distribution d’un plugin
Partie 6 : Développer des plugins — Chapitre 7/7 Public : développeur ayant construit ses premiers plugins (6.3–6.6). On passe de « ça marche sur ma machine » à « ça vit en production et chez les autres ». Version de référence : Moodle 5.2 (PHP 8.3+, racine web
public/).
⏱️ TL;DR
- Versionner :
$plugin->version(YYYYMMDDXX) monte à chaque changement de schéma, d’access.php, detasks.php, deservices.phpou de comportement notable.$plugin->releaseest le libellé humain (v1.2.0).requiresfixe la version minimale de Moodle ;supported(tableau[min, max]) déclare la fourchette de branches supportées.dependenciesliste les autres plugins requis.environment.xml(rare) impose des prérequis serveur (extension PHP, version de lib).- Compatibilité multi-versions : une branche git par version majeure de Moodle supportée (
MOODLE_502_STABLE…), plusmain. On cherry-pick les correctifs entre branches.- Distribuer : git (le plus propre, dev + prod maîtrisée), zip (upload via l’interface), rarement composer. Un plugin = un dépôt nommé
moodle-<type>_<nom>.- Déployer en prod : sauvegarde → mode maintenance → déposer le code (git checkout/pull) →
php admin/cli/upgrade.php→ purge des caches → sortir de maintenance. Jamais d’upload à chaud sur une prod chargée.- Gérer les incompatibilités à l’upgrade de Moodle : lire les upgrade notes, tester en préprod contre la matrice, corriger les dépréciations.
- Publier sur moodle.org : licence GPL v3+, privacy provider, standards
moodle-cs, CI verte, process de review (détaillé en Partie 11).
🎯 Objectifs
À la fin de ce chapitre, vous saurez :
- Quand et comment incrémenter la version d’un plugin, et déclarer
requires/supported/dependencies. - Mettre en place une stratégie de branches git pour supporter plusieurs versions de Moodle.
- Distribuer votre plugin (git, zip) proprement.
- Déployer en production sans casse (procédure scriptée).
- Anticiper les incompatibilités lors des upgrades de Moodle et préparer une publication.
1. Versionner : le contrat de version.php, en profondeur
On a utilisé version.php dans chaque tutoriel. Voici sa version « production », avec tous les champs utiles.
<?php
// version.php (exemple complet)
defined('MOODLE_INTERNAL') || die();
$plugin->component = 'local_todolist'; // identité (obligatoire)
$plugin->version = 2026070801; // version de CE code (obligatoire)
$plugin->requires = 2026042000; // version MINIMALE de Moodle (5.2)
$plugin->supported = [502, 503]; // branches supportées : 5.2 à 5.3 (optionnel, 4.0+)
$plugin->maturity = MATURITY_STABLE; // ALPHA / BETA / RC / STABLE
$plugin->release = 'v1.2.0'; // libellé humain (SemVer conseillé)
$plugin->dependencies = [ // autres plugins requis (optionnel)
'mod_forum' => 2026042000,
];1.1 version : la règle du « quand bumper »
$plugin->version (YYYYMMDDXX) est l’entier qui déclenche l’upgrade. Vous devez l’incrémenter à chaque fois que Moodle doit « reprendre en compte » quelque chose :
| Vous avez modifié… | Bumper version ? | Pourquoi |
|---|---|---|
db/install.xml / le schéma | Oui (+ écrire upgrade.php) | rejouer la migration |
db/access.php (capabilities) | Oui | réenregistrer les capabilities |
db/tasks.php / services.php / messages.php / hooks.php | Oui | recharger ces déclarations |
| Une chaîne de langue | Non (purge des caches suffit) | pas de déclaration structurelle |
| Du code PHP/JS de logique | Recommandé (traçabilité) | marque une nouvelle release |
| Un template Mustache | Non (purge suffit) | rendu, pas structure |
En cas de doute : bumpez. Un bump inutile ne coûte qu’un passage d’upgrade à vide ; un bump manquant laisse une déclaration non prise en compte (le bug « ma capability/tâche n’existe pas »).
⚠️ Piège : la
versionne doit jamais reculer ni stagner sur un changement de schéma. Et en équipe, deux développeurs qui bumpent au mêmeYYYYMMDDXXcréent un conflit de version : convention utile → inclure l’incrémentXXet coordonner (ou utiliser la date du jour + un compteur).
1.2 requires, supported, dependencies, maturity, release
requires: version minimale de Moodle. En dessous, Moodle refuse d’installer le plugin. Mettez la valeur de la plus ancienne version que vous supportez réellement (et que vous testez).supported(depuis Moodle 4.0) : un tableau[branchemin, branchemax]en notation de branche (502= 5.2,503= 5.3). Purement déclaratif/informatif (le plugin directory et l’admin l’affichent), mais honnête : ne déclarez que ce que vous testez.dependencies: autres plugins requis, avec leur version minimale. Moodle bloque l’installation si une dépendance manque. À utiliser avec parcimonie (chaque dépendance complique le déploiement).maturity:MATURITY_ALPHA/BETA/RC/STABLE. N’affichezSTABLEque quand ça l’est.release: le libellé humain. Adoptez SemVer (MAJEUR.MINEUR.CORRECTIF) :MAJEUR= rupture,MINEUR= ajout compatible,CORRECTIF= bug fix. C’est ce que verront vos utilisateurs.
💡 Pour un dev React :
version(l’entier) ≈ le numéro de migration (interne, technique), etrelease(SemVer) ≈ la version npm (communication).requires/supported≈ le champenginesdepackage.json(« ce package tourne sur Node ≥ X »).dependencies≈ vos peerDependencies. La différence : ici les deux numéros (technique et humain) cohabitent explicitement, et l’entier technique pilote réellement l’upgrade.
2. environment.xml : imposer des prérequis serveur
Rare mais utile : votre plugin exige une extension PHP (ex. intl, gd), une version minimale d’une lib, ou un réglage serveur. On le déclare dans environment.xml, que Moodle vérifie avant l’installation.
<?xml version="1.0" encoding="UTF-8"?>
<!-- public/local/todolist/environment.xml -->
<COMPATIBILITY_MATRIX>
<PLUGIN name="local_todolist">
<PHP_EXTENSIONS>
<PHP_EXTENSION name="intl" level="required">
<FEEDBACK>
<ON_CHECK message="intlrequired" plugin="local_todolist" />
</FEEDBACK>
</PHP_EXTENSION>
</PHP_EXTENSIONS>
</PLUGIN>
</COMPATIBILITY_MATRIX>⚠️ Piège : ne dupliquez pas dans
environment.xmlce querequires/dependenciesgèrent déjà (version de Moodle, plugins). Réservez-le aux prérequis d’environnement système (extensions PHP, versions de bibliothèques) que Moodle ne vérifie pas autrement.
3. Compatibilité multi-versions : la stratégie de branches git
Moodle sort deux versions par an (chapitre 1.5) et n’offre pas de garantie de compatibilité d’API entre majeures. Un plugin sérieux supporte donc plusieurs versions de Moodle en parallèle. La stratégie communautaire standard : une branche git par version majeure de Moodle.
Le modèle :
main(oumaster) : développement pour la version de Moodle la plus récente que vous ciblez.MOODLE_XYZ_STABLE: une branche par version majeure supportée (MOODLE_502_STABLE,MOODLE_501_STABLE…). Ce sont les mêmes noms de branches que le core — les outils de la communauté (dontmoodle-plugin-ci) s’appuient dessus.- Un correctif (bug fix compatible) est écrit une fois, puis cherry-pické sur chaque branche supportée.
- Une fonctionnalité qui exige une API de la 5.2 ne descend pas sur la branche 5.1 : c’est là que le multi-branches gagne son coût.
Sur le plugin directory, chaque release est associée aux versions de Moodle qu’elle vise, et vous téléversez (ou laissez la CI publier) une release par branche.
⚠️ Piège : ne tentez pas de supporter toutes les versions dans un seul code avec des
if (moodle_major() >= …)partout. Au-delà d’un ou deux tests conditionnels, le code devient illisible et fragile. Le multi-branches isole proprement les différences. Réservez les conditions in-code aux petits écarts d’API.
💡 Pour un dev React : c’est le modèle des branches de maintenance d’une lib open source (
v4.x,v5.x) avec backport de correctifs par cherry-pick — exactement ce que font React, Next, etc. Lemainfile vers l’avant ; les branches stables reçoivent les fixes rétroportés.
4. Structurer et distribuer le dépôt
4.1 Un plugin = un dépôt
Chaque plugin vit dans son propre dépôt git, nommé par convention moodle-<type>_<nom> :
moodle-local_todolistmoodle-block_progressinfomoodle-mod_simplecheck
Le contenu du dépôt = le contenu du dossier du plugin (le dépôt est local/todolist/). À l’installation, on le clone dans le bon dossier de Moodle :
# Depuis la racine du code Moodle
git clone https://github.com/vous/moodle-local_todolist.git public/local/todolistUn README.md, un LICENSE (GPL v3+), un .gitignore (ignorer amd/build/, node_modules/, vendor/) et un fichier CI (.github/workflows/) complètent le dépôt.
4.2 Les trois voies de distribution
- Git (recommandé) — en dev et en production maîtrisée : versionnable, diff-able,
git pullpour mettre à jour, branches par version de Moodle. C’est la voie propre. - Zip via l’interface — Administration du site → Plugins → Installer des plugins : téléverser le zip (le zip doit contenir un dossier racine nommé comme le plugin). Simple pour un client non technique, mais pas de traçabilité git.
- Composer — marginal dans l’écosystème Moodle. Certaines équipes gèrent l’ensemble Moodle + plugins via Composer et un installer dédié, mais ce n’est pas la norme communautaire. À réserver aux infrastructures qui le maîtrisent déjà.
⚠️ Piège du zip : l’archive doit avoir exactement un dossier racine, nommé comme le plugin (
todolist, pasmoodle-local_todolist-mainque GitHub génère). Un zip mal structuré fait échouer la détection avec un message obscur. Beaucoup préfèrent générer le zip via la CI plutôt qu’à la main.
⚠️ Piège du build JS : si votre plugin a des modules AMD (
amd/src/), la version distribuée doit inclureamd/build/compilé (grunt amd). En dev on ignorebuild/dans git ; pour une release, on le compile et on l’inclut (ou la CI le fait). Un plugin distribué sansamd/build/a un JS qui ne charge pas en production (oùcachejsest activé).
5. Déployer en production sans casse
Voici la procédure de référence. Ne déviez pas : un upgrade raté sur une prod d’étudiants est un incident majeur.
# 0. SAUVEGARDER (base + moodledata + code). Non négociable.
mysqldump ... > backup.sql # ou pg_dump
tar czf moodledata.tgz /var/moodledata
git -C /var/www/moodle rev-parse HEAD > code-version.txt
# 1. Mode maintenance (les utilisateurs voient une page d'attente)
php admin/cli/maintenance.php --enable
# 2. Déployer le code du plugin (git recommandé)
git -C public/local/todolist fetch --tags
git -C public/local/todolist checkout v1.2.0 # une release taguée, pas "latest"
# (ou pour le core : git checkout MOODLE_502_STABLE && git pull)
# 3. Compiler le JS si nécessaire (souvent fait en amont dans la release)
# grunt amd (dans un environnement de build, pas forcément la prod)
# 4. Jouer les mises à niveau de schéma
php admin/cli/upgrade.php --non-interactive
# 5. Purger les caches
php admin/cli/purge_caches.php
# 6. Sortir du mode maintenance
php admin/cli/maintenance.php --disableLes principes derrière la procédure :
- Sauvegarder d’abord, toujours : base et
moodledataet la référence du code (pour rollback). Un upgrade qui échoue à mi-parcours doit pouvoir être annulé. - Mode maintenance :
upgrade.phppeut modifier le schéma pendant que des utilisateurs écrivent → incohérences. On ferme le site le temps de l’opération (quelques secondes à quelques minutes). - Déployer une version taguée, pas un
mainmouvant : vous devez savoir exactement quel code tourne. - CLI, pas l’interface web, sur une prod : plus fiable, scriptable, journalisable, sans timeout de navigateur.
- Purge des caches en fin : sinon l’ancien code/langue/JS reste servi.
⚠️ Piège : ne jamais déposer du code de plugin à chaud sur une prod chargée sans mode maintenance, en pariant que « l’upgrade est petit ». Entre le moment où le nouveau code est en place et où
upgrade.phpa tourné, l’application est dans un état incohérent (nouveau code, ancien schéma) — erreurs fatales pour les utilisateurs actifs.
💡 Pour un dev React : c’est votre pipeline de déploiement habituel (backup → migrate → deploy → cache-bust) transposé, avec une particularité : le mode maintenance joue le rôle d’un deploy window explicite parce que la migration de schéma et le code ne sont pas atomiques. Pensez « migration Prisma en prod » : on ne la lance pas pendant que l’app écrit dans les tables concernées.
6. Survivre aux upgrades de Moodle
Deux fois par an, une nouvelle version de Moodle peut casser votre plugin (chapitre 1.5). Le processus pour l’anticiper :
- Lire les Upgrade / API change notes de la nouvelle version sur
moodledev.io: la liste des changements d’API et des dépréciations. C’est votre checklist. - Tester tôt contre la release candidate de Moodle, avant sa sortie officielle. La CI (
moodle-plugin-ci) teste votre plugin sur une matrice Moodle × PHP × base de données (Partie 11). - Corriger les dépréciations : une fonction dépréciée émet un
debugging()mais fonctionne encore une version ou deux ; utilisez ce sursis pour migrer vers la nouvelle API avant qu’elle disparaisse. - Publier une release compatible, déclarer la nouvelle branche dans
supported, ajouter la branche gitMOODLE_XYZ_STABLE.
⚠️ Piège : ne « figez » jamais une instance sur une vieille version de Moodle pour éviter de mettre à jour vos plugins. La dette s’accumule et explose à l’upgrade forcé (fin de vie/faille), avec potentiellement plusieurs versions à franchir d’un coup et des plugins tous à réparer en même temps. Le bon rythme : suivre chaque version, tester tôt, corriger au fil de l’eau.
7. Vers la publication sur moodle.org (intro)
Distribuer en privé (git/zip) ne demande rien de spécial. Publier sur le plugin directory de moodle.org impose une barre de qualité — c’est le sujet complet de la Partie 11, chapitre 8. En avant-goût, les prérequis non négociables :
- Licence GPL v3+ (fichier
LICENSE, en-têtes de fichiers). Non négociable : tout le directory est GPL. - Privacy provider : même un
null_providersi le plugin ne stocke aucune donnée personnelle (Partie 11). Obligatoire depuis le RGPD. - Standards de code :
moodle-cs(le PHPCS de Moodle) vert, PHPDoc complet, pas de fonctions dépréciées. - Tests + CI : une CI
moodle-plugin-civerte est quasi attendue par les reviewers. - Documentation :
README,CHANGES, chaînes de langue anglaises propres (la traduction viendra via AMOS). - Puis le process de review humain (délais de plusieurs semaines, allers-retours) — préparez-vous à itérer.
📚 Aller plus loin : les prérequis officiels sont sur moodledev.io/general/community/plugincontribution et le checklist de publication. Le détail complet (process, maintenance d’un plugin communautaire, monétisation en écosystème GPL) est en Partie 11.8.
8. Récapitulatif : le cycle de vie complet
Vous avez bouclé la Partie 6 : du premier fichier d’un plugin (6.2) à sa vie en production et sa distribution (ce chapitre). Le fil rouge : Moodle est un système à contrats explicites — de fichiers (par type de plugin) et de cycle de vie (version, upgrade, backup, déploiement). Une fois ces contrats intégrés, développer pour Moodle devient mécanique et prévisible, malgré la taille du système.
✏️ Exercices
Exercice 1 — Bump ou pas bump ?
Pour chaque changement, faut-il incrémenter $plugin->version ? (a) corriger une faute dans une chaîne FR ; (b) ajouter une colonne à une table ; (c) ajouter une capability ; (d) modifier un template Mustache ; (e) ajouter une tâche planifiée.
✅ Solution
(a) Non (purge des caches suffit) — (b) Oui (+ upgrade.php + install.xml) — (c) Oui (access.php n’est relu qu’à l’upgrade) — (d) Non (purge suffit) — (e) Oui (tasks.php n’est relu qu’à l’upgrade). Règle : tout ce qui est déclaratif structurel (db/*.php, schéma) ⇒ bump ; le rendu/langue ⇒ purge. En cas de doute, bumper.
Exercice 2 — Supporter 5.1 et 5.2.
Votre plugin doit tourner sur Moodle 5.1 et 5.2, mais une fonctionnalité utilise une API apparue en 5.2. Quelle stratégie, et que mettez-vous dans version.php de chaque branche ?
✅ Solution
Stratégie de branches : une branche MOODLE_501_STABLE (sans la fonctionnalité 5.2, requires = version 5.1, supported = [501, 501]) et une branche MOODLE_502_STABLE (avec la fonctionnalité, requires = version 5.2, supported = [502, 502] ou [502, 503]). Les correctifs communs sont écrits sur une branche et cherry-pickés sur l’autre ; la fonctionnalité 5.2 reste uniquement sur la branche 5.2. On publie deux releases sur le directory, chacune ciblant sa version. On évite les if (version >= …) in-code au-delà de très petits écarts.
Exercice 3 — Zip qui échoue.
Vous téléchargez votre plugin depuis GitHub (moodle-local_todolist-main.zip) et l’upload via l’interface échoue avec un message peu clair. Pourquoi, et comment produire un zip correct ?
✅ Solution
Le zip GitHub contient un dossier racine moodle-local_todolist-main/ (nom du repo + branche), or Moodle attend un seul dossier racine nommé exactement comme le plugin : todolist/. Il faut renommer ce dossier racine en todolist (et rezipper), ou générer l’archive proprement (ex. via git archive ou la CI) avec todolist/ à la racine. Vérifier aussi que amd/build/ est présent et compilé si le plugin a du JS.
Exercice 4 — Ordonner un déploiement.
Remettez dans le bon ordre : purge des caches ; upgrade.php ; mode maintenance ON ; sauvegarde ; déployer le code ; mode maintenance OFF. Justifiez deux contraintes d’ordre.
✅ Solution
Ordre : 1. sauvegarde → 2. maintenance ON → 3. déployer le code → 4. upgrade.php → 5. purge des caches → 6. maintenance OFF. Contraintes : (a) maintenance AVANT upgrade.php — la migration modifie le schéma ; laisser les utilisateurs écrire pendant ce temps crée des incohérences. (b) sauvegarde AVANT tout — sans point de restauration, un upgrade raté est irréversible. (c) bonus : purge APRÈS upgrade (sinon l’ancien code/JS/langue en cache est servi), et maintenance OFF en dernier.
Exercice 5 — Préparer une publication. Un client veut publier votre plugin sur moodle.org. Listez les cinq prérequis à vérifier avant de soumettre.
✅ Solution
(1) Licence GPL v3+ (fichier LICENSE + en-têtes) ; (2) Privacy provider (au minimum null_provider si aucune donnée perso) ; (3) moodle-cs vert + PHPDoc complet, pas de fonctions dépréciées ; (4) CI moodle-plugin-ci verte (matrice versions/PHP/BDD, phpunit/behat) ; (5) documentation (README, chaînes anglaises propres, versions supportées déclarées dans supported). Ensuite seulement, soumettre — et s’attendre à des allers-retours avec les reviewers (détail en Partie 11.8).
🧠 Quiz de révision
Q1. Dans quels cas doit-on incrémenter $plugin->version ?
Réponse
install.xml + upgrade.php), db/access.php, db/tasks.php, services.php, messages.php, hooks.php, et par convention à chaque nouvelle release. Pas nécessaire pour une simple chaîne de langue ou un template (purge des caches suffit). En cas de doute : bumper.Q2. À quoi servent respectivement requires, supported et dependencies ?
Réponse
requires = version minimale de Moodle (installation refusée en dessous) ; supported = fourchette de branches déclarée (informatif, à ne renseigner que si testé) ; dependencies = autres plugins requis avec leur version minimale (installation bloquée s’ils manquent).Q3. Décrivez la stratégie de branches git pour supporter plusieurs versions de Moodle.
Réponse
MOODLE_XYZ_STABLE par version majeure de Moodle supportée (mêmes noms que le core), plus main pour la version la plus récente. Les correctifs communs sont cherry-pickés entre branches ; les fonctionnalités dépendant d’une API récente restent sur la branche correspondante. On publie une release par branche/version.Q4. Quelle est la procédure de déploiement d’un plugin en production et pourquoi le mode maintenance ?
Réponse
php admin/cli/upgrade.php → purge des caches → maintenance OFF. Le mode maintenance ferme le site pendant la migration de schéma pour éviter que des écritures utilisateur, sur un schéma en cours de modification, ne créent des incohérences.Q5. Comment prépare-t-on un plugin aux deux upgrades annuels de Moodle ?
Réponse
moodle-plugin-ci contre la RC sur une matrice (Moodle × PHP × BDD), corriger les dépréciations pendant le sursis, puis publier une release compatible (nouvelle branche stable + supported mis à jour). Ne jamais figer une instance pour éviter les mises à jour.Fin de la Partie 6. Vous savez désormais construire (local, block, mod, et tous les autres types), versionner, distribuer et déployer un plugin, et le maintenir à travers les versions de Moodle. Le socle développeur est complet.
Partie suivante : Partie 7 — Frontend : JavaScript, React et Tailwind. On quitte le PHP pour le navigateur : Mustache en profondeur, modules AMD/ES6, l’écosystème core/*, puis intégrer React 19 et Tailwind dans un plugin sans casser la plateforme — le terrain le plus familier pour un dev Next.js, avec quelques surprises.