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:
207
.gitea/workflows/security-audit.yml
Normal file
207
.gitea/workflows/security-audit.yml
Normal 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
199
.gitea/workflows/tests.yml
Normal 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
225
docs/CI_SETUP.md
Normal 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
109
requirements-ci.txt
Normal 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
|
||||
Reference in New Issue
Block a user