# ------------------------------------------------------------------ # 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 : erreurs critiques uniquement (E9 syntax, F63 invalid print, # F7 syntax, F82 undefined in __all__). # F821 (undefined name) volontairement exclu le temps de nettoyer # la dette technique préexistante (voir docs/STATUS.md). # Dossiers legacy exclus : # - agent_v0/deploy/windows_client/ : clone obsolète (marqué OBSOLÈTE) # - tests/property/ : tests cassés connus (cf. MEMORY.md) ruff check --select=E9,F63,F7,F82 --output-format=github \ --exclude "agent_v0/deploy/windows_client" \ --exclude "tests/property" \ --exclude "tests/integration/test_visual_rpa_checkpoint.py" \ 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. # Dossiers legacy exclus (cohérent avec ruff). black --check --diff \ --exclude "agent_v0/deploy/windows_client|tests/property" \ 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