Spaces:
Sleeping
Centraliser la config ruff et rendre le lint bloquant (vrai fix)
Browse filesLes deux corrections précédentes (de4ff68 sur picarones/, b20a7dc sur
tests/) étaient du rafistolage — elles enlevaient les symptômes sans
traiter la cause. La cause réelle : **trois configurations ruff
divergentes** sur le projet, et **aucun filet local** avant le push.
État de départ problématique :
1. `.github/workflows/ci.yml` : appelait ruff avec des flags
`--select=E,W,F --ignore=E501,E402`, ET avait `continue-on-error:
true` (donc le lint était décoratif).
2. `Makefile::lint` : appelait `ruff check $(PACKAGE)/ tests/` SANS
flags — prenait la config ruff par défaut, DIFFÉRENTE du CI.
3. `pyproject.toml` : aucune section `[tool.ruff]`.
Résultat : `make lint` pouvait dire "OK" localement pendant que le CI
trouvait des erreurs. Et même si le CI trouvait des erreurs, le lint
ne bloquait pas la PR. D'où l'accumulation de 10 erreurs F401/E741 sur
les sprints 16-21, découvertes par vagues successives.
Correctif de fond :
- `pyproject.toml` : ajout de `[tool.ruff]` (line-length=100,
target=py311) et `[tool.ruff.lint]` (select=E,W,F, ignore=E501,E402).
Les règles retenues sont celles du CI d'origine — aucune rupture de
contrat, juste une centralisation.
- `.github/workflows/ci.yml` : la commande passe de
`ruff check picarones/ --select=... && ruff check tests/ --select=...`
à simplement `ruff check picarones/ tests/`. La config vient du
pyproject.toml. **Suppression de `continue-on-error: true`** — le
lint devient bloquant, cohérent avec les jobs tests/build/demo.
- `Makefile::lint` : passe à `ruff check $(PACKAGE)/ tests/` (identique
au CI). Le fallback flake8 est retiré (ruff est la seule voie
officielle, moins de chemins à maintenir). Si ruff n'est pas installé,
on sort avec code 1 et un message explicite au lieu d'un silence qui
laissait croire que c'était vert.
- `CLAUDE.md` : nouvelle règle explicite — `make lint` avant tout push,
le job `lint` du CI est bloquant par design.
Vérifications :
- `ruff check picarones/ tests/` : All checks passed!
- `make lint` : All checks passed!
- `ruff check --show-settings` : line_length=100, py311, select
correctement chargé depuis pyproject.toml
- Suite complète : 1241 passed, 2 skipped (inchangée)
Conséquence : toute future erreur lint sera attrapée localement avant
le push. Plus d'allers-retours CI pour des imports oubliés.
https://claude.ai/code/session_0162FdNNJyNvBuYzkgtsr9VB
- .github/workflows/ci.yml +5 -5
- CLAUDE.md +5 -0
- Makefile +6 -4
- pyproject.toml +13 -0
|
@@ -175,12 +175,14 @@ jobs:
|
|
| 175 |
retention-days: 30
|
| 176 |
|
| 177 |
# ──────────────────────────────────────────────────────────────────
|
| 178 |
-
# Job 4 : Vérification de la qualité du code
|
|
|
|
|
|
|
|
|
|
| 179 |
# ──────────────────────────────────────────────────────────────────
|
| 180 |
lint:
|
| 181 |
name: Code quality
|
| 182 |
runs-on: ubuntu-latest
|
| 183 |
-
continue-on-error: true # Ne bloque pas le CI si le lint échoue
|
| 184 |
|
| 185 |
steps:
|
| 186 |
- name: Checkout
|
|
@@ -196,9 +198,7 @@ jobs:
|
|
| 196 |
run: pip install ruff
|
| 197 |
|
| 198 |
- name: Run ruff
|
| 199 |
-
run:
|
| 200 |
-
ruff check picarones/ --select=E,W,F --ignore=E501,E402
|
| 201 |
-
ruff check tests/ --select=E,W,F --ignore=E501,E402
|
| 202 |
|
| 203 |
# ──────────────────────────────────────────────────────────────────
|
| 204 |
# Job 5 : CI/CD — Détection de régression CER (optionnel)
|
|
|
|
| 175 |
retention-days: 30
|
| 176 |
|
| 177 |
# ──────────────────────────────────────────────────────────────────
|
| 178 |
+
# Job 4 : Vérification de la qualité du code
|
| 179 |
+
# La config ruff est centralisée dans pyproject.toml ([tool.ruff]),
|
| 180 |
+
# donc CI, Makefile et invocations locales produisent exactement les
|
| 181 |
+
# mêmes résultats — pas de divergence possible entre flags.
|
| 182 |
# ──────────────────────────────────────────────────────────────────
|
| 183 |
lint:
|
| 184 |
name: Code quality
|
| 185 |
runs-on: ubuntu-latest
|
|
|
|
| 186 |
|
| 187 |
steps:
|
| 188 |
- name: Checkout
|
|
|
|
| 198 |
run: pip install ruff
|
| 199 |
|
| 200 |
- name: Run ruff
|
| 201 |
+
run: ruff check picarones/ tests/
|
|
|
|
|
|
|
| 202 |
|
| 203 |
# ──────────────────────────────────────────────────────────────────
|
| 204 |
# Job 5 : CI/CD — Détection de régression CER (optionnel)
|
|
@@ -128,6 +128,11 @@ correspondants (`test_sprint15_llm_pipeline_bugs.py`, `test_sprint8_escriptorium
|
|
| 128 |
`logger.warning("[module] fonctionnalité dégradée : %s", e)`.
|
| 129 |
- **Toujours utiliser `logger.warning` avec message explicite** quand une fonctionnalité optionnelle
|
| 130 |
échoue (confusion, taxonomy, structure, image_quality, etc.).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
- **Les profils de normalisation** sont dans `picarones/core/normalization.py` — l'endpoint
|
| 132 |
`/api/normalization/profiles` doit les lire dynamiquement depuis ce fichier, pas depuis une
|
| 133 |
liste statique.
|
|
|
|
| 128 |
`logger.warning("[module] fonctionnalité dégradée : %s", e)`.
|
| 129 |
- **Toujours utiliser `logger.warning` avec message explicite** quand une fonctionnalité optionnelle
|
| 130 |
échoue (confusion, taxonomy, structure, image_quality, etc.).
|
| 131 |
+
- **Avant tout push, lancer `make lint`** (ou `ruff check picarones/ tests/`).
|
| 132 |
+
La config est centralisée dans `pyproject.toml` sous `[tool.ruff]`, donc
|
| 133 |
+
CI, Makefile et invocation directe produisent le même résultat. Le job
|
| 134 |
+
`lint` du CI est bloquant — un F401 (import inutilisé) ou un E741
|
| 135 |
+
(variable ambiguë) fait échouer la PR, par design.
|
| 136 |
- **Les profils de normalisation** sont dans `picarones/core/normalization.py` — l'endpoint
|
| 137 |
`/api/normalization/profiles` doit les lire dynamiquement depuis ce fichier, pas depuis une
|
| 138 |
liste statique.
|
|
@@ -84,15 +84,17 @@ test-sprint9: ## Tests Sprint 9 uniquement
|
|
| 84 |
# Qualité du code
|
| 85 |
# ──────────────────────────────────────────────────────────────────
|
| 86 |
|
| 87 |
-
lint: ## Vérifie le style du code (
|
|
|
|
|
|
|
|
|
|
| 88 |
@if command -v ruff > /dev/null 2>&1; then \
|
| 89 |
ruff check $(PACKAGE)/ tests/; \
|
| 90 |
elif $(VENV_BIN)/python -m ruff --version > /dev/null 2>&1; then \
|
| 91 |
$(VENV_BIN)/python -m ruff check $(PACKAGE)/ tests/; \
|
| 92 |
-
elif command -v flake8 > /dev/null 2>&1; then \
|
| 93 |
-
flake8 $(PACKAGE)/ tests/ --max-line-length=100 --ignore=E501,W503; \
|
| 94 |
else \
|
| 95 |
-
echo "
|
|
|
|
| 96 |
fi
|
| 97 |
|
| 98 |
typecheck: ## Vérification de types avec mypy (si installé)
|
|
|
|
| 84 |
# Qualité du code
|
| 85 |
# ──────────────────────────────────────────────────────────────────
|
| 86 |
|
| 87 |
+
lint: ## Vérifie le style du code (configuration lue depuis pyproject.toml)
|
| 88 |
+
@# La config ruff vit dans [tool.ruff] de pyproject.toml : cette cible,
|
| 89 |
+
@# le job CI et une invocation directe `ruff check` produisent le même
|
| 90 |
+
@# résultat. Lancez avant tout push pour éviter un échec en PR.
|
| 91 |
@if command -v ruff > /dev/null 2>&1; then \
|
| 92 |
ruff check $(PACKAGE)/ tests/; \
|
| 93 |
elif $(VENV_BIN)/python -m ruff --version > /dev/null 2>&1; then \
|
| 94 |
$(VENV_BIN)/python -m ruff check $(PACKAGE)/ tests/; \
|
|
|
|
|
|
|
| 95 |
else \
|
| 96 |
+
echo "ruff non installé — installation : pip install ruff"; \
|
| 97 |
+
exit 1; \
|
| 98 |
fi
|
| 99 |
|
| 100 |
typecheck: ## Vérification de types avec mypy (si installé)
|
|
@@ -94,3 +94,16 @@ picarones = [
|
|
| 94 |
[tool.pytest.ini_options]
|
| 95 |
testpaths = ["tests"]
|
| 96 |
addopts = "-v --tb=short"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
[tool.pytest.ini_options]
|
| 95 |
testpaths = ["tests"]
|
| 96 |
addopts = "-v --tb=short"
|
| 97 |
+
|
| 98 |
+
[tool.ruff]
|
| 99 |
+
# Configuration centralisée pour que `ruff check`, `make lint` et le job CI
|
| 100 |
+
# produisent exactement les mêmes résultats sans flags en ligne de commande.
|
| 101 |
+
line-length = 100
|
| 102 |
+
target-version = "py311"
|
| 103 |
+
|
| 104 |
+
[tool.ruff.lint]
|
| 105 |
+
# E/W = pycodestyle, F = pyflakes. On conserve les mêmes règles que le CI
|
| 106 |
+
# d'origine (avant Sprint 22), qui excluait les lignes longues (E501) et les
|
| 107 |
+
# imports non-top (E402, parfois utiles pour imports conditionnels).
|
| 108 |
+
select = ["E", "W", "F"]
|
| 109 |
+
ignore = ["E501", "E402"]
|