ci: Gitea Actions workflows + requirements-ci allégé

Workflows :
  .gitea/workflows/tests.yml          -> lint + unit + security (PR + push)
  .gitea/workflows/security-audit.yml -> bandit + pip-audit + grep secrets
                                         (hebdo + push main)

requirements-ci.txt : sous-ensemble léger de requirements.txt
  - Sans torch, transformers, CUDA, FAISS binaire, Ollama, PyQt5, doctr
  - Gain ~3 Go + ~2 min d'install CI
  - À resynchroniser manuellement si nouveau test importe un package absent

Tests slow/gpu/integration/performance/visual/smoke exclus volontairement
(nécessitent CUDA, Ollama localhost:11434, serveur complet).

Temps estimé par run :
  - Cold : ~3 min
  - Warm (cache pip) : ~1m30

Security-tests (test_security_safe_condition + test_security_signed_serializer)
marqués bloquants : régression sur ast eval safe ou pickle HMAC casse la CI.

docs/CI_SETUP.md : activation Gitea Actions, enregistrement runner,
skip CI, troubleshooting.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dom
2026-04-15 09:07:40 +02:00
parent b808e48b1f
commit 2bfcfa4535
4 changed files with 740 additions and 0 deletions

View File

@@ -0,0 +1,207 @@
# ------------------------------------------------------------------
# Audit sécurité — bandit + pip-audit + scan secrets
# ------------------------------------------------------------------
# Jamais bloquant : on reporte les warnings, on ne casse pas la CI.
# Utile pour détecter les dérives progressives (nouveaux CVE, secrets
# oubliés dans un commit, patterns risqués).
#
# Fréquence : à chaque push sur main + hebdo (cron).
# ------------------------------------------------------------------
name: security-audit
on:
push:
branches:
- main
schedule:
# Tous les lundis à 6h UTC (8h Paris hiver, 7h Paris été).
- cron: "0 6 * * 1"
workflow_dispatch: {}
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# ----------------------------------------------------------------
# Job 1 — bandit (bonnes pratiques sécu Python)
# ----------------------------------------------------------------
bandit:
name: Bandit (scan statique)
runs-on: ubuntu-latest
timeout-minutes: 5
continue-on-error: true
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip"
- name: Installation bandit
run: |
python -m pip install --upgrade pip
pip install "bandit[toml]==1.7.10"
- name: Scan bandit sur core/
run: |
# -ll : niveau LOW minimum (remonte tout)
# -ii : confiance LOW minimum
# --skip B101 : on ignore les asserts (usuels en tests/validation)
bandit -r core/ \
--skip B101,B404,B603 \
--format txt \
--exit-zero \
--output bandit-report.txt
echo "=== RAPPORT BANDIT ==="
cat bandit-report.txt
- name: Upload rapport bandit
if: always()
uses: actions/upload-artifact@v3
with:
name: bandit-report
path: bandit-report.txt
retention-days: 30
if-no-files-found: ignore
# ----------------------------------------------------------------
# Job 2 — pip-audit (CVE sur requirements)
# ----------------------------------------------------------------
pip-audit:
name: pip-audit (CVE dépendances)
runs-on: ubuntu-latest
timeout-minutes: 5
continue-on-error: true
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip"
- name: Installation pip-audit
run: |
python -m pip install --upgrade pip
pip install "pip-audit==2.7.3"
- name: Audit CVE sur requirements-ci.txt
run: |
if [ -f requirements-ci.txt ]; then
pip-audit -r requirements-ci.txt \
--format json \
--output pip-audit-ci.json \
--progress-spinner off \
--disable-pip || echo "::warning::CVE détectées dans requirements-ci.txt"
echo "=== RAPPORT pip-audit (CI) ==="
cat pip-audit-ci.json || true
else
echo "::notice::requirements-ci.txt absent — skip"
fi
- name: Audit CVE sur requirements.txt (best-effort)
run: |
# Timeout généreux car requirements.txt est massif (torch, CUDA).
timeout 120 pip-audit -r requirements.txt \
--format json \
--output pip-audit-full.json \
--progress-spinner off \
--disable-pip 2>&1 | head -200 || \
echo "::warning::pip-audit sur requirements.txt a timeout ou échoué (non bloquant)"
- name: Upload rapports pip-audit
if: always()
uses: actions/upload-artifact@v3
with:
name: pip-audit-reports
path: |
pip-audit-ci.json
pip-audit-full.json
retention-days: 30
if-no-files-found: ignore
# ----------------------------------------------------------------
# Job 3 — Scan secrets en clair (grep simple)
# ----------------------------------------------------------------
# Patterns recherchés : clés API Anthropic (sk-ant-), OpenAI (sk-),
# Google (AIzaSy), AWS (AKIA), tokens Hugging Face (hf_).
# Ne cherche QUE dans les fichiers trackés (pas .env, pas .venv).
# ----------------------------------------------------------------
secrets-scan:
name: Scan secrets (grep)
runs-on: ubuntu-latest
timeout-minutes: 3
continue-on-error: true
steps:
- name: Checkout (historique complet)
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Scan patterns de secrets
run: |
# Chemins exclus : venvs, caches, data, htmlcov, models.
EXCLUDES='--exclude-dir=.venv --exclude-dir=venv_v3 --exclude-dir=.git \
--exclude-dir=node_modules --exclude-dir=htmlcov --exclude-dir=models \
--exclude-dir=data --exclude-dir=__pycache__ --exclude-dir=.pytest_cache \
--exclude=*.lock --exclude=*.log --exclude=*.md'
echo "=== Recherche de secrets potentiels ==="
FOUND=0
# Anthropic
if grep -rnI $EXCLUDES -E 'sk-ant-[a-zA-Z0-9_-]{20,}' . 2>/dev/null; then
echo "::warning::Clé Anthropic potentielle détectée"
FOUND=1
fi
# OpenAI
if grep -rnI $EXCLUDES -E 'sk-proj-[a-zA-Z0-9_-]{20,}|sk-[a-zA-Z0-9]{40,}' . 2>/dev/null; then
echo "::warning::Clé OpenAI potentielle détectée"
FOUND=1
fi
# Google Cloud / API Keys
if grep -rnI $EXCLUDES -E 'AIzaSy[a-zA-Z0-9_-]{33}' . 2>/dev/null; then
echo "::warning::Clé Google API potentielle détectée"
FOUND=1
fi
# AWS
if grep -rnI $EXCLUDES -E 'AKIA[0-9A-Z]{16}' . 2>/dev/null; then
echo "::warning::Clé AWS potentielle détectée"
FOUND=1
fi
# Hugging Face
if grep -rnI $EXCLUDES -E 'hf_[a-zA-Z0-9]{30,}' . 2>/dev/null; then
echo "::warning::Token Hugging Face potentiel détecté"
FOUND=1
fi
# Mots-clés suspects à côté d'assignations
if grep -rnI $EXCLUDES -E '(password|passwd|secret|api_key|apikey|token)\s*=\s*["\x27][a-zA-Z0-9_\-!@#\$%]{12,}["\x27]' . 2>/dev/null \
| grep -viE '(example|dummy|placeholder|test|fake|xxx|changeme|\$\{)' 2>/dev/null; then
echo "::warning::Assignation suspecte d'un secret détectée"
FOUND=1
fi
if [ "$FOUND" -eq 0 ]; then
echo "Aucun secret détecté par les patterns de base."
else
echo ""
echo "::notice::Vérifier manuellement les occurrences ci-dessus."
echo "::notice::Si faux positif : ajouter le fichier aux exclusions ou reformater."
fi
# Toujours succès (job non bloquant).
exit 0

199
.gitea/workflows/tests.yml Normal file
View File

@@ -0,0 +1,199 @@
# ------------------------------------------------------------------
# CI principale — Tests unitaires + lint léger
# ------------------------------------------------------------------
# Déclenchement : push / pull_request sur n'importe quelle branche.
# Objectif : feedback rapide (< 3 min) sans GPU ni Ollama.
# Runner : self-hosted (label "ubuntu-latest" ou équivalent).
#
# Les tests marqués `slow`, `gpu`, `integration`, `performance`,
# `visual` et `smoke` sont exclus volontairement — ils nécessitent
# CUDA, Ollama, ou des captures d'écran réelles.
# ------------------------------------------------------------------
name: tests
on:
push:
branches:
- "**"
pull_request:
branches:
- "**"
# Permet à une nouvelle exécution d'annuler les précédentes
# sur la même branche (évite l'engorgement du runner local).
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
# Empêche l'import accidentel de torch/CUDA pendant la CI.
PYTHONDONTWRITEBYTECODE: "1"
PIP_DISABLE_PIP_VERSION_CHECK: "1"
PIP_NO_PYTHON_VERSION_WARNING: "1"
# Les modules d'exécution lisent parfois ces vars ; valeurs neutres en CI.
RPA_VISION_CI: "1"
RPA_AUTH_VAULT_PATH: "/tmp/ci_vault.enc"
jobs:
# ----------------------------------------------------------------
# Job 1 — Lint (ruff + black --check)
# ----------------------------------------------------------------
# Non-bloquant : si ruff/black ne sont pas installables, on log
# un warning et on continue. L'objectif ici est d'alerter, pas de
# casser la CI pour des espaces en trop.
# ----------------------------------------------------------------
lint:
name: Lint (ruff + black)
runs-on: ubuntu-latest
timeout-minutes: 5
continue-on-error: true
steps:
- name: Checkout du code
uses: actions/checkout@v4
- name: Setup Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip"
- name: Installation des linters
run: |
python -m pip install --upgrade pip
pip install "ruff==0.6.9" "black==23.12.1" || {
echo "::warning::Impossible d'installer ruff/black — job ignoré"
exit 0
}
- name: Ruff (lint rapide)
run: |
if command -v ruff >/dev/null 2>&1; then
# Ruff : on limite aux erreurs critiques (E9, F63, F7, F82) pour
# éviter le bruit. Dom peut durcir progressivement.
ruff check --select=E9,F63,F7,F82 --output-format=github \
core/ agent_v0/ tests/ || {
echo "::warning::Ruff a trouvé des erreurs critiques"
exit 1
}
else
echo "::warning::ruff indisponible — skip"
fi
- name: Black (format check)
run: |
if command -v black >/dev/null 2>&1; then
# --check : ne modifie pas, signale juste.
black --check --diff core/ agent_v0/ tests/ || {
echo "::warning::Black suggère un reformatage — non bloquant"
exit 0
}
else
echo "::warning::black indisponible — skip"
fi
# ----------------------------------------------------------------
# Job 2 — Tests unitaires
# ----------------------------------------------------------------
# Exclut tous les marqueurs lourds. Utilise requirements-ci.txt
# pour éviter torch/CUDA (économie ~3 Go + ~2 min).
# ----------------------------------------------------------------
unit-tests:
name: Tests unitaires (sans GPU)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout du code
uses: actions/checkout@v4
- name: Setup Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip"
cache-dependency-path: |
requirements-ci.txt
requirements.txt
- name: Installation des dépendances CI
run: |
python -m pip install --upgrade pip
if [ -f requirements-ci.txt ]; then
echo "Utilisation de requirements-ci.txt (léger, sans torch)"
pip install -r requirements-ci.txt
else
echo "::warning::requirements-ci.txt absent — fallback requirements.txt (lourd)"
pip install -r requirements.txt
fi
- name: Vérification imports critiques
run: |
python -c "import pytest; print(f'pytest {pytest.__version__}')"
python -c "import sys; sys.path.insert(0, '.'); import core; print('core OK')" || {
echo "::error::Impossible d'importer core.*"
exit 1
}
- name: Tests unitaires (hors slow/gpu/integration)
run: |
python -m pytest tests/unit/ \
-m "not slow and not gpu and not integration and not performance and not visual" \
--tb=short \
--strict-markers \
-q \
--maxfail=10 \
-o cache_dir=/tmp/.pytest_cache_ci
- name: Upload logs si échec
if: failure()
uses: actions/upload-artifact@v3
with:
name: pytest-logs
path: |
/tmp/.pytest_cache_ci
logs/
retention-days: 3
if-no-files-found: ignore
# ----------------------------------------------------------------
# Job 3 — Tests sécurité (bloquant)
# ----------------------------------------------------------------
# Les tests `test_security_*` valident des invariants critiques
# (évaluation sûre, sérialisation signée). Aucune régression tolérée.
# ----------------------------------------------------------------
security-tests:
name: Tests sécurité (critique)
runs-on: ubuntu-latest
timeout-minutes: 5
needs: [unit-tests]
steps:
- name: Checkout du code
uses: actions/checkout@v4
- name: Setup Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip"
cache-dependency-path: |
requirements-ci.txt
requirements.txt
- name: Installation des dépendances CI
run: |
python -m pip install --upgrade pip
if [ -f requirements-ci.txt ]; then
pip install -r requirements-ci.txt
else
pip install -r requirements.txt
fi
- name: Tests sécurité (test_security_*)
run: |
python -m pytest tests/unit/test_security_*.py \
--tb=long \
--strict-markers \
-v \
-o cache_dir=/tmp/.pytest_cache_ci_sec

225
docs/CI_SETUP.md Normal file
View File

@@ -0,0 +1,225 @@
# CI Setup — Gitea Actions pour RPA Vision V3
Ce document décrit la CI minimale mise en place sur `gitea.localhost:3100`
pour prévenir les régressions silencieuses sur `main` et les PR.
## Vue d'ensemble
Deux workflows Gitea Actions (syntaxe compatible GitHub Actions) :
| Workflow | Fichier | Déclencheur | Bloquant |
|----------------------------------|---------------------------------------|------------------------------|----------|
| Tests | `.gitea/workflows/tests.yml` | push + PR (toutes branches) | Oui (unit + security) |
| Audit sécurité | `.gitea/workflows/security-audit.yml` | push main + cron hebdo | Non |
### Jobs du workflow `tests`
1. **lint** (non bloquant) — `ruff` + `black --check` sur `core/`, `agent_v0/`, `tests/`.
2. **unit-tests** (bloquant) — `pytest tests/unit/` avec `-m "not slow and not gpu and not integration and not performance and not visual"`.
3. **security-tests** (bloquant) — `pytest tests/unit/test_security_*.py` en mode verbose. Dépend de `unit-tests`.
### Jobs du workflow `security-audit`
1. **bandit** — scan statique sur `core/` (asserts ignorés).
2. **pip-audit** — détection CVE sur `requirements-ci.txt` et `requirements.txt`.
3. **secrets-scan**`grep` pour patterns `sk-ant-`, `sk-proj-`, `AIzaSy`, `AKIA`, `hf_`.
Aucun de ces jobs ne casse la CI — ils produisent des artefacts consultables.
## Activation de Gitea Actions
Gitea Actions n'est pas actif par défaut. Deux étapes :
### 1. Activer Actions dans Gitea
Sur `http://localhost:3100`, éditer `/home/dom/Install_base/docker-compose.yml`
(ou le `app.ini` monté dans le conteneur Gitea) et ajouter :
```ini
[actions]
ENABLED = true
DEFAULT_ACTIONS_URL = https://github.com
```
Puis redémarrer Gitea :
```bash
cd /home/dom/Install_base
docker compose restart gitea
```
Vérifier : dans l'UI Gitea → `Site Administration``Configuration Summary`
→ la section `[actions]` doit afficher `enabled: true`.
Côté dépôt : `Settings``Advanced Settings` → cocher **"Enable Repository Actions"**.
### 2. Installer et enregistrer un runner local
Gitea a besoin d'un `act_runner` (fork de nektos/act) pour exécuter les jobs.
```bash
# Téléchargement du runner (Linux amd64)
cd /home/dom/Install_base
mkdir -p gitea_runner && cd gitea_runner
wget https://dl.gitea.com/act_runner/0.2.11/act_runner-0.2.11-linux-amd64 -O act_runner
chmod +x act_runner
# Génération de la config
./act_runner generate-config > config.yaml
# Récupération du token d'enregistrement
# Site Administration → Actions → Runners → Create new Runner
# (ou pour un runner par-dépôt : Settings du dépôt → Actions → Runners)
# Enregistrement (interactif)
./act_runner register --no-interactive \
--instance http://localhost:3100 \
--token <TOKEN_COPIE_DEPUIS_GITEA> \
--name "runner-local-cpu" \
--labels "ubuntu-latest:docker://catthehacker/ubuntu:act-22.04"
# Lancement en daemon
nohup ./act_runner daemon --config config.yaml > runner.log 2>&1 &
```
Pour persister au reboot : créer un service systemd
(cf. `~/ai/rpa_vision_v3/deploy/systemd/` pour un modèle).
**Note** : le label `ubuntu-latest` pointe sur une image Docker légère
(`catthehacker/ubuntu:act-22.04`, ~300 Mo) qui suffit pour nos jobs Python.
### 3. Premier test
```bash
cd /home/dom/ai/rpa_vision_v3
# Modification triviale
echo "" >> README.md
git add README.md
git commit -m "chore: trigger CI"
git push gitea main
```
Dans l'UI Gitea → onglet `Actions` du dépôt, le workflow doit apparaître
et passer en ~2 minutes.
## Lancer les tests localement avant push
Identique à la CI :
```bash
cd /home/dom/ai/rpa_vision_v3
source .venv/bin/activate
# Tests unitaires (hors slow/gpu/integration) — ~60s
pytest tests/unit/ -m "not slow and not gpu and not integration and not performance and not visual" -q
# Tests sécurité seulement — ~5s
pytest tests/unit/test_security_*.py -v
# Lint (si installé)
ruff check --select=E9,F63,F7,F82 core/ agent_v0/ tests/
black --check core/ agent_v0/ tests/
```
Ou via Makefile :
```bash
make test-fast # équivalent à "not slow"
make check # validate-imports + test-fast
```
## Désactiver temporairement la CI (merge urgent)
Trois options, de la plus propre à la plus brutale :
### Option 1 — Skip via message de commit (recommandé)
Préfixer le message avec `[skip ci]` ou `[ci skip]` :
```bash
git commit -m "fix: hotfix prod [skip ci]"
```
Gitea Actions respecte cette convention.
### Option 2 — Désactiver le workflow côté dépôt
Dans l'UI Gitea → dépôt → `Actions` → sélectionner le workflow → bouton
**"Disable workflow"**. Réactivable au même endroit.
### Option 3 — Renommer le fichier
```bash
mv .gitea/workflows/tests.yml .gitea/workflows/tests.yml.disabled
git commit -am "chore: disable CI temporarily"
```
Ne **jamais** supprimer le fichier — ça rend le rollback pénible.
## Limitations connues
- **Pas de tests `slow` / `gpu` / `integration`** en CI. Ces tests nécessitent
CUDA, Ollama (port 11434), ou des captures d'écran réelles. Ils doivent
être lancés manuellement sur la machine de dev avant un tag de release.
- **Pas de tests E2E `smoke`** (`tests/smoke/`) — nécessitent le serveur
complet (ports 5005, 5001, 5002, 3002).
- **Pas de tests `visual`** (`tests/visual/`) — nécessitent le serveur GPU.
- **Runner unique** : tant qu'il n'y a qu'un `act_runner` enregistré,
les jobs s'exécutent en série. Acceptable pour < 10 builds/jour.
- **Pas de `torch` en CI** : si un test unitaire importe `torch` directement
(sans lazy import), il échouera. Convention : les imports GPU doivent
être dans `try/except ImportError` + marqueur `@pytest.mark.gpu`.
- **`requirements-ci.txt` à resynchroniser** : quand une dépendance est
ajoutée à `requirements.txt` et utilisée par un test unitaire, penser
à l'ajouter aussi à `requirements-ci.txt`.
## Temps d'exécution estimé
| Job | Cold (sans cache pip) | Warm (cache pip) |
|-----------------|----------------------|------------------|
| lint | ~40s | ~15s |
| unit-tests | ~2m30 | ~1m15 |
| security-tests | ~1m | ~30s |
| **Total CI** | **~3m** | **~1m30** |
Le cache pip est géré automatiquement par `actions/setup-python@v5`
via la clé `requirements-ci.txt` + `requirements.txt`.
## Troubleshooting
### Le workflow ne se déclenche pas
1. Vérifier que `[actions]` est actif dans `app.ini` Gitea.
2. Vérifier que le runner est bien enregistré : `Site Administration``Actions``Runners`.
3. Le runner doit être `Online` (point vert).
4. Le dépôt doit avoir Actions activées dans ses paramètres.
### Erreur "No runner available"
Le runner est stoppé ou a un label incompatible. Relancer :
```bash
cd /home/dom/Install_base/gitea_runner
ps aux | grep act_runner # vérifier s'il tourne
tail -f runner.log # voir les erreurs
```
### Timeout sur `pip install`
`requirements.txt` contient torch + CUDA (~3 Go). Si la CI tombe sur
`requirements.txt` au lieu de `requirements-ci.txt`, vérifier que le
fichier léger est bien committé à la racine du repo.
### Tests passent en local mais échouent en CI
Diff le plus fréquent :
- Variables d'environnement (`.env.local` absent en CI → tester avec `unset` en local).
- Ports déjà pris par `svc.sh` en local mais libres en CI (→ OK).
- Paths absolus hardcodés (`/home/dom/...`) → utiliser `pathlib` + fixtures.
## Évolutions possibles
- Ajouter un job `type-check` avec `mypy core/` (actuellement dans `requirements.txt` mais pas en CI — choix délibéré : trop lent et 200+ erreurs à nettoyer d'abord).
- Ajouter un job `coverage` avec seuil minimum (ex: 60%).
- Brancher les résultats sur un badge README via `gitea-actions-status`.
- Pour les PR : bloquer le merge tant que `unit-tests` + `security-tests` ne passent pas (réglable dans `Settings``Branches``Branch protection rules`).

109
requirements-ci.txt Normal file
View File

@@ -0,0 +1,109 @@
# ------------------------------------------------------------------
# requirements-ci.txt — Dépendances pour la CI (tests unitaires)
# ------------------------------------------------------------------
# Objectif : installer le minimum pour que `pytest tests/unit/`
# passe sans GPU, sans Ollama, sans torch, sans FAISS GPU.
#
# Les tests lourds (torch, transformers, CLIP, FAISS GPU, doctr,
# Ollama) sont marqués `slow`, `gpu` ou `integration` et exclus
# via `-m "not slow and not gpu and not integration"`.
#
# Versions alignées sur requirements.txt pour éviter les surprises
# lors du runtime local, mais allégées (CPU-only, headless).
# ------------------------------------------------------------------
# --- Runtime core ---
pydantic==2.12.5
pydantic_core==2.41.5
python-dotenv==1.0.0
PyYAML==6.0.1
click==8.3.1
typing_extensions==4.15.0
annotated-types==0.7.0
# --- Web frameworks (utilisés par les tests API/dashboard) ---
fastapi==0.128.0
starlette==0.50.0
uvicorn==0.40.0
Flask==3.0.0
Flask-Caching==2.1.0
Flask-Cors==4.0.0
Flask-SQLAlchemy==3.1.1
Werkzeug==3.1.5
Jinja2==3.1.6
itsdangerous==2.2.0
blinker==1.9.0
# --- DB (tests auth/audit/extraction) ---
SQLAlchemy==2.0.23
alembic==1.18.4
# --- HTTP clients ---
httpx==0.28.1
requests==2.32.5
urllib3==2.6.3
certifi==2026.1.4
idna==3.11
charset-normalizer==3.4.4
h11==0.16.0
httpcore==1.0.9
anyio==4.12.1
sniffio==1.3.1; python_version >= "3.7"
# --- Sécurité (test_security_*, auth vault, TOTP) ---
cryptography==46.0.3
cffi==2.0.0
pycparser==2.23
# --- Images (opencv-python-headless au lieu de opencv-python pour CI) ---
pillow==12.1.0
opencv-python-headless==4.12.0.88
numpy==2.2.6
# --- Pytest et plugins ---
pytest==9.0.2
pytest-asyncio==1.3.0
pytest-cov==4.1.0
pytest-flask==1.3.0
pytest-mock==3.12.0
iniconfig==2.3.0
pluggy==1.6.0
packaging==25.0
# --- Couverture ---
coverage==7.13.1
# --- Utilitaires divers (imports indirects fréquents) ---
python-dateutil==2.8.2
six==1.17.0
attrs==25.4.0
jsonschema==4.20.0
jsonschema-specifications==2025.9.1
referencing==0.37.0
rpds-py==0.30.0
RapidFuzz==3.14.3
regex==2025.11.3
python-multipart==0.0.21
validators==0.35.0
prometheus_client==0.23.1
psutil==7.2.1
filelock==3.20.3
tqdm==4.67.1
# --- Hypothesis (property tests, si inclus plus tard) ---
hypothesis==6.92.1
sortedcontainers==2.4.0
# --- NOTES ---
# Volontairement absents :
# - torch / torchvision / triton / nvidia-* → GPU, hors CI
# - transformers / accelerate / tokenizers → chargent torch
# - open_clip_torch / timm → idem
# - faiss-cpu → binaire lourd (~90 Mo),
# utilisé uniquement en
# tests `slow` / `integration`
# - ollama → nécessite serveur Ollama
# - python-doctr / pypdfium2 → OCR, tests `slow`
# - pynput / pyautogui / mss / PyQt5 → GUI / simulation I/O
# - python-socketio / Flask-SocketIO → WS, tests intégration
# - eds-nlp / spacy → modèles NLP hors CI