Publier des dépendances PHP sur Packagist dans un projet monorepo

Les dépôts monolithiques (on parle également de dépôt monorepo ou monorepository) consistent tout simplement à avoir un dépôt de code unique regroupant plusieurs projets. Cela peut être des applications distinctes (dans le cas de microservices), des composants d'un même projet (une API, avec son interface et éventuellement des bibliothèques de code autonomes) ou tout le code appartenant à une société.

A la différence d'un projet de type monolithe, chaque projet gère de manière indépendante ses propres dépendances. Cela permet d'avoir une gestion plus fine des dépendances de chaque projet, mais également de pouvoir utiliser des technologies différentes si le besoin le nécessite. On peut par exemple imaginer: une UI de type SPA développée en Javascript fonctionnant avec un backend composé d'une API PHP et d'un service de gestion des utilisateurs développé en Go. Tout cela centraliser au sein d'un même dépôt.

Je passerai sur les avantages et inconvénients de ce type de gestion du code. Si vous n'êtes pas à l'aise avec le sujet, je vous laisserai vous faire votre propre opinion en parcourant cette liste de ressources.

Si le sujet n'a rien de nouveau, je ne l'ai vu que très rarement utilisé. Je ne connais très peu sociétés françaises utilisant ce modèle (je peux les compter sur les doigts de la main). S'il ne s'agit aucunement d'une sylver bullet, je suis convaincu que c'est une méthode efficace pour travailler sur certains projets. Par exemple, je travaille actuellement sur une plateforme orientée microservices (une dizaine), utilisée par 3 fronts et également composée de quelques bibliothèques de code indépendantes. Cette plateforme est gérée par une seule et même équipe, ce qui en fait un excellent candidat. Pourtant, nous gérons actuellement chaque brique au sein de son propre dépôt de code.

Mais nous ne sommes pas là pour débattre autour de ce point, mais plutôt pour évoquer comment gérer les dépendances Composer dans un dépôt de code monolithique. C'est un sujet que j'ai déjà abordé il y a quelques années dans un article de ce blog. Mais comment faire lorsque l'on souhaite partager une dépendance Composer sur Packagist.

Vous ne le savez peut-être pas, mais pour déclarer un composant PHP comme une bibliothèque de code pouvant être utilisée via Composer, il est nécessaire que votre code PHP soit présent dans un dépôt de code indépendant avec un fichier composer.json à la racine. Ce mode de fonctionnement n'est donc pas compatible avec un dépôt monolithique.

Pour obtenir ce dépôt de code indépendant, il est bien entendu hors de question de copier à la main les changements. Mais quelles solutions s'offrent à nous dans ce cas ?

La première solution (en partant du principe que vous gérez votre code avec Git) est d'utiliser les subtrees. Ces derniers vont permettre d'extraire une sous-arborescence de votre dépôt afin de le pousser code dans un autre dépôt.

Supposons que notre dossier contienne l'arborescence ci-dessous :

.git
/api
/backend
/ui
/packages
    /package-1
    /package-2

Nous souhaitons extraire le dossier /packages/package-2 dans son propre dépôt afin de publier ce dernier sur Packagist. Pour cela, nous allons commencer par créer le dépôt de code qui accueillera le projet:

$ cd <chemin-nouveau-depot>
$ git init --bare

Nous allons ensuite créer notre sous-arbre Git sur le dépôt original et indiquer un nom de branche :

$ git subtree split --prefix=packages/package-2 -b split

Il ne nous reste plus qu'à pousser les modifications dans le dépôt du composant :

$ git push <chemin-nouveau-depot> split:master

Vous pouvez ensuite pousser le dépôt de code indépendant là où vous le souhaitez et déclarer ce dernier dans Package comme n'importe quel autre dépôt.

Par la suite, vous n'aurez plus qu'à répéter les étapes 2 et 3 afin de mettre à jour le dépôt du composant avec les modifications du dépôt principal. Pour simplifier et automatiser ces mises à jour, je vous recommande vivement d'ajouter ces opérations dans votre intégration continue.

L'utilisation de git subtree split n'est en soi pas compliquée, mais elle est un peu verbeuse et nécessite de jongler entre différents dépôts de code. Il existe des outils pour simplifier cette opération.

Le premier outil auquel je pense (même si je ne l'ai jamais utilisé) est splitsh. Il s'agit de l'outil utilisé pour découper le projet monolithique de Symfony.

Le second outil est un composant PHP développé par Tomáš Votruba, membre reconnu de la communauté PHP. Tomáš a développé un composant qu'il a nommé MonorepoBuilder et qui contient tout un ensemble d'outils pour aider les développeurs à gérer un monorepo. Ce qui qui nous intéresse particulièrement est la commande de split. Pour l'utiliser, vous devrez récupérer la dépendance via Composer au travers de la commande composer require symplify/monorepo-builder --dev. Il faudra ensuite écrire un fichier de configuration monorepo-builder.yml qui permettra de configurer quel dossier doit être extrait vers quel dépôt.

# monorepo-builder.yml
parameters:
    directories_to_repositories:
        packages/package-1: 'git@github.com:Foo/Package1.git'
        packages/package-2: 'git@github.com:Foo/Package2.git'

Vous n'avez ensuite plus qu'à lancer l'opération via la commande vendor/bin/monorepo-builder split pour extraire le dossier dans son propre dépôt.