Reading Time: 13 minutes

L’intégration continue (CI) construit, teste et valide automatiquement le code de recherche à chaque validation. Pour les logiciels scientifiques, CI est essentiel pour la reproductibilité, la détection précoce des bogues et le maintien de la qualité dans le temps. Implémentez CI en : (1) la rédaction de tests automatisés avec PyTest, (2) la configuration d’un pipeline CI à l’aide d’actions GitHub ou GitLab CI, (3) à l’aide de Docker/CONDA pour la cohérence de l’environnement, (4) l’ajout de rapports de couverture et (5) l’intégration de benchmarks de performance. Gérez les tests numériques avec pytest.approx, utilisez des stratégies matricielles pour tester les versions Python et les dépendances de cache afin de réduire le temps d’exécution. CI transforme le code de recherche de scripts fragiles en logiciels fiables et maintenables.

Introduction : Pourquoi l’intégration continue est-elle importante pour la recherche ?

Les logiciels de recherche sont connus pour leur rupture silencieuse. Un petit changement dans une partie du code peut produire des résultats subtilement différents en aval, en invalidant les résultats publiés ou en perdant des mois de temps de calcul. Les tests manuels traditionnels, en exécutant quelques exemples à la main, ne sont pas étendus à des codes de simulation complexes avec des dizaines de modules interdépendants.

L’intégration continue (CI) résout ce problème en exécutant automatiquement une suite de tests complète à chaque time code. Mais CI est plus qu’une simple automatisation ; C’est une discipline de qualité qui renforce la reproductibilité et valide l’exactitude en permanence. Comme l’indique le papier meilleures pratiques pour le calcul scientifique n’est pas négociable pour les logiciels scientifiques fiables » (Wilson et al., 2012).

Pour les équipes de recherche, CI offre des avantages concrets :

  • Reproductibilité : CI vérifie que le code produit des résultats cohérents dans les environnements et dans le temps.
  • Détection précoce des défauts : les bogues sont détectés quelques minutes après leur introduction, pas des semaines plus tard lors de la préparation du manuscrit.
  • Confiance à Refactor : Avec un filet de sécurité de tests, vous pouvez améliorer la structure du code sans craindre de casser quelque chose.
  • Activation de la collaboration : plusieurs contributeurs peuvent fonctionner sur la même base de code avec des vérifications automatisées empêchant les régressions.
  • Documentation des attentes : les tests servent de spécifications exécutables qui documentent le comportement du code.

Malgré ces avantages, de nombreux projets de recherche n’ont toujours pas de CI. Les excuses courantes incluent « notre code est trop complexe pour être testé », « les tests prennent trop de temps » ou « nous n’avons pas le temps de configurer CI ». Ce guide démantèle ces objections et fournit une approche pratique, étape par étape, à CI adapté aux logiciels scientifiques.

Qu’est-ce que l’intégration continue, vraiment ?

L’intégration continue consiste à fusionner fréquemment des modifications de code dans un référentiel partagé, idéalement plusieurs fois par jour, et à vérifier automatiquement chaque fusion avec un pipeline de construction et de test automatisé. La partie « continue » signifie que la rétroaction est rapide ; Les développeurs savent en quelques minutes si leur changement a cassé quelque chose.

Un pipeline CI comprend généralement :

  1. Checkout : le système CI récupère le dernier code.
  2. Configuration de l’environnement : les dépendances sont installées (souvent dans un conteneur).
  3. Analyse statique : le code est lié aux problèmes de style et aux bogues potentiels.
  4. Tests unitaires : les fonctions et les modules individuels sont testés isolément.
  5. Tests d’intégration : plusieurs composants sont testés ensemble.
  6. Rapport de couverture : la fraction du code exercé par les tests est mesurée.
  7. Construction d’artefacts : une documentation, des packages ou des fichiers binaires sont générés.
  8. Marques de performances (facultatif) : la vitesse d’exécution et l’utilisation de la mémoire sont suivies.

Pour les logiciels de recherche, nous ajoutons :

  • Validation numérique : tests qui tiennent compte des tolérances en virgule flottante et des variations stochastiques.
  • Vérifications de reproductibilité : vérification des résultats correspondant aux résultats de référence dans des limites acceptables.
  • Validation des données : Assurer l’intégrité des données d’entrée et de sortie.

Composants de base : création d’un pipeline CI prêt pour la recherche

Un pipeline CI robuste pour les projets scientifiques Python devrait inclure ces composants, chacun traitant d’un aspect de qualité spécifique.

Tests automatisés avec PyTest

La fondation est une suite de tests complète utilisant pytest. PyTest est la norme de facto pour les tests Python en raison de sa simplicité, de ses appareils puissants et de son écosystème riche.

Pour le code scientifique, concentrez-vous sur :

  • Tests unitaires pour les fonctions individuelles (par exemple, un solveur de diffusion calcule-t-il correctement sur un simple maillage ?).
  • Tests de régression qui comparent les résultats aux résultats connus de bons (essentiel pour les solveurs PDE).
  • Tests basés sur la propriété en utilisant hypothèse pour générer des entrées aléatoires et vérifier les invariants.

Les tests unitaires pour le brouillon de code scientifique (en cours) couvrent en profondeur les stratégies Pytest, notamment la gestion de la précision numérique.

Gestion des comparaisons numériques

Le code scientifique traite de l’arithmétique à virgule flottante, où l’égalité exacte est souvent impossible en raison d’erreurs d’arrondissement. PyTest fournit pytest.approx pour les comparaisons approximatives :

def test_diffusion_result():
    result = run_simulation()
    expected = 0.123456
    assert result == pytest.approx(expected, rel=1e-6)  # 0.1% tolerance

Pour les tableaux, utilisez numpy.testing.assert_allclose :

import numpy.testing as npt

def test_field_solution():
    computed = solve_pde()
    reference = load_reference_solution()
    npt.assert_allclose(computed, reference, rtol=1e-5, atol=1e-10)

Choisissez des tolérances en fonction de la physique et de la précision de la discrétisation. Documentez la raison pour laquelle des tolérances spécifiques ont été choisies.

Mesure de couverture de code

La couverture du code mesure la quantité de votre base de code exécutée lors des tests. Bien que la couverture à 100 % n’est pas toujours nécessaire (ou réalisable), le suivi de la couverture permet d’identifier les chemins de code non testés.

Utilisez pytest-cov pour générer des rapports de couverture :

pytest --cov=src/ --cov-report=xml --cov-report=html

Intégrer avec codecov ou coveralls pour suivre la couverture dans le temps et imposer des seuils minimums dans CI.

Le guide de développement de python scientifique fournit des exemples de configuration de couverture détaillés.

Analyse statique et peluche

Les outils d’analyse statique récupèrent les bogues et appliquent la cohérence du style avant la fusion du code :

  • Flake8 : application du guide de style PEP 8 et vérification des erreurs de base.
  • MyPy : vérification du type statique (le typage progressif est précieux, même dans le code de recherche).
  • Black : formatage automatique du code (élimine les débats de style).
  • Pylint : analyse plus approfondie de la qualité du code (utilisez prudemment ; certaines règles peuvent être trop strictes pour le code de recherche).

Exécutez-les en tant que travaux CI distincts afin que les échecs ne bloquent pas les itérations de test rapides.

Cohérence de l’environnement avec Docker ou Conda

L’un des plus grands défis de reproductibilité est la dépendance de l’enfer – différentes versions des bibliothèques produisent des résultats différents. CI élimine cela en installant des dépendances dans un environnement propre et contrôlé.

Option A : Docker (recommandé pour CI)

Docker fournit une conteneurisation complète au niveau du système. A Dockerfile définit l’environnement exact :

FROM python:3.11-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .

boettiger (2015) soutient que Docker « est la meilleure chose qui soit jamais arrivée à la reproductibilité scientifique » car il verrouille toute la pile de logiciels, du système d’exploitation aux bibliothèques.

Option B : Environnements Conda

Si votre projet repose sur des dépendances non-Python (par exemple, HDF5, MPI), utilisez Conda :

# environment.yml
name: research-ci
dependencies:
  - python=3.11
  - numpy>=1.24
  - scipy
  - pip:
    - pytest
    - pytest-cov

Les systèmes CI peuvent créer et activer cet environnement avec conda env create -f environment.yml.

Important : Docker ne garantit pas la reproductibilité avertit que même les conteneurs peuvent avoir des différences subtiles (horodatages, graines aléatoires). Pour une reproductibilité maximale, corrigez également les versions et les graines de la bibliothèque.

Création de documentation

Incluez une étape pour créer de la documentation (Sphinx, MkDocs) et le déployer éventuellement. Documentation-as-code garantit que les documents restent synchronisés avec le code. Le projet de meilleures pratiques de documentation pour les packages scientifiques Python en détaille.

Références de performance

Pour les logiciels de recherche intensifs en calculs, surveillez les performances pour détecter les régressions. Des outils tels que asv (vélocité airspeed) exécutent automatiquement des benchmarks et se comparent aux essais précédents.

Waller et al. (2015) décrivent y compris les benchmarks de performance dans CI pour détecter rapidement les dégradations des performances. Ceci est particulièrement important pour les solveurs PDE où les modifications algorithmiques peuvent affecter considérablement le temps d’exécution.

Comparaison de plateformes : actions GitHub vs Gitlab CI

Deux plates-formes CI dominantes existent : les actions GitHub et Gitlab CI. Les deux sont matures et prêts à la production. Le choix dépend souvent de l’endroit où votre code est hébergé.

Actions GitHub

Forences :

  • Intégration approfondie avec GitHub (vérification des demandes d’extraction, Marketplace des actions).
  • Syntaxe de configuration plus simple pour les flux de travail courants.
  • une plus grande communauté et plus d’actions de tiers.
  • gratuit pour les référentiels publics ; Généreux niveau gratuit pour les dépôts privés.

faiblesses :

  • Moins puissant pour les flux de travail complexes par rapport à GitLab.
  • Fonctionnalités intégrées limitées pour la mise en cache des dépendances dans les premières versions (maintenant améliorée).
  • Lié à l’écosystème GitHub.

Adoption : 33 % des organisations utilisent des actions GitHub (JetBrains, 2026).

Gitlab CI

Forences :

  • Plus riche en fonctionnalités prêtes à l’emploi (tout sur une seule plate-forme).
  • Stratégies matricielles puissantes et pipelines parent-enfant.
  • Un meilleur soutien pour Monorepos.
  • Option d’auto-hébergement pour les environnements de recherche à écartement aérien.

faiblesses :

  • Courbe d’apprentissage plus raide.
  • Communauté plus petite que les actions GitHub.
  • L’interface peut sembler moins raffinée.

Adoption : 19 % des organisations (JetBrains, 2026).

Recommandation

Si votre code est sur GitHub, utilisez Actions GitHub pour la simplicité et l’intégration des écosystèmes. Si vous êtes sur Gitlab ou si vous avez besoin de fonctionnalités avancées de pipeline, choisissez Gitlab CI. Pour les environnements HPC à écart d’air, envisagez GitLab auto-hébergé.

Les deux plates-formes peuvent obtenir les mêmes résultats ; Les différences sont principalement une préférence de workflow. Les exemples ci-dessous utilisent des actions GitHub en raison de sa popularité, mais les équivalents Gitlab CI sont simples à construire.

Configuration de CI : un flux de travail complet des actions GitHub

Cette section fournit un workflow d’actions GitHub prêt pour la production pour un package scientifique Python. Adaptez-le à la structure de votre projet.

Les prérequis

  1. Les tests existent (tests/ répertoire).
  2. Les exigences sont épinglées (requirements.txt ou environment.yml).
  3. Facultatif mais recommandé : Dockerfile pour la reproductibilité de l’environnement.
  4. Le référentiel de code est sur GitHub.

Flux de travail de base

Créer .github/workflows/ci.yml :

name: CI

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

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.9", "3.10", "3.11", "3.12"]

    steps:
    - uses: actions/checkout@v4

    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}
        cache: 'pip'
        cache-dependency-path: 'requirements.txt'

    - name: Install dependencies
      run: |
        pip install --upgrade pip
        pip install -r requirements.txt
        pip install pytest pytest-cov

    - name: Run tests with coverage
      run: |
        pytest --cov=src/ --cov-report=xml --cov-report=term-missing --junitxml=test-results.xml

    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v4
      with:
        file: ./coverage.xml
        flags: unittests
        name: codecov-umbrella

    - name: Upload test results
      if: always()
      uses: actions/upload-artifact@v4
      with:
        name: test-results-${{ matrix.python-version }}
        path: test-results.xml

Caractéristiques principales :

  • Stratégie de la matrice : les tests s’exécutent en parallèle sur Python 3,9 à 3,12, ce qui récupère les problèmes de compatibilité.
  • Caching : actions/setup-python met en cache les packages PIP, réduisant considérablement le temps d’installation.
  • Couverture : sortie de terminal et XML pour CodeCoV.
  • Artifacts : les résultats des tests sont téléchargés même si les tests échouent, préservant les preuves.

Utilisation de Docker dans CI

Si vous avez un Dockerfile, utilisez-le pour assurer la cohérence de l’environnement :

    - name: Build Docker image
      run: docker build -t myproject-ci -f Dockerfile.ci .

    - name: Run tests in Docker
      run: |
        docker run --rm 
          -v ${{ github.workspace }}:/app 
          myproject-ci 
          pytest --cov=src/ --cov-report=xml

Gérer les tests de longue durée

Les simulations scientifiques peuvent prendre des heures. Les coureurs CI ont des limites de temps (souvent 6 heures). Stratégies :

  1. Séparez les tests rapides et lents : utilisez des marqueurs PyTest.
# In test file
import pytest

@pytest.mark.slow
def test_large_simulation():
    # Takes >5 minutes
    pass

Dans CI :

    - name: Run quick tests
      run: pytest -m "not slow"

    - name: Run slow tests (optional, separate job)
      if: github.event_name == 'schedule'  # Only on schedule, not on every PR
      run: pytest -m slow
  1. Sélection du test : Exécutez uniquement les tests affectés par le changement de code à l’aide de pytest --last-failed ou pytest -k "test_name".
  2. PARALLELIZE : répartissez les tests entre plusieurs travaux CI en utilisant pytest-xdist.

Dépendances de mise en cache

Au-delà de la mise en cache des packages Python, des extensions compilées en cache et des fichiers de données volumineux :

    - name: Cache pip packages
      uses: actions/cache@v4
      with:
        path: ~/.cache/pip
        key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
        restore-keys: |
          ${{ runner.os }}-pip-

    - name: Cache pytest
      uses: actions/cache@v4
      with:
        path: .pytest_cache
        key: ${{ runner.os }}-pytest-${{ hashFiles('**/*.py') }}

Ajout de peluche

Ajoutez un travail distinct afin que les problèmes de style ne bloquent pas l’exécution du test :

  lint:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-python@v5
      with:
        python-version: "3.11"
    - run: pip install flake8 black mypy
    - run: flake8 src/ tests/
    - run: black --check src/ tests/
    - run: mypy src/

Des pièges courants et comment les éviter

Sur la base des défis CI/CD identifiés dans les logiciels de recherche (testMu, 2026), voici des erreurs et des solutions fréquentes.

Pitfall 1 : tests qui s’épanouissent

Les tests floconneux réussissent parfois et échouent les autres, érodant la confiance dans CI. Ils sont particulièrement fréquents avec :

  • Conditions de course dans des tests parallèles.
  • Assomptions temporelles (par exemple, « attendre 1 seconde »).
  • aléatoire sans graines fixes.

Solution : tout déterminer. Utilisez pytest les luminaires avec scope="session" pour les ressources partagées. Définissez des graines aléatoires au début de chaque test :

import random
import numpy as np

def setup_function():
    random.seed(42)
    np.random.seed(42)

Pitfall 2 : CI qui prend trop de temps

Si votre pipeline prend des heures, les développeurs le contourneront.

Solution :

  • Diviser en travaux rapides (sur chaque commit) et lents (nuit).
  • Cache de manière agressive (PIP, couches Docker, données de test).
  • Paralléliser à l’aide de stratégies matricielles.
  • Marquez les tests lents connus avec @pytest.mark.slow et exécutez-les séparément.

Pitfall 3 : Dérive environnementale entre CI et développement

Les tests réussissent dans CI mais échouent localement car les environnements diffèrent.

Solution : utilisez partout la même définition d’environnement. Docker est idéal : les développeurs exécutent docker-compose run test localement et CI utilise le même Dockerfile. Vous pouvez également utiliser tox pour gérer plusieurs environnements de manière cohérente.

Pitfall 4 : dépendances manquantes ou obsolètes

CI échoue car une dépendance a été mise à niveau en amont et a rompu la compatibilité.

Solution : dépendances de broches exactement dans requirements.txt (package==1.2.3), pas avec des plages (>=1.0). Utilisez un fichier de verrouillage de dépendance (pip freeze > requirements.txt). Mettez régulièrement à jour les dépendances de manière contrôlée (p. ex., hebdomadaire dependabot PR).

Pitfall 5 : Pas de surveillance des performances

Le code devient plus lent au fil du temps, mais vous ne remarquez que lorsqu’il est catastrophique.

Solution : ajoutez des références à CI avec asv. Configurez-le pour qu’il échoue si les performances se dégradent au-delà d’un seuil (par exemple, 5 % plus lents). Voir guide de la vitesse de Python pour la mise en œuvre.

Pitfall 6 : Ignorer la validation numérique

Les tests utilisent == sur les flotteurs et échouent par intermittence, ou pire, réussissent de manière incorrecte.

Solution : utilisez pytest.approx et numpy.testing.assert_allclose partout. Choisissez des tolérances basées sur l’analyse numérique (par exemple, l’erreur de discrétisation doit être O(H²) pour les méthodes du second ordre). Documentation de tolérance de document dans Test DocStrings.

Guide de décision : quand utiliser quoi

Sélection de la plateforme

Situation Plateforme recommandée
Code hébergé sur GitHub Actions GitHub
Code hébergé sur Gitlab Gitlab CI
Besoin de coureurs auto-hébergés Gitlab CI (auto-hébergé)
Vous voulez une configuration la plus simple Actions GitHub
Pipelines complexes multi-projets Gitlab CI (pipelines parent-enfant)

stratégie de test

Type de code Approche recommandée
Fonctions Pure Python Tests unitaires avec Pytest, cible de couverture élevée (+90%)
Solveurs d’EDP Tests de régression contre des solutions de référence, tests basés sur des propriétés
Algorithmes stochastiques Correction des graines aléatoires + tests statistiques (moyenne, variance)
Grandes simulations (>5 min) Séparez les tests lents, exécutez la nuit ; Utiliser @pytest.mark.slow
Couplage multi-composants Tests d’intégration avec de petits cas de test, valider l’exactitude du couplage

Choix de conteneurs

Besoin Recommandation
Reproductibilité maximale, inclut les DEP de niveau OS Docker
Une gestion plus simple et plus simple Environnement Conda
HPC avec bibliothèques MPI Conda (ou Docker avec --network=host et --ipc=host)
Environnement à combles Conda Pack ou Docker Enregistrer/charger

Intégration de CI avec les flux de travail de recherche

CI n’existe pas isolément. Il se connecte avec d’autres outils et pratiques.

Intégration du suivi des problèmes

Le statut CI apparaît automatiquement sur les demandes d’extraction GitHub/GitLab. Configurez les règles de protection des branches pour exiger le passage de CI avant la fusion. Cela garantit que seul le code validé entre la branche principale.

Messages existants de Matforge sur Suivi des problèmes et dette technique complète en définissant la manière dont les problèmes sont gérés. CI fournit une vérification automatique que les problèmes sont correctement résolus.

Connexion de reproductibilité

Comme indiqué dans reproductibilité et son rôle dans le débogage, CI est une pierre angulaire de la recherche reproductible. Chaque commit qui passe CI peut être approuvé pour produire les mêmes résultats sur n’importe quelle machine avec le même environnement. Ceci est essentiel pour :

  • Reproductibilité papier : lorsque les examinateurs demandent du code, vous pouvez pointer vers un commit spécifique qui a réussi CI et produit les chiffres.
  • Collaboration : les contributeurs externes peuvent exécuter les mêmes tests localement.
  • Entretien à long terme : des années plus tard, vous pouvez toujours reconstruire les résultats d’un commit validé par CI.

Flux de travail de révision de code

Associez CI à une révision de code obligatoire :

  1. Le développeur pousse la branche, CI s’exécute.
  2. Si CI réussit, ouvrez une demande d’extraction.
  3. Les examinateurs vérifient la logique du code et s’assurent que les tests sont adéquats.
  4. Fusionner uniquement après les passages de CI et l’examen approuvé.

Ce flux de travail est standard dans l’industrie mais encore rare dans la recherche. La mise en œuvre augmente considérablement la qualité des logiciels.

Sujets avancés

Test de matrice pour plusieurs dépendances

Les packages scientifiques dépendent souvent de NumPy/Scipy avec un comportement spécifique à la version. Testez une matrice de versions Python et de dépendance :

strategy:
  matrix:
    python-version: ["3.9", "3.10", "3.11"]
    numpy-version: ["1.24", "1.25", "1.26"]

Installez la version spécifique de NumPy à l’étape Install dependencies :

    - run: |
        pip install "numpy==${{ matrix.numpy-version }}" scipy

Cela permet de détecter les problèmes de compatibilité plus tôt.

Détection de la régression des performances

Utilisez asv pour suivre les performances dans le temps :

    - name: Run benchmarks
      run: |
        asv run --quick --show-stderr
      # asv compares against previous commits and reports regressions

Configurez ASV pour échouer au travail CI si une référence est de 10 % plus lente que l’exécution précédente. Voir article de PythonSpeed pour plus de détails.

Déploiement continu de la documentation

CI peut déployer automatiquement la documentation sur les pages GitHub :

  deploy-docs:
    needs: test  # Only run after tests pass
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - run: pip install -r requirements-docs.txt
    - run: sphinx-build -b html docs/ public/
    - uses: peaceiris/actions-gh-pages@v3
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_dir: ./public

Cela maintient la documentation synchronisée avec les modifications de code.

Guides connexes

Résumé et étapes suivantes

L’intégration continue transforme les logiciels de recherche à partir de scripts fragiles et non documentés en actifs fiables et maintenables. Les étapes essentielles sont :

  1. Rédigez des tests automatisés avec PyTest, en utilisant pytest.approx pour les comparaisons numériques.
  2. Configurez un pipeline CI (actions GitHub ou Gitlab CI) qui s’exécute à chaque requête push et pull.
  3. Utilisez Docker ou Conda pour assurer la cohérence de l’environnement entre CI et le développement.
  4. Ajoutez la création de rapports de couverture, de peluchage et de création de documentation.
  5. Surveillez les performances avec des benchmarks pour détecter les régressions.
  6. Intégrez CI à vos processus existants de suivi des problèmes et de révision de code.

Actions immédiates :

  • Si vous n’avez pas de tests, commencez par en écrire quelques-uns pour les fonctions les plus critiques. Même une couverture de 20 % est meilleure que rien.
  • Créez un fichier de configuration CI de base (.github/workflows/ci.yml comme indiqué ci-dessus) et itérez.
  • Réparez immédiatement les tests floconneux, ils érodent la confiance.
  • Ajoutez un « badge » à votre fichier Lisez-moi indiquant le statut CI (par exemple, ci).

Quand rechercher une consultation : si votre projet implique des dépendances complexes (MPI, code GPU, bibliothèques propriétaires) ou possède 10 000 lignes de code, envisagez un examen professionnel de votre configuration CI. Nous proposons services de mise en œuvre CI/CD personnalisés pour les équipes de recherche.

Références et lectures complémentaires


Compte de mots : ~2 200
Temps de lecture : ~10 minutes
Audience cible : chercheurs, étudiants diplômés et développeurs travaillant sur des projets Python scientifiques qui ont besoin d’établir une qualité fiable et automatisée assurance.