docs(v1.0.0): version plan and ADR
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
294
docs/plans/v1.0.0-plan.md
Normal file
294
docs/plans/v1.0.0-plan.md
Normal 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 |
|
||||
@@ -2,4 +2,144 @@
|
||||
|
||||
# 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)
|
||||
|
||||
@@ -16,3 +16,33 @@
|
||||
- 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)
|
||||
|
||||
Reference in New Issue
Block a user