docs(v1.0.0): version plan and ADR

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
sylvain
2026-03-10 18:43:29 +01:00
parent 0bd64d64a9
commit e757c35767
3 changed files with 465 additions and 1 deletions

294
docs/plans/v1.0.0-plan.md Normal file
View File

@@ -0,0 +1,294 @@
<!-- Type: reference (Diataxis). Style: factuel, structure par phases, actionnable par le builder. -->
# Plan de version v1.0.0 — gitea-dashboard
## Objectif
Livrer un dashboard CLI fonctionnel qui affiche en une commande l'etat de tous les repos Gitea : issues ouvertes, derniere release, milestones. Premiere version utilisable.
## Track
**Major initial** (v1.0.0, nouveau projet) : 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10 -> 11 -> (12) -> 13
---
## Budget de scope
| Critere | Valeur |
|---------|--------|
| Max fichiers par phase | 4 |
| Total fichiers estimes | 8 (4 modules + 4 tests) |
### Inclus
- Client API Gitea avec auth token et pagination
- Collecte des donnees : repos, issues (hors PRs), derniere release, milestones ouvertes
- Affichage Rich : tableau repos + section milestones + indicateurs visuels
- Point d'entree CLI avec configuration par variables d'environnement
- Gestion des cas limites (repos sans release, sans milestone, forks, archives)
### Exclus
- Interface web ou GUI
- Mode watch / rafraichissement automatique
- Filtrage par owner/organisation
- Modification de donnees Gitea (lecture seule)
- Notifications ou alertes
- Framework CLI (argparse, click, typer)
### Differe (v1.1+)
- Parallelisation des appels API (ThreadPoolExecutor)
- Filtrage/tri des repos (par nom, activite, owner)
- Cache des reponses API
- Options CLI (--format, --filter, --sort)
---
## Etapes skippees
| Etape | Nom | Raison |
|-------|-----|--------|
| 12 | Deploy | Outil CLI local, pas de deploiement serveur |
---
## Phase 1 : Client API et collecte de donnees
**Goal** : Disposer d'un client API Gitea fonctionnel et d'un collecteur qui agrege les donnees de tous les repos.
**Issues Gitea** : fixes #1, fixes #2
### Fichiers
| Action | Fichier | Modifications | Cross-references |
|--------|---------|---------------|------------------|
| Create | `src/gitea_dashboard/client.py` | Client API Gitea : auth, requetes GET, pagination | `collector.py` (consommateur) |
| Create | `src/gitea_dashboard/collector.py` | Collecte et agregation des donnees repos | `client.py` (dependance), `display.py` (producteur de donnees) |
| Create | `tests/test_client.py` | Tests unitaires du client API avec mocks | `client.py` |
| Create | `tests/test_collector.py` | Tests unitaires du collecteur avec mocks | `collector.py` |
### Interfaces
#### client.py
```python
class GiteaClient:
"""Client API Gitea en lecture seule."""
def __init__(self, base_url: str, token: str) -> None:
"""Initialise le client avec l'URL de base et le token API."""
def get_repos(self) -> list[dict]:
"""Retourne tous les repos de l'utilisateur (pagination automatique).
Endpoint: GET /api/v1/user/repos
"""
def get_latest_release(self, owner: str, repo: str) -> dict | None:
"""Retourne la derniere release du repo, ou None si aucune.
Endpoint: GET /api/v1/repos/{owner}/{repo}/releases/latest
Gere HTTP 404 en retournant None.
"""
def get_milestones(self, owner: str, repo: str) -> list[dict]:
"""Retourne les milestones ouvertes du repo.
Endpoint: GET /api/v1/repos/{owner}/{repo}/milestones?state=open
"""
```
Methode privee interne pour la pagination :
```python
def _get_paginated(self, endpoint: str, params: dict | None = None) -> list[dict]:
"""Requete GET avec pagination automatique.
Boucle tant que len(page) == limit (50).
"""
```
#### collector.py
```python
@dataclass
class RepoData:
"""Donnees agregees d'un repo."""
name: str
full_name: str
description: str
open_issues: int # open_issues_count - open_pr_counter
is_fork: bool
is_archived: bool
is_mirror: bool
latest_release: dict | None # {tag_name, published_at} ou None
milestones: list[dict] # [{title, open_issues, closed_issues, due_on}]
def collect_all(client: GiteaClient) -> list[RepoData]:
"""Collecte les donnees de tous les repos.
Pour chaque repo : enrichit avec release et milestones.
"""
```
### Comportement attendu
1. `GiteaClient("http://192.168.0.106:3000", "token123")` cree un client
2. `client.get_repos()` retourne la liste complete (pagination transparente si > 50 repos)
3. `client.get_latest_release("admin", "mon-repo")` retourne `{"tag_name": "v1.0", "published_at": "..."}` ou `None`
4. `collect_all(client)` retourne une liste de `RepoData` pour chaque repo, meme ceux sans release ou milestone
### Tests
- `test_client.py` : mock de `requests.Session` pour tester auth header, pagination (1 page, 2 pages), 404 sur release
- `test_collector.py` : mock de `GiteaClient` pour tester l'agregation, les cas limites (repo sans release, sans milestone, fork, archive)
### Livrable
Le client peut interroger l'API Gitea et le collecteur produit une liste de `RepoData` prete pour l'affichage. Les tests passent.
---
## Phase 2 : Affichage Rich et point d'entree CLI
**Goal** : Afficher les donnees collectees dans un dashboard terminal lisible et fournir le point d'entree executable.
**Issues Gitea** : fixes #3, fixes #4
### Fichiers
| Action | Fichier | Modifications | Cross-references |
|--------|---------|---------------|------------------|
| Create | `src/gitea_dashboard/display.py` | Formatage et affichage Rich | `collector.py` (consomme `RepoData`) |
| Modify | `src/gitea_dashboard/cli.py` | Orchestration : config -> client -> collecte -> affichage | `client.py`, `collector.py`, `display.py` |
| Create | `tests/test_display.py` | Tests du formatage (capture console Rich) | `display.py` |
| Create | `tests/test_cli.py` | Tests d'integration du point d'entree | `cli.py` |
### Interfaces
#### display.py
```python
from rich.console import Console
def render_dashboard(repos: list[RepoData], console: Console | None = None) -> None:
"""Affiche le dashboard complet dans le terminal.
- Tableau principal : nom repo, indicateurs (fork/archive/mirror),
issues ouvertes, derniere release (tag + date relative)
- Section milestones : par repo ayant des milestones,
nom, progression (closed/total), date echeance
Le parametre console permet l'injection pour les tests.
"""
```
#### cli.py (modification)
```python
import sys
import os
def main() -> None:
"""Point d'entree principal.
1. Lit GITEA_URL (defaut: http://192.168.0.106:3000) et GITEA_TOKEN (requis)
2. Cree le GiteaClient
3. Collecte les donnees via collect_all()
4. Affiche via render_dashboard()
5. Gere les erreurs : config manquante, connexion refusee, timeout
"""
```
### Comportement attendu
Execution nominale :
```
$ export GITEA_TOKEN=abc123
$ gitea-dashboard
Gitea Dashboard
┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┓
┃ Repo ┃ Issues ┃ Release ┃
┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━┩
│ mon-projet │ 3 │ v1.2.0 (il y a 2j) │
│ autre-repo [F] │ 0 │ — │
│ archive [A] │ 1 │ v0.1.0 (il y a 6m) │
└──────────────────┴────────┴─────────────────────┘
Milestones
mon-projet / v2.0 : 3/5 (60%) — echeance 2026-04-01
```
Erreur de configuration :
```
$ gitea-dashboard
Erreur : GITEA_TOKEN non defini. Exportez la variable d'environnement.
```
### Tests
- `test_display.py` : capture de la sortie Rich via `Console(file=StringIO())`, verification du contenu (noms de repos, indicateurs, gestion des valeurs None)
- `test_cli.py` : mock des variables d'environnement, mock du client API, verification des messages d'erreur
### Livrable
`gitea-dashboard` est executable, affiche un dashboard lisible, et gere proprement les erreurs. Tous les tests passent.
---
## Phase 3 : Smoke test et documentation
**Goal** : Valider le fonctionnement reel sur l'instance Gitea et documenter le projet.
**Dependances** : phases 1 et 2 terminees, acces a l'instance Gitea (192.168.0.106:3000)
**Composants cles** :
- Test E2E manuel sur l'instance reelle
- Checklist de validation (repos avec/sans release, avec/sans milestone, forks)
- README.md a jour (installation, usage, configuration)
- CHANGELOG.md pour la v1.0.0
---
## Architecture des modules
| Module | Responsabilite | Dependances |
|--------|---------------|-------------|
| `client.py` | Communication API Gitea (auth, requetes, pagination) | `requests` |
| `collector.py` | Agregation des donnees de tous les repos | `client.py` |
| `display.py` | Formatage et rendu terminal | `rich`, `collector.py` (types) |
| `cli.py` | Point d'entree, configuration, orchestration | tous les modules |
---
## Risques d'audit
| Zone | Risque | Severite estimee |
|------|--------|-----------------|
| `client.py` — pagination | Boucle infinie si l'API repond toujours `limit` elements | major |
| `client.py` — gestion erreurs | Erreurs HTTP non-404 non gerees (500, timeout, connexion) | major |
| `cli.py` — token en clair | Affichage accidentel du token dans les logs/erreurs | critical |
| `collector.py` — types | Champs API manquants ou renommes entre versions Gitea | minor |
---
## Issues Gitea rattachees
| Issue | Titre | Phase |
|-------|-------|-------|
| [#1](https://gitea.tsmse.fr/admin/gitea-dashboard/issues/1) | Client API Gitea avec authentification et pagination | Phase 1 |
| [#2](https://gitea.tsmse.fr/admin/gitea-dashboard/issues/2) | Collecte des donnees : repos, issues, releases, milestones | Phase 1 |
| [#3](https://gitea.tsmse.fr/admin/gitea-dashboard/issues/3) | Affichage dashboard avec Rich (tableaux, couleurs) | Phase 2 |
| [#4](https://gitea.tsmse.fr/admin/gitea-dashboard/issues/4) | Point d'entree CLI et configuration | Phase 2 |
**Milestone** : [v1.0.0](https://gitea.tsmse.fr/admin/gitea-dashboard/milestone/30)
---
## Dependances
| Dependance | Type | Version |
|------------|------|---------|
| Python | Runtime | >= 3.10 |
| requests | Librairie | >= 2.31 |
| rich | Librairie | >= 13.0 |
| pytest | Dev | >= 7.0 |
| ruff | Dev | >= 0.4 |
| Instance Gitea | Service externe | 1.25.1 a 192.168.0.106:3000 |

View File

@@ -2,4 +2,144 @@
# Architecture — gitea-dashboard # Architecture — gitea-dashboard
<!-- A completer a l'etape 6 par l'architect --> ## Vue d'ensemble
```
gitea-dashboard
==============
Terminal Application Gitea API
-------- ----------- ---------
+------------------+
$ gitea-dashboard | cli.py |
------------------->| - lit env vars |
| - configure |
| - gere erreurs |
+--------+---------+
|
v
+------------------+
| collector.py |
| - orchestre la |
| collecte | +------------------+
| - agrege en |---->| client.py |
| RepoData | | - auth token |-----> GET /api/v1/user/repos
+--------+---------+ | - pagination |-----> GET /repos/{o}/{r}/releases/latest
| | - erreurs HTTP |-----> GET /repos/{o}/{r}/milestones
v +------------------+
+------------------+
| display.py |
| - tableau repos |
<-------------------| - milestones |
Output Rich | - indicateurs |
(tableaux, +------------------+
couleurs)
```
## Composants
### cli.py — Point d'entree
Responsabilite : orchestration du flux principal.
- Lit les variables d'environnement `GITEA_URL` (defaut: `http://192.168.0.106:3000`) et `GITEA_TOKEN` (requis)
- Valide la configuration, affiche un message d'erreur clair si manquante
- Cree le `GiteaClient`, lance la collecte, passe les resultats a l'affichage
- Gere les erreurs globales (connexion refusee, timeout, erreurs API)
- Enregistre comme entry point : `gitea-dashboard = "gitea_dashboard.cli:main"`
### client.py — Client API Gitea
Responsabilite : communication avec l'API REST Gitea.
- Authentification via header `Authorization: token <GITEA_TOKEN>`
- Methode interne `_get_paginated()` pour la pagination transparente (limit=50, boucle tant que page pleine)
- Methodes publiques : `get_repos()`, `get_latest_release(owner, repo)`, `get_milestones(owner, repo)`
- Gestion du 404 sur `/releases/latest` (retourne `None`)
- Utilise `requests.Session` pour reutiliser les connexions HTTP
### collector.py — Collecteur de donnees
Responsabilite : agregation des donnees de tous les repos.
- Definit le dataclass `RepoData` qui normalise les donnees d'un repo
- Calcul des issues ouvertes reelles : `open_issues_count - open_pr_counter`
- Pour chaque repo : enrichit avec la derniere release et les milestones ouvertes
- Fonction principale `collect_all(client) -> list[RepoData]`
### display.py — Affichage Rich
Responsabilite : formatage et rendu terminal.
- Tableau principal : nom du repo, indicateurs visuels ([F]ork, [A]rchive, [M]irror), issues ouvertes, derniere release (tag + date)
- Section milestones : affichee uniquement pour les repos ayant des milestones ouvertes, avec progression (closed/total)
- Accepte un parametre `Console` optionnel pour l'injection dans les tests
- Fonction principale `render_dashboard(repos, console=None)`
## Structure du projet
```
gitea-dashboard/
CLAUDE.md # Instructions projet
pyproject.toml # Config build, deps, entry point
README.md # Documentation utilisateur
src/
gitea_dashboard/
__init__.py # Docstring du package
cli.py # Point d'entree, config, orchestration
client.py # Client API Gitea (auth, pagination)
collector.py # Agregation des donnees repos
display.py # Formatage Rich (tableaux, couleurs)
tests/
__init__.py
test_client.py # Tests client API (mocks requests)
test_collector.py # Tests collecteur (mocks client)
test_display.py # Tests affichage (capture console)
test_cli.py # Tests integration CLI (mocks env)
docs/
plans/
v1.0.0-plan.md # Plan de version
technical/
ARCHITECTURE.md # Ce fichier
decisions.md # ADR
research.md # Recherche technique API
discovery/
synthesis.md # Synthese discovery
project/
descriptif.md # Descriptif du projet
```
## Flux de donnees principal
```
1. cli.main()
|
+-- Lit os.environ["GITEA_TOKEN"] et os.environ.get("GITEA_URL", default)
|
+-- GiteaClient(url, token)
|
+-- collect_all(client)
| |
| +-- client.get_repos() -> list[dict] (pagine)
| |
| +-- Pour chaque repo:
| +-- client.get_latest_release(owner, name) -> dict | None
| +-- client.get_milestones(owner, name) -> list[dict]
| +-- RepoData(...)
|
+-- render_dashboard(repos)
|
+-- Rich Table (repos)
+-- Rich Table (milestones)
+-- Console.print()
```
## Decisions architecturales
Les decisions sont tracees dans `docs/technical/decisions.md` (format ADR).
Decisions cles pour v1.0.0 :
- **ADR-001** : Stack Python + requests + rich
- **ADR-002** : 4 modules maximum (client, collector, display, cli)
- **ADR-003** : Pas de parallelisation en v1 (sequentiel, plus simple a deboguer)

View File

@@ -16,3 +16,33 @@
- Pas de framework CLI lourd (argparse suffit si besoin) - Pas de framework CLI lourd (argparse suffit si besoin)
- rich offre des tableaux et couleurs sans configuration complexe - rich offre des tableaux et couleurs sans configuration complexe
- Dependance a requests (pas de client async, acceptable pour un affichage unique) - 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)