Plan 2 phases : bugfix timeout + config YAML, puis vue milestones + colonnes. ADR-012 a ADR-015 couvrant degradation gracieuse, config.py, MilestoneData, et colonnes configurables. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
11 KiB
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)
--helpgenere 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_reposimportable 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
--healthest mutuellement exclusif avec le mode dashboard- Si d'autres modes alternatifs apparaissent, reconsiderer les sous-commandes
ADR-012 : Degradation gracieuse sur timeout dans _get_paginated (v1.4.0)
Date : 2026-03-13 Statut : accepte
Contexte : Un timeout reseau sur une page intermediaire de la pagination fait crasher tout le collecteur. Le retry existant (ADR-007/ADR-009) retente les requetes individuelles, mais apres epuisement des retries, l'exception remonte et les donnees des pages precedentes sont perdues.
Decision : Dans _get_paginated, catch les exceptions Timeout apres epuisement des retries uniquement pour les pages > 1. Retourner les donnees collectees jusque-la et emettre un warnings.warn(). Si la premiere page echoue, l'exception remonte normalement.
Consequences :
- Le dashboard affiche un resultat partiel plutot qu'un crash
- L'utilisateur est informe via un warning
- La premiere page echouee reste un crash clair
- Coherent avec le principe "Gestion gracieuse" de CLAUDE.md
ADR-013 : Nouveau module config.py pour la configuration YAML (v1.4.0)
Date : 2026-03-13 Statut : accepte
Contexte : L'issue #17 demande un fichier de configuration YAML. La logique (lecture fichier, resolution variables, merge de priorites) est distincte du parsing CLI.
Decision : Creer config.py comme 7eme module source. Nouvelle dependance PyYAML. Le principe "un module = une responsabilite" de ADR-002 reste respecte.
Consequences :
- Separation claire : cli.py parse les args, config.py resout la configuration
- Le module est testable independamment avec des fixtures YAML
- Premiere nouvelle dependance ajoutee au projet (PyYAML)
- Le merge de priorites (CLI > env > config > defaults) est centralise et testable
ADR-014 : Dataclass MilestoneData pour la vue milestones (v1.4.0)
Date : 2026-03-13 Statut : accepte
Contexte : La vue --milestones collecte des milestones de tous les repos. Les milestones de l'API sont des dicts bruts sans reference au repo parent.
Decision : Creer un dataclass MilestoneData dans collector.py. Collecte avec state=all pour afficher l'historique complet.
Consequences :
- Coherent avec RepoData : donnees normalisees et documentees
- Le calcul du pourcentage de progression est centralise dans le collecteur
state=allaugmente les appels API mais donne une vue complete
ADR-015 : Colonnes configurables par inclusion/exclusion (v1.4.0)
Date : 2026-03-13 Statut : accepte
Contexte : L'issue #19 demande de pouvoir choisir les colonnes affichees. L'approche actuelle (--no-desc) est ad hoc. Un systeme generique est maintenant justifie par le besoin.
Decision : Ajouter --columns avec syntaxe a virgules. Support inclusion directe et exclusion par prefixe -. --no-desc reste fonctionnel comme alias.
Consequences :
- Remplace l'approche YAGNI de v1.3.0 (maintenant justifie)
- Retrocompatible :
--no-desccontinue de fonctionner --columns helpfournit une aide contextuelle- Les deux flags combines s'appliquent cumulativement