Files
gitea-dashboard/docs/technical/decisions.md
sylvain 9783389bfb chore(workflow): complete step 6 (plan v1.3.0), start step 7
3 phases: corrections/robustesse (#11,#12), tests edge (#13), features (#14,#15)
ADR-009 (retry 429), ADR-010 (sanitize JSON), ADR-011 (--health flag)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 19:07:57 +01:00

170 lines
7.7 KiB
Markdown

<!-- Type: reference (Diataxis). Style: factuel, format ADR Nygard (Contexte/Decision/Consequences). Jamais supprimer un ADR. -->
# Architecture Decision Records — gitea-dashboard
## ADR-001 : Stack Python + requests + rich
**Date** : 2026-03-10
**Statut** : accepte
**Contexte** : Besoin d'un outil CLI de dashboard pour Gitea. Choix du langage et des librairies.
**Decision** : Python avec requests pour les appels API et rich pour le formatage terminal.
**Consequences** :
- Stack simple et maitrisee par l'utilisateur
- Pas de framework CLI lourd (argparse suffit si besoin)
- rich offre des tableaux et couleurs sans configuration complexe
- Dependance a requests (pas de client async, acceptable pour un affichage unique)
## ADR-002 : 4 modules maximum (client, collector, display, cli)
**Date** : 2026-03-10
**Statut** : accepte
**Contexte** : Definir la granularite des modules Python pour un projet simple (dashboard CLI).
**Decision** : Limiter a 4 modules avec des responsabilites claires : `client.py` (API), `collector.py` (agregation), `display.py` (formatage), `cli.py` (entree).
**Consequences** :
- Separation des responsabilites sans over-engineering
- Chaque module est testable independamment
- Un fichier = une responsabilite = un jeu de tests
- Si le projet grandit, chaque module peut evoluer independamment
## ADR-003 : Pas de parallelisation en v1
**Date** : 2026-03-10
**Statut** : accepte
**Contexte** : La collecte de donnees necessite N appels par repo (release + milestones). La parallelisation (ThreadPoolExecutor) est possible mais ajoute de la complexite.
**Decision** : V1 en sequentiel. La parallelisation est differee a v1.1+.
**Consequences** :
- Code plus simple a ecrire, deboguer et tester
- Temps de reponse acceptable pour < 20 repos (estimee < 10s)
- Pas de problemes de concurrence
- Facile a ajouter plus tard sans changer les interfaces (le collecteur est le seul point d'appel)
## ADR-004 : Argparse pour le parsing CLI (v1.1.0)
**Date** : 2026-03-11
**Statut** : accepte
**Contexte** : La v1.1.0 introduit des options CLI (`--repo`, `--exclude`). Un parser d'arguments est necessaire. Trois options : argparse (stdlib), click, typer.
**Decision** : Utiliser argparse (stdlib Python). Pas de dependance externe pour le parsing CLI.
**Consequences** :
- Zero nouvelle dependance (argparse est dans la stdlib)
- Coherent avec ADR-001 (stack simple, pas de framework lourd)
- `--help` genere automatiquement
- Suffisant pour des options simples (repeatable flags)
- Si les options deviennent complexes (sous-commandes, autocompletion), migration vers click/typer sera possible
## ADR-005 : Filtrage par sous-chaine dans le collecteur (v1.1.0)
**Date** : 2026-03-11
**Statut** : accepte
**Contexte** : Le filtrage des repos peut etre fait dans le CLI (apres collecte) ou dans le collecteur (pendant la collecte). Le filtrage par regex est plus puissant mais plus complexe que la sous-chaine.
**Decision** : Filtrage par sous-chaine (insensible a la casse) dans `collect_all()`. Ordre : include d'abord, exclude ensuite.
**Consequences** :
- Le collecteur reste le seul responsable de "quels repos collecter"
- Le CLI reste un simple orchestrateur (ADR-002 respecte)
- Retrocompatible : les nouveaux parametres ont des valeurs par defaut None
- Sous-chaine est intuitive pour l'utilisateur (pas besoin de connaitre les regex)
- Le filtrage est post-fetch car l'API Gitea ne supporte pas le filtre par nom
## ADR-006 : Ajout du module exporter.py (v1.2.0)
**Date** : 2026-03-12
**Statut** : accepte
**Contexte** : L'export JSON est une nouvelle responsabilite. L'ajouter a `display.py` melangerait serialisation structuree et formatage Rich. ADR-002 limitait a 4 modules pour v1.0.0.
**Decision** : Creer `exporter.py` pour la serialisation des donnees (JSON, et futurs formats). Le projet passe a 5 modules.
**Consequences** :
- Separation claire : `display.py` = rendu terminal, `exporter.py` = serialisation donnees
- ADR-002 est relaxe (4 -> 5 modules), pas invalide (le principe "un module = une responsabilite" reste)
- Le module est independant de Rich (pas de dependance supplementaire)
- Extensible pour CSV/YAML sans modifier l'existant
## ADR-007 : Retry simple plutot que urllib3.Retry (v1.2.0)
**Date** : 2026-03-12
**Statut** : accepte
**Contexte** : Les timeouts API causent un crash. Deux strategies : configurer `HTTPAdapter` avec `urllib3.Retry`, ou implementer un retry manuel dans le client.
**Decision** : Retry manuel (boucle + time.sleep) dans `GiteaClient._get_with_retry()`. Maximum 2 retries, backoff lineaire (1s, 2s).
**Consequences** :
- Code explicite et testable (mock de `time.sleep`)
- Pas de dependance sur l'API interne de urllib3
- Applicable a tous les appels HTTP du client de maniere uniforme
- Limite : pas d'exponential backoff (acceptable pour un outil CLI local)
## ADR-008 : Tri dans display.py, pas dans collector.py (v1.2.0)
**Date** : 2026-03-12
**Statut** : accepte
**Contexte** : Le tri des repos peut etre place dans le collecteur (donnees ordonnees) ou dans l'affichage (presentation ordonnee).
**Decision** : Le tri est dans `display.py`. Le collecteur retourne les donnees dans l'ordre de l'API. L'affichage decide de l'ordre de presentation.
**Consequences** :
- Le collecteur reste un simple agregateur de donnees (SRP)
- Le tri est teste independamment de la collecte
- L'export JSON peut aussi appliquer le tri (via `_sort_repos` importable depuis display)
- Le critere de tri par defaut ("name") garantit un affichage stable entre les executions
## ADR-009 : Retry HTTP 429 avec Retry-After dans _get_with_retry (v1.3.0)
**Date** : 2026-03-12
**Statut** : accepte
**Contexte** : Le retry dans `_get_with_retry` ne gere que `requests.Timeout`. Un HTTP 429 (rate limiting) retourne une reponse HTTP, pas une exception Timeout. Le retry ne se declenche donc pas sur rate limiting.
**Decision** : Etendre `_get_with_retry` pour intercepter les reponses HTTP 429. Respecter le header `Retry-After` (en secondes) si present, sinon utiliser le backoff lineaire standard. Apres epuisement des retries, lever `requests.HTTPError`.
**Consequences** :
- La logique de retry reste centralisee dans une seule methode (coherent avec ADR-007)
- Le header Retry-After est un standard HTTP (RFC 7231), le respecter evite les retries inutiles
- La boucle gere desormais 2 cas (Timeout + 429), complexite accrue mais testable
- Pas de changement d'interface publique
## ADR-010 : Sanitisation des caracteres de controle dans exporter.py (v1.3.0)
**Date** : 2026-03-12
**Statut** : accepte
**Contexte** : L'export JSON peut contenir des caracteres de controle ASCII (0x00-0x1F) provenant des descriptions de repos. Ces caracteres sont invalides en JSON (RFC 8259) et causent des erreurs avec `python3 -m json.tool`.
**Decision** : Sanitiser les champs texte dans `repos_to_dicts()` avant serialisation. Supprimer les caracteres de controle sauf `\n`, `\r` et `\t` (qui sont echappes nativement par `json.dumps`).
**Consequences** :
- La sanitisation est au point de sortie (exporter), pas dans le collecteur
- Les donnees dans RepoData restent brutes (pas de perte pour le rendu Rich)
- Approche defensive contre les donnees inattendues de l'API Gitea
## ADR-011 : --health comme flag optionnel, pas sous-commande (v1.3.0)
**Date** : 2026-03-12
**Statut** : accepte
**Contexte** : L'option `--health` est un mode alternatif au dashboard. Deux approches : flag optionnel ou sous-commande.
**Decision** : Utiliser un flag `--health` dans argparse. Pas de sous-commandes.
**Consequences** :
- Coherent avec ADR-004 (argparse simple)
- Un seul niveau d'arguments
- `--health` est mutuellement exclusif avec le mode dashboard
- Si d'autres modes alternatifs apparaissent, reconsiderer les sous-commandes