Airflow / Construction de l'image (push) Successful in 1m2s
Backend / Analyse statique de sécurité (push) Successful in 7s
Backend / Tests exigeant une base (push) Failing after 5m3s
Airflow / Lint et intégrité des DAGs (push) Successful in 9m41s
ML / Analyse statique de sécurité (push) Successful in 6s
Backend / Lint, typage et tests (push) Successful in 10m10s
Backend / Audit des dépendances (push) Successful in 9m36s
ML / ML - DB et chaîne ML - DB - API (push) Failing after 5m6s
SonarQube / test-ml (push) Failing after 6m6s
ML / Lint, typage et tests (push) Successful in 11m31s
SonarQube / build-front (push) Successful in 9m49s
SonarQube / build-back (push) Successful in 9m54s
SonarQube / test-front (push) Failing after 5m10s
SonarQube / test-back (push) Failing after 5m13s
SonarQube / SonarQube (push) Skipped
Revue de la branche : trois defauts empechaient la surveillance de tenir ce qu'elle annonce. - `evaluate()` gardait les microsecondes de `now()` dans `window_end`, la cle de `uq_drift_report_window`. Deux executions ne collidaient donc jamais et l'index ne dedoublonnait rien, contrairement a ce qu'affirmaient l'ADR 0011, 20-backend et le docstring du DAG. L'instant de reference est desormais tronque a l'heure. - Un site qui cessait d'etre score disparaissait du rapport : la liste des sites ne venait que de la fenetre recente. La panne que cette surveillance existe pour dire etait exactement celle qu'elle taisait. La fenetre de reference entre maintenant dans l'union, et le site recoit sa ligne `indetermine` a zero observation. - Sans fenetre de reference, `_plafond` rendait `None` et le verdict tombait sur `stable`, une affirmation que la donnee ne portait pas. C'est `indetermine` desormais. `ml.yml` ecoute `apps/backend/app/**` et non les seuls modeles : ce workflow est le seul a jouer `-m chaine`, or la chaine traverse les endpoints, les services et les schemas jusqu'a `GET /predictions`. Une PR touchant `predictions.py` ne declenchait pas le test qui l'assert. Hygiene de tests : le nettoyage des fixtures API connait `drift_report` (cle etrangere RESTRICT vers `site`), le test sans rapport rend ses overrides en teardown, `test_chaine_ml_api` compare les `created_at` strictement (un `>=` passait aussi quand l'API resservait la premiere ligne), et `test_data_integration` filtre sur le site seme au lieu de juger tout le contenu d'une fenetre dans une base partagee. Docs remises d'aplomb : sept revisions Alembic et non six, `derive.py` dans l'inventaire de etl/README, et le diagramme de 20-backend gagne DriftService, le depot drift et sa treizieme table.
181 lines
6.5 KiB
YAML
181 lines
6.5 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).
|
|
|
|
on:
|
|
push:
|
|
paths:
|
|
- "ml/**"
|
|
- ".github/workflows/ml.yml"
|
|
# Piege : `backend.yml` ne joue jamais `-m chaine`, ce job est le seul. Le test de chaine
|
|
# traverse tout `apps/backend/app` jusqu'a GET /predictions : restreindre le filtre aux
|
|
# migrations et aux modeles le laisserait muet sur la PR meme qui le casse. Meme raisonnement
|
|
# que le filtre d'airflow.yml, qui inclut deja des chemins de ml/ et de apps/backend/.
|
|
- "apps/backend/alembic/**"
|
|
- "apps/backend/app/**"
|
|
- "apps/backend/tests/test_chaine_ml_api.py"
|
|
- "apps/backend/pyproject.toml"
|
|
- "apps/backend/uv.lock"
|
|
pull_request:
|
|
paths:
|
|
- "ml/**"
|
|
- ".github/workflows/ml.yml"
|
|
# Piege : `backend.yml` ne joue jamais `-m chaine`, ce job est le seul. Le test de chaine
|
|
# traverse tout `apps/backend/app` jusqu'a GET /predictions : restreindre le filtre aux
|
|
# migrations et aux modeles le laisserait muet sur la PR meme qui le casse. Meme raisonnement
|
|
# que le filtre d'airflow.yml, qui inclut deja des chemins de ml/ et de apps/backend/.
|
|
- "apps/backend/alembic/**"
|
|
- "apps/backend/app/**"
|
|
- "apps/backend/tests/test_chaine_ml_api.py"
|
|
- "apps/backend/pyproject.toml"
|
|
- "apps/backend/uv.lock"
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: ml-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
verification:
|
|
name: Lint, typage et tests
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: ml
|
|
|
|
steps:
|
|
- name: Récupère le dépôt
|
|
uses: actions/checkout@v7
|
|
|
|
- name: Installe uv
|
|
uses: astral-sh/setup-uv@v7
|
|
with:
|
|
enable-cache: true
|
|
cache-dependency-glob: ml/uv.lock
|
|
|
|
- name: Installe l'interpréteur déclaré par .python-version
|
|
run: uv python install
|
|
|
|
- name: Synchronise les dépendances sans dévier du verrou
|
|
run: uv sync --all-groups --frozen
|
|
|
|
- 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`, ecarte par defaut et
|
|
# joue par le job `integration` ci-dessous.
|
|
- name: Tests
|
|
run: uv run pytest
|
|
|
|
# Le seul job du depot qui dispose a la fois des deux environnements uv et d'une base. Piege :
|
|
# le schema de la base ML est celui du backend (apps/backend/alembic, proprietaire du schema).
|
|
# Le reconstruire ici a la main rendrait ce job vert sur une base qui n'est pas la notre.
|
|
integration:
|
|
name: ML - DB et chaîne ML - DB - API
|
|
runs-on: ubuntu-latest
|
|
|
|
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@v7
|
|
with:
|
|
enable-cache: true
|
|
cache-dependency-glob: |
|
|
ml/uv.lock
|
|
apps/backend/uv.lock
|
|
|
|
- name: Installe l'interpréteur déclaré par .python-version
|
|
working-directory: ml
|
|
run: uv python install
|
|
|
|
- name: Synchronise le pipeline ML sans dévier du verrou
|
|
working-directory: ml
|
|
run: uv sync --all-groups --frozen
|
|
|
|
# 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 sans dévier du verrou
|
|
working-directory: apps/backend
|
|
run: uv sync --all-groups --frozen
|
|
|
|
# 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
|
|
|
|
# `-m` en ligne de commande écrase celui d'addopts. 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
|
|
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@v7
|
|
|
|
- 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
|