Files
ENI-projet-piscine/.github/workflows/ml.yml
T
Johan LEROY 18307e9be3 ci: rassemble la CI dans un orchestrateur unique et retire le doublon Sonar
Chaque workflow se déclenchait sur push (toutes branches) et sur pull_request : chaque commit
de PR jouait tout deux fois. sonarqube.yml reconstruisait et retestait front, back et ML en
parallèle des workflows qui le faisaient déjà, et son test backend tournait sans uv sync.

ci.yml devient le seul point d'entrée (pull_request, push sur dev et main) :
- paths-filter choisit les composants à jouer sur une PR, tout est rejoué sur dev et main ;
- backend, frontend, ml, airflow et infra passent en workflow_call ;
- le job sonar reprend les couvertures versées par ces jobs au lieu de tout rejouer ;
- « CI ok » agrège le résultat, seul check à exiger dans les règles de branche.

Au passage :
- npm run test:ci au lieu de npm test --watch=false, option que npm gardait pour lui ;
- uv sync --locked au lieu de --frozen, pour qu'un verrou périmé casse la CI ;
- setup-uv et sonarqube-scan-action épinglés sur un SHA (règle S7637), timeout sur chaque job ;
- frontend : un seul npm ci pour la construction et les tests ;
- infra : validation des fichiers Compose et actionlint sur les workflows ;
- exclusions Sonar en globs, doublon apps/frontend/sonar-project.properties supprimé.
2026-09-23 09:16:08 +02:00

168 lines
5.8 KiB
YAML

name: ML
# Piège : la version de Python vient de ml/.python-version, et doit rester en 3.14 (cf.
# .github/workflows/backend.yml, même contrainte).
# Pourquoi : aucun déclencheur propre. ci.yml l'appelle aussi quand les migrations ou les modèles
# du backend changent, dont dépend le job `integration` (ADR 0014).
on:
workflow_call:
permissions:
contents: read
jobs:
verification:
name: Lint, typage et tests
runs-on: ubuntu-latest
timeout-minutes: 15
defaults:
run:
working-directory: ml
steps:
- name: Récupère le dépôt
uses: actions/checkout@v7
# Action tierce, épinglée sur le commit du tag (règle Sonar githubactions:S7637).
- name: Installe uv
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
with:
enable-cache: true
cache-dependency-glob: ml/uv.lock
prune-cache: false
- name: Installe l'interpréteur déclaré par .python-version
run: uv python install
- name: Synchronise les dépendances sur le verrou
run: uv sync --all-groups --locked
- name: Vérifie le formatage
run: uv run ruff format --check .
- name: Analyse statique
run: uv run ruff check --output-format=github .
- name: Typage
run: uv run mypy enervision_ml tests
# Les tests exigeant une base portent le marqueur `integration`, écarté par défaut et
# joué par le job `integration` ci-dessous.
- name: Tests et couverture
run: uv run pytest --cov-report=xml
- name: Verse la couverture pour Sonar
uses: actions/upload-artifact@v7
with:
name: ml-coverage
path: ml/coverage.xml
if-no-files-found: error
# Piège : le schéma de la base ML est celui du backend (apps/backend/alembic, propriétaire du
# schéma). Le reconstruire ici à la main rendrait ce job vert sur une base qui n'est pas la nôtre.
integration:
name: ML - DB et chaîne ML - DB - API
runs-on: ubuntu-latest
timeout-minutes: 20
services:
db:
image: timescale/timescaledb-ha:pg17
env:
POSTGRES_USER: enervision
POSTGRES_PASSWORD: change_me
POSTGRES_DB: enervision_test
ports:
- "5433:5432"
options: >-
--health-cmd "pg_isready -U enervision -d enervision_test"
--health-interval 10s
--health-timeout 5s
--health-retries 12
--health-start-period 40s
env:
# Deux variables, deux dialectes : Alembic et l'API parlent asyncpg, le pipeline ML parle
# psycopg en synchrone. Cf. docs/ML-START.md, section 1.
DATABASE_URL: postgresql+asyncpg://enervision:change_me@localhost:5433/enervision_test
ML_DATABASE_URL: postgresql+psycopg://enervision:change_me@localhost:5433/enervision_test
APP_SECRET_KEY: secret-de-test-assez-long-pour-le-validateur
PGPASSWORD: change_me
steps:
- name: Récupère le dépôt
uses: actions/checkout@v7
- name: Installe uv
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
with:
enable-cache: true
cache-dependency-glob: |
ml/uv.lock
apps/backend/uv.lock
prune-cache: false
- name: Installe l'interpréteur déclaré par .python-version
working-directory: ml
run: uv python install
- name: Synchronise le pipeline ML sur le verrou
working-directory: ml
run: uv sync --all-groups --locked
# Le backend est installé ici parce qu'il porte les migrations, seule source du schéma, et
# le test de chaîne, qui interroge l'API.
- name: Synchronise le backend sur le verrou
working-directory: apps/backend
run: uv sync --all-groups --locked
# db/init/110-test-database.sql n'est pas monté ici, et sans l'extension la première
# révision Alembic refuse de s'appliquer.
- name: Active TimescaleDB sur la base de test
run: psql -h localhost -p 5433 -U enervision -d enervision_test -c "CREATE EXTENSION IF NOT EXISTS timescaledb"
- name: Applique les migrations du backend, propriétaire du schéma
working-directory: apps/backend
run: uv run alembic upgrade head
# Couverture désactivée : ce job ne joue qu'une partie de la suite, son taux n'aurait pas
# de sens (même raison que backend.yml).
- name: Tests ML exigeant une base
working-directory: ml
run: uv run pytest -m integration --no-cov
# Lance les vrais binaires enervision_ml.train et .score en sous-processus, comme les DAGs
# ml_train et ml_score, puis relit le résultat par GET /api/v1/predictions.
- name: Chaîne complète ML vers DB vers API
working-directory: apps/backend
env:
ML_PYTHON: ${{ github.workspace }}/ml/.venv/bin/python
run: uv run pytest -m chaine --no-cov
sast:
name: Analyse statique de sécurité
runs-on: ubuntu-latest
timeout-minutes: 10
defaults:
run:
working-directory: ml
steps:
- name: Récupère le dépôt
uses: actions/checkout@v7
# Pourquoi : pas de cache ici. uvx n'installe pas le projet, le verrou n'alimente donc
# aucune clé de cache ; la seule roue téléchargée est celle de Bandit.
- name: Installe uv
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
with:
enable-cache: false
- name: Analyse le code livré (bloquant à partir de MEDIUM)
run: uvx bandit==1.9.4 --recursive enervision_ml --severity-level medium --confidence-level medium
- name: Rapport complet, tous niveaux
continue-on-error: true
run: uvx bandit==1.9.4 --recursive enervision_ml