Picarones / Makefile
Claude
ci(compose): validate base+prod merge in CI and freeze invariants
d349d11 unverified
# Makefile β€” Picarones
# Usage : make <cible>
# Cibles principales : install, test, demo, serve, build, build-exe, docker-build, clean
.PHONY: all install install-dev install-all test test-cov lint demo serve \
build build-exe docker-build docker-run docker-compose-up clean help
PYTHON := python3
PIP := pip
VENV := .venv
VENV_BIN := $(VENV)/bin
PICARONES := $(VENV_BIN)/picarones
PYTEST := $(VENV_BIN)/pytest
PACKAGE := picarones
# Couleurs
BOLD := \033[1m
GREEN := \033[32m
CYAN := \033[36m
RESET := \033[0m
# ──────────────────────────────────────────────────────────────────
# Aide
# ──────────────────────────────────────────────────────────────────
help: ## Affiche cette aide
@echo ""
@echo "$(BOLD)Picarones β€” Commandes disponibles$(RESET)"
@echo ""
@grep -E '^[a-zA-Z_-]+:.*## ' $(MAKEFILE_LIST) \
| sort \
| awk 'BEGIN {FS = ":.*## "}; {printf " $(CYAN)%-18s$(RESET) %s\n", $$1, $$2}'
@echo ""
all: install test ## Installer et tester
# ──────────────────────────────────────────────────────────────────
# Installation
# ──────────────────────────────────────────────────────────────────
$(VENV):
$(PYTHON) -m venv $(VENV)
install: $(VENV) ## Installe Picarones en mode Γ©ditable (dΓ©pendances de base)
$(VENV_BIN)/pip install --upgrade pip
$(VENV_BIN)/pip install -e .
@echo "$(GREEN)βœ“ Installation de base terminΓ©e$(RESET)"
@echo " Activez l'environnement : source $(VENV)/bin/activate"
install-dev: $(VENV) ## Installe avec les dΓ©pendances de dΓ©veloppement (tests, lint)
$(VENV_BIN)/pip install --upgrade pip
$(VENV_BIN)/pip install -e ".[dev]"
@echo "$(GREEN)βœ“ Installation dev terminΓ©e$(RESET)"
install-web: $(VENV) ## Installe avec l'interface web (FastAPI + uvicorn)
$(VENV_BIN)/pip install --upgrade pip
$(VENV_BIN)/pip install -e ".[web,dev]"
@echo "$(GREEN)βœ“ Installation web terminΓ©e$(RESET)"
install-all: $(VENV) ## Installe avec tous les extras (web, HuggingFace, dev)
$(VENV_BIN)/pip install --upgrade pip
$(VENV_BIN)/pip install -e ".[web,hf,dev]"
@echo "$(GREEN)βœ“ Installation complΓ¨te terminΓ©e$(RESET)"
# ──────────────────────────────────────────────────────────────────
# Tests
# ──────────────────────────────────────────────────────────────────
test: ## Lance la suite de tests complète
$(PYTEST) tests/ -q --tb=short
@echo "$(GREEN)βœ“ Tests terminΓ©s$(RESET)"
test-cov: ## Tests avec rapport de couverture HTML
$(PYTEST) tests/ --cov=$(PACKAGE) --cov-report=html --cov-report=term-missing -q
@echo "$(GREEN)βœ“ Rapport de couverture : htmlcov/index.html$(RESET)"
test-fast: ## Tests rapides uniquement (exclut les tests lents)
$(PYTEST) tests/ -q --tb=short -x
test-sprint9: ## Tests Sprint 9 uniquement
$(PYTEST) tests/integration/test_packaging.py -v
# ──────────────────────────────────────────────────────────────────
# QualitΓ© du code
# ──────────────────────────────────────────────────────────────────
lint: ## VΓ©rifie le style du code (configuration lue depuis pyproject.toml)
@# La config ruff vit dans [tool.ruff] de pyproject.toml : cette cible,
@# le job CI et une invocation directe `ruff check` produisent le mΓͺme
@# rΓ©sultat. Lancez avant tout push pour Γ©viter un Γ©chec en PR.
@if command -v ruff > /dev/null 2>&1; then \
ruff check $(PACKAGE)/ tests/; \
elif $(VENV_BIN)/python -m ruff --version > /dev/null 2>&1; then \
$(VENV_BIN)/python -m ruff check $(PACKAGE)/ tests/; \
else \
echo "ruff non installΓ© β€” installation : pip install ruff"; \
exit 1; \
fi
doc-check: ## Audit de cohΓ©rence README/SPECS/CHANGELOG (Sprint A2)
@# Vérifie que la documentation reflète le code réel : moteurs annoncés
@# ont un adapter, commandes CLI listΓ©es existent, endpoints API
@# documentΓ©s sont exposΓ©s, compteur de tests Γ  jour, sprints
@# rΓ©fΓ©rencΓ©s dans CHANGELOG rΓ©solvent. Voir docs/developer/doc-consistency.md.
@# Fallback Python système si .venv/ absent (pattern aligné avec ``lint``).
@if [ -x $(VENV_BIN)/python ]; then \
$(VENV_BIN)/python -m pytest tests/docs/ -q --tb=short --no-header; \
else \
$(PYTHON) -m pytest tests/docs/ -q --tb=short --no-header; \
fi
sync-counters: ## Régénère README/CLAUDE.md avec les compteurs réels (script gen_readme_tables.py)
@if [ -x $(VENV_BIN)/python ]; then \
$(VENV_BIN)/python scripts/gen_readme_tables.py; \
else \
$(PYTHON) scripts/gen_readme_tables.py; \
fi
sync-counters-check: ## CI : Γ©choue si README/CLAUDE.md divergent du code (compteurs tests + tables)
@if [ -x $(VENV_BIN)/python ]; then \
$(VENV_BIN)/python scripts/gen_readme_tables.py --check; \
else \
$(PYTHON) scripts/gen_readme_tables.py --check; \
fi
docs: ## Construit le site mkdocs en mode strict (Γ©choue sur les warnings)
@if [ -x $(VENV_BIN)/python ]; then \
$(VENV_BIN)/python -m mkdocs build --strict; \
else \
$(PYTHON) -m mkdocs build --strict; \
fi
docs-serve: ## Lance mkdocs en mode dev (http://localhost:8000)
@if [ -x $(VENV_BIN)/python ]; then \
$(VENV_BIN)/python -m mkdocs serve; \
else \
$(PYTHON) -m mkdocs serve; \
fi
typecheck: ## VΓ©rification de types avec mypy (si installΓ©)
@$(VENV_BIN)/python -m mypy $(PACKAGE)/ --ignore-missing-imports --no-strict-optional 2>/dev/null \
|| echo "mypy non installΓ© : pip install mypy"
# ──────────────────────────────────────────────────────────────────
# DΓ©monstration
# ──────────────────────────────────────────────────────────────────
demo: ## Génère un rapport de démonstration complet (rapport_demo.html)
$(PICARONES) demo --docs 12 --output rapport_demo.html \
--with-history --with-robustness
@echo "$(GREEN)βœ“ Rapport demo : rapport_demo.html$(RESET)"
@echo " Ouvrez : file://$(PWD)/rapport_demo.html"
demo-json: ## Génère rapport demo + export JSON
$(PICARONES) demo --docs 12 --output rapport_demo.html --json-output resultats_demo.json
@echo "$(GREEN)βœ“ Rapport : rapport_demo.html | JSON : resultats_demo.json$(RESET)"
demo-history: ## DΓ©monstration du suivi longitudinal
$(PICARONES) history --demo --regression
demo-robustness: ## DΓ©monstration de l'analyse de robustesse
mkdir -p /tmp/picarones_demo_corpus
$(PICARONES) robustness \
--corpus /tmp/picarones_demo_corpus \
--engine tesseract \
--demo \
--degradations noise,blur,rotation
# ──────────────────────────────────────────────────────────────────
# Serveur web
# ──────────────────────────────────────────────────────────────────
serve: ## Lance l'interface web locale (http://localhost:8000)
$(PICARONES) serve --host 127.0.0.1 --port 8000
serve-public: ## Lance le serveur en mode public (0.0.0.0:8000)
$(PICARONES) serve --host 0.0.0.0 --port 8000
serve-dev: ## Lance le serveur en mode dΓ©veloppement (rechargement automatique)
$(PICARONES) serve --reload --verbose
# ──────────────────────────────────────────────────────────────────
# Build & packaging
# ──────────────────────────────────────────────────────────────────
build: ## Construit la distribution Python (wheel + sdist)
$(VENV_BIN)/pip install --upgrade build
$(VENV_BIN)/python -m build
@echo "$(GREEN)βœ“ Distribution : dist/$(RESET)"
build-exe: ## Génère un exécutable standalone avec PyInstaller
@echo "$(CYAN)Construction de l'exΓ©cutable standalone…$(RESET)"
$(VENV_BIN)/pip install pyinstaller
$(VENV_BIN)/pyinstaller picarones.spec --noconfirm
@echo "$(GREEN)βœ“ ExΓ©cutable : dist/picarones/$(RESET)"
build-exe-onefile: ## Génère un exécutable unique (plus lent au démarrage)
$(VENV_BIN)/pip install pyinstaller
$(VENV_BIN)/pyinstaller picarones.spec --noconfirm --onefile
@echo "$(GREEN)βœ“ ExΓ©cutable : dist/picarones$(RESET)"
# ──────────────────────────────────────────────────────────────────
# Docker
# ──────────────────────────────────────────────────────────────────
# Version dΓ©rivΓ©e du package (setuptools-scm), repli ``dev`` si le
# paquet n'est pas installΓ© sur la machine de build. Plus de tag
# ``1.0.0`` figΓ© qui ment sur la version rΓ©elle.
DOCKER_VERSION := $(shell python -c "import importlib.metadata as m; print(m.version('picarones'))" 2>/dev/null || echo dev)
docker-build: ## Construit l'image Docker Picarones
docker build --build-arg PICARONES_VERSION=$(DOCKER_VERSION) \
-t picarones:latest -t picarones:$(DOCKER_VERSION) .
@echo "$(GREEN)βœ“ Image Docker : picarones:latest (= picarones:$(DOCKER_VERSION))$(RESET)"
docker-run: ## Lance Picarones dans Docker (http://localhost:7860)
docker run --rm -p 7860:7860 \
-e OPENAI_API_KEY="$${OPENAI_API_KEY:-}" \
-e ANTHROPIC_API_KEY="$${ANTHROPIC_API_KEY:-}" \
-e MISTRAL_API_KEY="$${MISTRAL_API_KEY:-}" \
-v "$(PWD)/corpus:/app/corpus:ro" \
picarones:latest
docker-compose-up: ## Lance Picarones + Ollama avec Docker Compose
docker compose up -d
@echo "$(GREEN)βœ“ Services dΓ©marrΓ©s$(RESET)"
@echo " Picarones : http://localhost:7860"
@echo " Ollama : http://localhost:11434"
docker-compose-down: ## ArrΓͺte les services Docker Compose
docker compose down
docker-compose-logs: ## Affiche les logs Docker Compose
docker compose logs -f picarones
compose-check: ## Valide que docker-compose.yml seul + le merge avec prod.yml sont syntaxiquement corrects
@# Sans cette validation, une incohΓ©rence dans le merge (variable
@# non hΓ©ritΓ©e, port surchargΓ© mal, conflit de services) ne se
@# voit qu'au dΓ©ploiement. Le secret CSRF est forcΓ© sur une
@# valeur factice pour permettre l'Γ©valuation YAML (prod.yml
@# exige ``${PICARONES_CSRF_SECRET:?...}``) β€” la valeur n'est
@# jamais utilisΓ©e.
@docker compose -f docker-compose.yml config > /dev/null
@PICARONES_CSRF_SECRET=compose-check-not-a-real-secret \
docker compose -f docker-compose.yml -f docker-compose.prod.yml config > /dev/null
@echo "$(GREEN)βœ“ compose merge (local + prod) syntaxiquement valide$(RESET)"
# ──────────────────────────────────────────────────────────────────
# Nettoyage
# ──────────────────────────────────────────────────────────────────
clean: ## Supprime les fichiers gΓ©nΓ©rΓ©s (cache, build, dist)
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
find . -type f -name "*.pyc" -delete 2>/dev/null || true
find . -type f -name "*.pyo" -delete 2>/dev/null || true
find . -type d -name "*.egg-info" -exec rm -rf {} + 2>/dev/null || true
rm -rf dist/ build/ .eggs/ htmlcov/ .coverage .pytest_cache/
@echo "$(GREEN)βœ“ Nettoyage terminΓ©$(RESET)"
clean-all: clean ## Supprime aussi l'environnement virtuel
rm -rf $(VENV)/
@echo "$(GREEN)βœ“ Environnement virtuel supprimΓ©$(RESET)"
# ──────────────────────────────────────────────────────────────────
# Utilitaires
# ──────────────────────────────────────────────────────────────────
info: ## Affiche les informations de version Picarones
$(PICARONES) info
engines: ## Liste les moteurs OCR disponibles
$(PICARONES) engines
history-demo: ## Affiche l'historique de dΓ©monstration
$(PICARONES) history --demo --regression
changelog: ## Affiche le CHANGELOG
@cat CHANGELOG.md | head -80
version: ## Affiche la version courante
@grep -m1 'version' pyproject.toml | awk '{print $$3}' | tr -d '"'