Construire la CI d'un monorepo: les parent-child pipelines de Gitlab-ci

Le monorepo est une stratégie de versionning qui consiste à n'avoir qu'un seul repository pour les bases de code de plusieurs projets. Ces projets ont généralement leur propre cycle de vie, mais aussi leurs propres technologies. La construction du livrable de chaque projet peut alors être soumise à des contraintes différentes. Il est dès lors compliqué de mettre en place une chaîne de CI (continuous integration) qui puisse fonctionner pour l'ensemble.
Sortis en janvier 2020, les parent-child pipelines sont la réponse apportée par Gitlab aux problématiques de CI liées à l'exploitation des monorepos.

Voyons cela de plus près !

Un cas concret

Imaginons une banque dont les services sont accessibles depuis un client web en Angular, une application Android, une application iOS, le tout reposant sur les API d’un backend. Nous aurions alors classiquement 4 projets, mais que nous aimerions garder au sein d’un seul et unique repository.

Nous avons alors un repository my-awesome-bank dans lequel seraient versionnés les projets suivants :

  • web-client (l’application web Angular)
  • android-app (l’application Android en Kotlin)
  • ios-app (l’application iOS en Swift)
  • backend (un backend monolithique en Java, parce que je n’ai pas eu le courage de mettre d'autres trucs :p)

Le projet est accessible depuis mon gitlab. Finalement, je vous ai un peu menti en disant que le cas était concret. Nous ne ferons joujou qu'avec GitLab CI sans nous intéresser aux projets qui resteront vides. ¯\_(ツ)_/¯

Créer le pipeline parent

Dans notre exemple, le pipeline parent pourrait se résumer à un seul et unique stage : celui responsable du déclenchement des pipelines de chacun des projets. Appelons-le trigger-child-pipelines ...

# .gitlab-ci.yml
stages: 
    - trigger-child-pipelines

... et associons-y un job qui déclenche le pipeline du web-client

# .gitlab-ci.yml
web-client:
    stage: trigger-child-pipeline
    trigger:
        include: web-client/.gitlab-ci.yml
        strategy: depend
    only:
        changes:
            - web-client/**/*

Ce job déclenchera le pipeline décrit dans ./web-client/.gitlab-ci.yml.
La stratégie depend permet de dire au job que son statut (success, failed, cancelled, pending) dépend de celui du pipeline qu'il a déclenché. Cela permet d'attendre la fin du pipeline enfant afin de passer à la suite du pipeline parent. Le job attendra alors la fin de l’exécution du pipeline qu’il a déclenché.

On notera la présence du only:changes sur le répertoire web-client qui permet de dire au job de ne se déclencher qu'en cas de modification apportée au projet web-client. Cela évite de déclencher inutilement un pipeline enfant lors de la réception d'un commit qui ne le concerne pas.

La logique sera la même pour les jobs qui déclencheront les pipelines des projets android-app, ios-app et backend.

A chacun son pipeline !

Il reste à définir les pipelines de chacun des projets. Ceux-ci se configurent de la même manière qu'un pipeline classique.

Il y a cependant une différence fondamentale. Le pipeline enfant est exécuté à la racine du repository.

Par exemple, si le pipeline de backend a été déclenché, son exécution ne se fera pas automatiquement au sein du répertoire backend. Ainsi, les commandes telles que mvn test ne fonctionneront pas à cause de l'absence de pom.xml (à la racine du projet).

Un simple cd backend dans chacun des jobs fera l'affaire.

# backend/.gitlab-ci.yml
.backend-setup:
    before_script:
        - cd backend

test: 
    extends: .backend-setup
    stage: test
    script:
        - mvn test

Les valeurs de artifact:paths et cache:paths ne sont, elles non plus, pas évaluées depuis le répertoire des projets. Il sera nécessaire de donner les chemins depuis la racine du repository.

# web-client/.gitlab-ci.yml

cache:
  paths:
    - web-client/node_modules

...

build: 
    extends: .web-client-setup
    stage: build
    script:
        - yarn build
    artifacts:
        paths:
            - web-client/dist/*

Des artifacts difficilement partageables entre pipelines.

Il existe des cas où l'on souhaiterait fournir aux pipelines enfants les artifacts du parent et vice versa.

On pourrait, par exemple, fusionner les rapports d'exécution de tests Cucumber afin de les exposer au sein d'une living doc.

L'issue #202093 a été ouverte à ce sujet et des solutions de contournement ont été proposées.

Des pipelines enfants qui ne devraient pas s'exécuter

Lors de l'ouverture d'une branche, l'ensemble des pipelines enfants sont exécutés, malgré la présence des contraintes on:changes. Cela peut vite être un problème si le monorepo contient beaucoup de projets. Le nombre de jobs peut être conséquent, et la facture salée si vous hébergez vous-mêmes vos runners.

L'issue #11427 traitant de ce sujet est ouverte depuis maintenant 1 an et ne semble toujours pas résolue.

NOTE : Ce souci est aussi présent dès lors que le pipeline est exécuté manuellement.

Au final

Les parent-child pipelines proposent une vraie solution aux équipes qui souhaitent partir sur des monorepos. Leur usage est identique à celui des pipelines proposés par Gitlab-ci à l'exception des quelques ajustements que nous avons vus.
Il faudra cependant être vigilant aux limitations encore existantes, notamment celles induites par l'issue #11427, et décider de les utiliser ou non en connaissance de cause.
Ces limitations ne seront pas problématiques sur des monorepos avec peu de projets (ex: un back et un front).


Vous avez trouvé cette publication utile? Cliquer sur
Ippon
Ippon est un cabinet de conseil en technologies, créé en 2002 par un sportif de Haut Niveau et un polytechnicien, avec pour ambition de devenir leader sur les solutions Digitales, Cloud et BigData.

Ippon accompagne les entreprises dans le développement et la transformation de leur système d’information avec des applications performantes et des solutions robustes.

Ippon propose une offre de services à 360° pour répondre à l’ensemble des besoins en innovation technologique : Conseil, Design, Développement, Hébergement et Formation.

Nous avons réalisé, en 2019, un chiffre d’affaires de 42 M€. Nous sommes aujourd’hui un groupe international riche de plus de 400 consultants répartis en France, aux USA, en Australie et en Russie.
FRANCE Website LinkedIn