GitHub Actions et GitLab CI se ressemblent beaucoup en surface : les deux définissent des pipelines en YAML, les deux exécutent des jobs sur des runners, les deux s’intègrent nativement à leur plateforme Git respective. Le choix réel ne se joue pas sur cette syntaxe proche, mais sur des différences structurelles qui pèsent bien plus lourd une fois le pipeline en production depuis un an.

Le marketplace contre l’intégré

GitHub Actions mise sur un marketplace d’actions communautaires : n’importe qui publie une action réutilisable, ce qui donne un catalogue immense mais de qualité inégale, chaque action tierce étant une dépendance de plus à auditer (voir l’angle sécurité de la provenance SLSA). GitLab CI intègre nativement bien plus de fonctionnalités qui, côté GitHub, passent par une action tierce : scan de sécurité, revue de licences de dépendances, environnements de déploiement avec approbation manuelle intégrée à l’interface.

# GitHub Actions : une action tierce pour presque tout,
# y compris des fonctionnalités que GitLab intègre nativement
- uses: some-org/security-scan-action@v3
# GitLab CI : le scan de sécurité est un mot-clé du langage,
# pas une dépendance externe à choisir et maintenir
include:
  - template: Security/SAST.gitlab-ci.yml

Les workflows réutilisables, une vraie différence d’architecture

GitHub Actions structure la réutilisation autour de « reusable workflows » et de « composite actions », des unités explicitement appelées depuis un autre workflow. GitLab CI structure la sienne autour de include, qui fusionne des fragments YAML dans un seul pipeline avant son exécution. La différence est plus profonde qu’une question de mot-clé : GitHub isole chaque workflow appelé dans son propre contexte d’exécution, GitLab fusionne tout dans un espace de noms unique, ce qui facilite le partage de variables entre étapes au prix d’un risque de collision de noms sur un pipeline complexe.

Le DAG natif de GitLab, l’ordonnancement implicite de GitHub

GitLab CI expose nativement un graphe de dépendances explicite entre jobs (needs:), permettant à un job de démarrer dès que ses dépendances directes sont finies, sans attendre la fin de tout un stage précédent. GitHub Actions atteint un résultat comparable via needs: sur les jobs, mais l’organisation par défaut reste plus séquentielle (des jobs dans un même workflow s’exécutent en parallèle sauf dépendance explicite), une nuance qui compte surtout sur un pipeline avec beaucoup d’étapes indépendantes à paralléliser au maximum.

Le registre de conteneurs et l’écosystème intégré

GitLab embarque un registre de conteneurs, un registre de packages génériques et une gestion d’environnements directement dans la même plateforme que le code et la CI. GitHub sépare ces briques (GitHub Container Registry existe, mais reste un produit à part, avec sa propre gestion de permissions) : un choix qui favorise la composition d’outils spécialisés côté GitHub, contre une plateforme unique et intégrée côté GitLab.

Le verrouillage réel : la plateforme Git, pas la CI

Le choix de CI se découple rarement du choix de plateforme Git : migrer de GitHub vers GitLab (ou l’inverse) pour changer de CI seule est rare en pratique, parce que les deux emportent avec eux les issues, les pull/merge requests, les webhooks configurés, l’historique de review. Le vrai verrouillage n’est donc pas la syntaxe YAML de la CI (portable à l’effort), c’est l’écosystème complet de la plateforme Git choisie en premier.

À retenir

GitHub Actions et GitLab CI se ressemblent en YAML mais divergent sur des choix structurels : marketplace ouvert contre fonctionnalités intégrées, workflows isolés contre fusion par include, écosystème composé contre plateforme unique. Aucun des deux n’est strictement supérieur : le bon choix dépend de la plateforme Git déjà retenue (le vrai verrouillage) et de la préférence entre composition d’outils spécialisés ou plateforme intégrée, une décision qui se prend au moment de l’industrialisation d’une chaîne CI/CD, rarement révisée ensuite sans coût significatif.