Files
gitea-dashboard/docs/technical/decisions.md
sylvain a1f613f3d8 docs(v1.4.0): version plan and ADR
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>
2026-03-13 03:34:55 +01:00

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)
  • --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

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=all augmente 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-desc continue de fonctionner
  • --columns help fournit une aide contextuelle
  • Les deux flags combines s'appliquent cumulativement