Monorepo Flutter avec Melos
Un dépôt, plusieurs packages
packages/ , chacun avec son propre pubspec.yaml . L'application et les modules se référencent entre eux par dépendance de chemin ( path: ../core_api ), et Melos résout ces liens localement au lieu d'aller chercher les versions publiées sur pub.dev.
feature_auth dépend de core_api , l'inverse ne compile pas. Le graphe des dépendances est écrit noir sur blanc dans les pubspec.yaml , et une dépendance interdite se voit tout de suite : elle ne résout pas. Un dépôt unique garde ces frontières entières et rend seulement leur franchissement plus simple quand il est légitime.
Le fichier melos.yaml
melos.yaml à la racine (les versions récentes acceptent aussi une clé melos: dans le pubspec.yaml de la racine). Il déclare les packages du workspace et des scripts nommés, réutilisables en local comme en CI.
exec: lance sa commande dans chaque package retenu, et packageFilters restreint l'ensemble avant exécution. Ici flutter: true écarte les packages Dart purs, et dirExists: test saute ceux qui n'ont pas de dossier de tests, ce qui évite un échec bruyant sur un package sans suite. La section command: configure les commandes intégrées ; sous version , workspaceChangelog ajoute un changelog agrégé à la racine en plus de celui de chaque package.
melos run analyze ou, sans argument, par un melos run qui propose la liste des scripts au clavier. La commande melos exec reste la porte de sortie pour tout ce qui n'a pas de script attitré.
Bootstrap et liaison locale
melos bootstrap (abrégé melos bs ) est la première commande qu'on lance après un clone. Elle récupère les dépendances de tous les packages en une fois et câble les dépendances de chemin entre eux, sans qu'on ait à enchaîner flutter pub get package par package.
pubspec_overrides.yaml qui pointe les dépendances internes vers leur dossier local :
pub le lit en surcouche du pubspec.yaml et privilégie la version locale indiquée. On le laisse hors du contrôle de version et on relance bootstrap après chaque modification d'un pubspec.yaml . Sur les versions récentes de Dart, Melos peut aussi s'appuyer sur les workspaces natifs de pub ( resolution: workspace ) pour obtenir la même résolution locale.
post de la configuration ci-dessus se déclenche à la fin de bootstrap . C'est l'endroit naturel pour la génération de code, très présente dans un projet Flutter avec freezed , json_serializable ou un générateur de providers : le clone est prêt à compiler dès la première commande, sans étape manuelle oubliée. Le filtre dependsOn: build_runner limite la génération aux packages qui en ont réellement besoin.
Cibler un sous-ensemble : les filtres
melos exec , qui exécute une commande arbitraire sur une partie du graphe. Les filtres se combinent :
--diff compare l'arbre de travail à une référence git et ne garde que les packages touchés. --scope et --ignore filtrent par nom avec des globs. --depends-on et --include-dependents suivent les arêtes du graphe pour attraper aussi les packages en amont ou en aval d'un changement. En CI, --diff="origin/main" est le levier qui évite de rejouer l'analyse et les tests de tout le dépôt à chaque push : seul le sous-graphe modifié tourne, et le reste garde son résultat précédent.
Versioning coordonné
melos version traduit les commits conventionnels en changements de version. Il lit l'historique git depuis le dernier tag de chaque package, en déduit le bump ( fix: en patch, feat: en minor, un BREAKING CHANGE: en major), met à jour le champ version: du pubspec.yaml concerné et écrit son CHANGELOG.md . Le tag posé porte le nom du package, par exemple core_api-v1.4.0 .
fix(core_api): ... fait monter core_api , mais aussi les packages qui en dépendent : Melos ajuste leur contrainte de version et leur applique un incrément. L'ensemble reste cohérent, sans qu'un package référence une version d'un voisin qui n'existe pas encore. Le scope entre parenthèses ( core_api ) rattache le commit au bon package quand un même changement en touche plusieurs.
melos publish pousse sur pub.dev les packages publiables. La commande tourne à blanc par défaut et n'agit réellement qu'avec --no-dry-run , et elle saute d'office les packages marqués publish_to: none , comme l'application Flutter elle-même.
L'enchaînement en CI
melos bootstrap installe et relie, puis melos run analyze et melos run test tournent sur les packages modifiés via --diff , et la branche de release ajoute melos version puis melos publish . Chaque étape réutilise la même définition de scripts que celle des développeurs en local, ce qui évite la dérive entre la CI et le poste de travail : ce qui passe sur une machine passe dans le pipeline, avec la même commande.
Un monorepo se juge au coût d'un changement transverse. Avec Melos, toucher un package partagé et ses consommateurs tient dans une seule branche à revoir et à fusionner, quand des dépôts séparés imposeraient une file de publications ordonnée à la main.