Les monorepos permettent d’héberger le code de plusieurs applications au sein d'un seul dépôt. Dans GitLab, cela consiste à organiser le code source de chaque application dans des répertoires distincts au sein d'un même projet. Bien que cette approche facilite le contrôle de version pour l'ensemble du code, tirer parti des capacités avancées des pipelines CI/CD de GitLab pouvait s'avérer complexe... jusqu'à l'arrivée d'une nouvelle fonctionnalité dans GitLab 16.4 !
Le cas idéal : des pipelines CI/CD indépendants pour un monorepo
Lorsque plusieurs applications coexistent dans un même dépôt, il est logique de vouloir disposer de pipelines CI/CD distincts pour chacune d'elles. Par exemple, si l'un de vos projets regroupe une application .NET et une application Spring, chacune d'elle nécessitera des jobs de compilation et de test différents. L'idéal serait donc de pouvoir découpler ces pipelines pour qu'ils s'exécutent uniquement lorsque des modifications sont apportées au code source de l'application concernée.
Techniquement, cela revient à configurer un fichier de pipeline .gitlab-ci.yml
au niveau du projet, qui inclut des fichiers YAML spécifiques basés sur les modifications détectées dans certains répertoires. Le fichier de pipeline .gitlab-ci.yml
agit comme un plan de contrôle qui déclenche le pipeline approprié en fonction des modifications apportées au code.
Ancienne approche : le contournement
Avant l'introduction de nouvelles fonctionnalités dans GitLab 16.4, il n'était pas possible d'inclure directement un fichier YAML en fonction des modifications apportées à un répertoire ou un fichier spécifique. Une solution de contournement était toutefois disponible.
Prenons un exemple concret : un monorepo avec deux répertoires, java
et python
, contenant respectivement le code source d'une application Java et d'une application Python. Chaque répertoire disposait d'un fichier YAML propre à l'application pour gérer sa compilation. Le fichier de pipeline principal du projet incluait simplement les deux fichiers YAML des applications, mais une logique de gestion des modifications devait être directement intégrée dans ces fichiers.
.gitlab-ci.yml
:
stages:
- build
- test
- deploy
top-level-job:
stage: build
script:
- echo "Hello world..."
include:
- local: '/java/j.gitlab-ci.yml'
- local: '/python/py.gitlab-ci.yml'
Pour chaque application, il était nécessaire de créer un job masqué (par exemple, .java-common ou .python-common), qui ne s'exécutait qu'en présence de modifications apportées au répertoire correspondant. Les jobs masqués ne s'exécutaient pas par défaut. Ils servaient à centraliser la logique de déclenchement et à la réutiliser dans d'autres jobs. Cependant, cela impliquait d'étendre le job masqué dans chaque pipeline afin de respecter les règles de détection des modifications, qui déclenchaient alors le job d'exécution du pipeline.
'j.gitlab-ci.yml':
''' stages:
- build
- test
- deploy
.java-common: rules: - changes: - ../java/*'
java-build-job: extends: .java-common stage: build script: - echo "Building Java"
java-test-job: extends: .java-common stage: test script: - echo "Testing Java"
'''
'py.gitlab-ci.yml':
''' stages:
- build
- test
- deploy
.python-common: rules: - changes: - "../python/*"
python-build-job: extends: .python-common stage: build script: - echo "Building Python"
python-test-job: extends: .python-common stage: test script: - echo "Testing Python"
'''
Cette méthode fonctionnait, mais présentait des inconvénients majeurs. En effet, chaque pipeline devait étendre le job masqué pour chaque autre job dans le fichier YAML afin de respecter les règles de déclenchement. Elle avait pour conséquence la création de code redondant et l'augmentation du risque d'erreur humaine. De plus, les jobs étendus n'acceptaient pas de clés en double. Vous ne pouviez donc pas définir votre propre logique de règles (rules
), car cela entraînait des conflits de clés et leurs valeurs n'étaient pas fusionnées.
Malgré tout, avec cette méthode, le pipeline est opérationnel, incluant les jobs j.gitlab-ci.yml dès que le répertoire java/
est mis à jour, et les jobs py.gitlab-ci.yml dès que le répertoire python/
est mis à jour.