# Makefile — Picarones # Usage : make # 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 '"'