`execution_timeout` plafonne une tentative, pas la tâche. Avec deux reprises, quinze minutes par tentative autorisaient quarante-neuf minutes par tâche et quatre-vingt-dix-huit pour l'enchaînement, quand le commentaire annonçait une somme tenant sous le pas horaire. Le plafond passe à cinq minutes, ce qui borne le pire cas à trente-huit minutes, et le test d'intégrité calcule désormais ce pire cas plutôt que la somme des plafonds : reprises et délais d'attente compris, c'est la durée qu'un `max_active_runs=1` fait payer à l'exécution suivante. La CI vérifie aussi `app.cli generate-recommendations --help` sans réseau. C'est la seconde commande du DAG, et son import tire FastAPI, les repositories et les services, donc une part de l'environnement `/opt/backend` que la détection seule ne touche pas. `10-infra.md` nomme enfin ce que le décalage de quinze minutes ne garantit pas : le plafond de `ml_score` valant trente minutes, un scoring qui déborde prive la règle `anomaly` de la prédiction de l'heure, qu'elle ne retrouvera au passage suivant que si sa fenêtre la couvre encore.
102 lines
3.6 KiB
YAML
102 lines
3.6 KiB
YAML
name: Airflow
|
|
|
|
# Piège : la version de Python vient de etl/airflow/.python-version. C'est 3.12 et non 3.14
|
|
# (contrairement à backend.yml et ml.yml) : apache-airflow 2.10 ne supporte pas 3.14. Le 3.14 de
|
|
# ml/ ne vit que dans l'image Docker, dans son propre environnement (cf. etl/airflow/Dockerfile).
|
|
#
|
|
# Piège : l'image COPY les fichiers de dépendances et le code de ml/ et de apps/backend/. Une
|
|
# modification de l'un ou de l'autre peut donc casser sa construction, d'où ces chemins dans
|
|
# les déclencheurs, alors même que ce workflow ne teste ni le modèle ni l'API.
|
|
|
|
on:
|
|
push:
|
|
paths:
|
|
- "etl/airflow/**"
|
|
- "ml/pyproject.toml"
|
|
- "ml/uv.lock"
|
|
- "ml/enervision_ml/**"
|
|
- "apps/backend/pyproject.toml"
|
|
- "apps/backend/uv.lock"
|
|
- "apps/backend/app/**"
|
|
- ".github/workflows/airflow.yml"
|
|
pull_request:
|
|
paths:
|
|
- "etl/airflow/**"
|
|
- "ml/pyproject.toml"
|
|
- "ml/uv.lock"
|
|
- "ml/enervision_ml/**"
|
|
- "apps/backend/pyproject.toml"
|
|
- "apps/backend/uv.lock"
|
|
- "apps/backend/app/**"
|
|
- ".github/workflows/airflow.yml"
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: airflow-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
verification:
|
|
name: Lint et intégrité des DAGs
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: etl/airflow
|
|
|
|
steps:
|
|
- name: Récupère le dépôt
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Installe uv
|
|
uses: astral-sh/setup-uv@v5
|
|
with:
|
|
enable-cache: true
|
|
cache-dependency-glob: etl/airflow/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 .
|
|
|
|
# Aucun test ne lance de tâche ni de scheduler : DagBag charge les fichiers de dags/ et
|
|
# vérifie import, planification, plafonds d'exécution et commande de chaque tâche.
|
|
- name: Tests d'intégrité des DAGs
|
|
run: uv run pytest
|
|
|
|
image:
|
|
name: Construction de l'image
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- name: Récupère le dépôt
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Construit l'image (contexte à la racine, elle COPY ml/ et apps/backend/)
|
|
run: docker build -f etl/airflow/Dockerfile -t enervision-airflow:ci .
|
|
|
|
# Vérifie ce qui ne casse qu'à l'exécution, pas à la construction : libgomp1 absent
|
|
# (`OSError: libgomp.so.1` au premier import) ou environnement ml/ non figé.
|
|
- name: Vérifie que le pipeline ML s'importe sans réseau
|
|
run: >
|
|
docker run --rm --network none enervision-airflow:ci
|
|
bash -c "cd /opt/ml && env -u VIRTUAL_ENV uv run --no-sync python -m enervision_ml.train --help"
|
|
|
|
# `--help` sort par argparse avant `get_settings()` : ni base ni secret requis, et
|
|
# l'import du module prouve que l'environnement /opt/backend est complet. Les deux
|
|
# commandes du DAG `alertes` sont couvertes, `app.cli` tirant tout FastAPI derrière lui.
|
|
- name: Vérifie que les deux commandes du DAG alertes s'importent sans réseau
|
|
run: >
|
|
docker run --rm --network none enervision-airflow:ci
|
|
bash -c "cd /opt/backend
|
|
&& env -u VIRTUAL_ENV uv run --no-sync python -m app.detection.internal_alerts --help
|
|
&& env -u VIRTUAL_ENV uv run --no-sync python -m app.cli generate-recommendations --help"
|