Aller au contenu

Anatomie d'un Pipeline CI

Comprendre la structure d'un pipeline, ses composants, et les techniques pour le rendre rapide et fiable.


Structure d'un pipeline

Un pipeline CI est compose de trois niveaux : stages, jobs et steps.

  • Stage : groupe logique d'étapes (build, test, deploy). Les stages s'exécutent en sequence.
  • Job : unite d'exécution au sein d'un stage. Les jobs d'un même stage peuvent tourner en parallèle.
  • Step : commande individuelle au sein d'un job.
graph LR
    subgraph "Stage: Build"
        A["compile"]
    end
    subgraph "Stage: Verify"
        B["unit-tests"]
        C["lint"]
    end
    subgraph "Stage: Package"
        D["build-image"]
        E["scan-image"]
    end
    subgraph "Stage: Deploy"
        F["deploy-staging"]
    end
    A --> B
    A --> C
    B --> D
    C --> D
    D --> E
    E --> F

Stages en sequence, jobs en parallèle

Les stages se suivent : on ne passe pas au stage suivant si le précédent a échoué. À l'intérieur d'un stage, les jobs sont indépendants et peuvent s'exécuter en parallèle, reduisant le temps total du pipeline.


Artefacts

Un artefact est la sortie d'un job — un binaire compile, une image Docker, un rapport de tests. Les artefacts permettent de passer des résultats entre jobs sans recalculer.

Cycle de vie

graph LR
    A["Job build<br/>produit l'artefact"] --> B["Stockage temporaire<br/>sur le runner CI"]
    B --> C["Job suivant<br/>telecharge l'artefact"]
    C --> D["Expiration<br/>apres N jours"]
    B --> E["Registre d'artefacts<br/>retention longue"]
Type d'artefact Rétention recommandee Exemple
Rapport de tests 30 jours JUnit XML, coverage HTML
Binaire de build 90 jours JAR, wheel Python
Image conteneur Indefinie (taguee) Image Docker dans un registre
Rapport de sécurité 1 an SARIF, rapport SBOM

Ne pas rebuilder en prod

L'artefact produit en CI doit être le même qui part en production. Rebuilder l'image au moment du déploiement brise la traçabilite et peut introduire des différences subtiles dues aux dépendances.


Parallelisation

Jobs indépendants

Les jobs sans dépendance entre eux peuvent tourner en parallèle. Dans l'exemple ci-dessus, unit-tests et lint tournent en même temps, reduisant la durée totale.

Matrix builds

Un matrix build exécuté le même job avec plusieurs combinaisons de parametres. Utile pour tester sur plusieurs versions d'un runtime.

strategy:
  matrix:
    python-version: ["3.10", "3.11", "3.12"]
    os: [ubuntu-latest, windows-latest]

Cela généré 6 jobs automatiquement. Si l'un échoué, les autres continuent.


Cache

Le cache permet de réutiliser des données coûteuses a produire entre les executions du pipeline.

Types de cache

Type Ce qu'on cache Invalidation
Dépendances node_modules/, .venv/, .m2/ Quand le lockfile change
Build incremental Fichiers objet .o, bytecode Quand les sources changent
Images Docker Couches intermédiaires Quand le Dockerfile change
- name: Cache dependances pip
  uses: actions/cache@v4
  with:
    path: ~/.cache/pip
    key: pip-${{ hashFiles('requirements.txt') }}
    restore-keys: pip-

Clé de cache basée sur le hash du lockfile

Une clé de cache qui inclut le hash de package-lock.json ou requirements.txt est invalidee automatiquement quand les dépendances changent. C'est le pattern recommande.


Exemple concret

Pipeline complet pour une application Python, syntaxe Gitea Actions / GitHub Actions :

name: CI

on:
  push:
    branches: [main, develop]
  pull_request:

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - name: Cache pip
        uses: actions/cache@v4
        with:
          path: ~/.cache/pip
          key: pip-${{ hashFiles('requirements.txt') }}
      - run: pip install ruff
      - run: ruff check .
      - run: ruff format --check .

  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - name: Cache pip
        uses: actions/cache@v4
        with:
          path: ~/.cache/pip
          key: pip-${{ hashFiles('requirements.txt') }}
      - run: pip install -r requirements.txt pytest pytest-cov
      - run: pytest --cov=src --cov-report=xml
      - uses: actions/upload-artifact@v4
        with:
          name: coverage-report
          path: coverage.xml
          retention-days: 30

  build:
    needs: [lint, test]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build image
        run: docker build -t myapp:${{ github.sha }} .
      - name: Scan image
        run: trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:${{ github.sha }}
      - name: Push to registry
        run: docker push registry.example.com/myapp:${{ github.sha }}

needs: déclaré les dépendances entre jobs

Le job build ne démarré que si lint et test ont réussi. Sans needs, tous les jobs démarrent en même temps.


Outils

Outil Description Lien
Gitea Actions CI/CD intégré a Gitea, compatible GitHub Actions gitea.com
GitHub Actions CI/CD natif GitHub, marketplace de milliers d'actions github.com/features/actions
GitLab CI CI/CD intégré a GitLab, runners Docker et Kubernetes docs.gitlab.com/ee/ci
Jenkins CI/CD open-source, très flexible, écosystème de plugins jenkins.io