Claude commited on
Commit
ce30e80
·
unverified ·
1 Parent(s): d4d4112

fix(ci): unblock CI hang — exclude live tests + cap codecov upload

Browse files

Cause racine de la CI bloquée 6h+ et annulée à 52min :

1. Marker live non enregistré dans pyproject.toml et non exclu
par addopts. Conséquence : tous les tests
tests/integration/live/ étaient COLLECTÉS et exécutés en CI :
- test_tesseract_live.py s'exécutait, échouait sur
/custom/bin/tesseract polluté par un mock unit-test précédent.
- test_llm_live.py se contentait de skip module-level via
pytest.importorskip.
Les 4 'deselected' visibles dans l'output CI ne couvraient que
le marker network.

2. Codecov upload step codecov/codecov-action@v4 sans
timeout-minutes. L'action a déjà bloqué 50+ min en attendant
un upload qui n'aboutissait pas (CODECOV_TOKEN expirée, DNS lent,
TLS handshake bloqué). Couplé à fail_ci_if_error: true, le
job entier hangait jusqu'au timeout du runner GitHub.

Fix
===

- pyproject.toml : live ajouté à markers et inclus dans
l'exclusion par défaut de addopts (-m 'not network and
not live'). Opt-in local via pytest -m live.
- ci.yml : timeout-minutes: 15 sur le step Run tests et
timeout-minutes: 5 sur Upload coverage to Codecov ;
fail_ci_if_error: false sur codecov pour qu'un échec
d'upload n'invalide pas un run de tests valide.

Vérification locale :
- collection : 5022 collected / 8 deselected (5 live + 3 network).
- pytest -m live : 5 tests skippés gracieusement (env vars
absentes), pas d'exécution erronée.
- ruff clean.

Files changed (2) hide show
  1. .github/workflows/ci.yml +10 -1
  2. pyproject.toml +6 -3
.github/workflows/ci.yml CHANGED
@@ -85,10 +85,14 @@ jobs:
85
  # ── Tests ───────────────────────────────────────────────────
86
  # Sprint A1 : --cov-fail-under=85 (baseline mesuré 87 %, marge 2 pts).
87
  # pytest-timeout est configuré dans pyproject.toml [tool.pytest.ini_options].
 
 
 
88
  - name: Run tests
89
  # Sur Python 3.13, on continue malgré une erreur pour ne pas bloquer
90
  # le merge pendant la fenêtre informationnelle de 6 mois (m-8).
91
  continue-on-error: ${{ matrix.python-version == '3.13' }}
 
92
  shell: bash
93
  run: |
94
  pytest tests/ -q --tb=short --no-header \
@@ -99,15 +103,20 @@ jobs:
99
  PYTHONUTF8: "1"
100
 
101
  # ── Couverture ──────────────────────────────────────────────
 
 
 
 
102
  - name: Upload coverage to Codecov
103
  if: runner.os == 'Linux' && matrix.python-version == '3.11' && env.CODECOV_TOKEN != ''
 
104
  uses: codecov/codecov-action@v4
105
  with:
106
  token: ${{ secrets.CODECOV_TOKEN }}
107
  files: coverage.xml
108
  flags: unittests
109
  name: picarones-coverage
110
- fail_ci_if_error: true
111
  env:
112
  CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
113
 
 
85
  # ── Tests ───────────────────────────────────────────────────
86
  # Sprint A1 : --cov-fail-under=85 (baseline mesuré 87 %, marge 2 pts).
87
  # pytest-timeout est configuré dans pyproject.toml [tool.pytest.ini_options].
88
+ # ``timeout-minutes`` au niveau step : le job ne hang JAMAIS plus de
89
+ # 15 min sur les tests, même si pytest-timeout (par-test) échoue à
90
+ # cleanup un thread daemon.
91
  - name: Run tests
92
  # Sur Python 3.13, on continue malgré une erreur pour ne pas bloquer
93
  # le merge pendant la fenêtre informationnelle de 6 mois (m-8).
94
  continue-on-error: ${{ matrix.python-version == '3.13' }}
95
+ timeout-minutes: 15
96
  shell: bash
97
  run: |
98
  pytest tests/ -q --tb=short --no-header \
 
103
  PYTHONUTF8: "1"
104
 
105
  # ── Couverture ──────────────────────────────────────────────
106
+ # ``timeout-minutes: 5`` : codecov-action v4 a déjà bloqué la CI
107
+ # 50+ min en attendant un upload qui n'aboutissait pas.
108
+ # ``fail_ci_if_error: false`` : un échec d'upload n'invalide pas
109
+ # le run de tests qui vient de passer.
110
  - name: Upload coverage to Codecov
111
  if: runner.os == 'Linux' && matrix.python-version == '3.11' && env.CODECOV_TOKEN != ''
112
+ timeout-minutes: 5
113
  uses: codecov/codecov-action@v4
114
  with:
115
  token: ${{ secrets.CODECOV_TOKEN }}
116
  files: coverage.xml
117
  flags: unittests
118
  name: picarones-coverage
119
+ fail_ci_if_error: false
120
  env:
121
  CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
122
 
pyproject.toml CHANGED
@@ -156,9 +156,11 @@ testpaths = ["tests"]
156
  # Windows) — utilisé par les tests CLI E2E qui résolvent leurs mock
157
  # adapters via dotted path (``importlib.import_module("tests.fixtures.…")``).
158
  pythonpath = ["."]
159
- # Exclusion par défaut : marker network non sélectionné. Override via
160
- # ``pytest -m network`` (CI réseau-friendly) ou ``pytest -m ""``.
161
- addopts = "-v --tb=short -m 'not network'"
 
 
162
  # Sprint A1 (M-15) : aucun test individuel ne doit dépasser 5 minutes.
163
  # Mode "thread" car certains tests utilisent ProcessPoolExecutor qui est
164
  # incompatible avec le timeout en mode "signal" sur certaines plateformes.
@@ -176,6 +178,7 @@ timeout_method = "thread"
176
  markers = [
177
  "slow: tests longs (corpus de référence, intégration cloud) ; non bloquants en dev local",
178
  "network: tests qui hit le réseau réel ; exclus par défaut",
 
179
  ]
180
 
181
  # ──────────────────────────────────────────────────────────────────
 
156
  # Windows) — utilisé par les tests CLI E2E qui résolvent leurs mock
157
  # adapters via dotted path (``importlib.import_module("tests.fixtures.…")``).
158
  pythonpath = ["."]
159
+ # Exclusion par défaut : markers ``network`` et ``live`` non
160
+ # sélectionnés. Override en local via ``pytest -m network`` ou
161
+ # ``pytest -m live`` (avec env vars / binaires correctement
162
+ # configurés). ``-m ""`` pour tout exécuter.
163
+ addopts = "-v --tb=short -m 'not network and not live'"
164
  # Sprint A1 (M-15) : aucun test individuel ne doit dépasser 5 minutes.
165
  # Mode "thread" car certains tests utilisent ProcessPoolExecutor qui est
166
  # incompatible avec le timeout en mode "signal" sur certaines plateformes.
 
178
  markers = [
179
  "slow: tests longs (corpus de référence, intégration cloud) ; non bloquants en dev local",
180
  "network: tests qui hit le réseau réel ; exclus par défaut",
181
+ "live: tests d'intégration contre vraie API/binaire (Tesseract, Anthropic, OpenAI, Mistral) ; exclus par défaut, opt-in en local via 'pytest -m live'",
182
  ]
183
 
184
  # ──────────────────────────────────────────────────────────────────