262 lines
8.0 KiB
Python
262 lines
8.0 KiB
Python
"""Point d'entree pour le CLI gitea-dashboard."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import os
|
|
import sys
|
|
|
|
import requests
|
|
from rich.console import Console
|
|
|
|
from gitea_dashboard.client import GiteaClient
|
|
from gitea_dashboard.collector import collect_all, collect_milestones
|
|
from gitea_dashboard.config import load_config, merge_config
|
|
from gitea_dashboard.display import (
|
|
AVAILABLE_COLUMNS,
|
|
parse_columns,
|
|
render_dashboard,
|
|
render_milestones,
|
|
sort_repos,
|
|
)
|
|
from gitea_dashboard.exporter import export_json, export_milestones_json
|
|
|
|
_DEFAULT_URL = "http://192.168.0.106:3000"
|
|
|
|
|
|
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
|
"""Parse les arguments CLI.
|
|
|
|
Options:
|
|
--repo / -r : noms de repos a inclure (repeatable)
|
|
--exclude / -x : noms de repos a exclure (repeatable)
|
|
--milestones : affiche la vue milestones au lieu du dashboard repos
|
|
--columns : liste des colonnes a afficher
|
|
--config : chemin vers un fichier de configuration YAML alternatif
|
|
|
|
Returns:
|
|
Namespace avec les options parsees.
|
|
"""
|
|
parser = argparse.ArgumentParser(
|
|
description="Dashboard CLI affichant l'etat des repos Gitea.",
|
|
)
|
|
parser.add_argument(
|
|
"--repo",
|
|
"-r",
|
|
action="append",
|
|
default=None,
|
|
help="Filtrer par nom de repo (sous-chaine, insensible a la casse). Repeatable.",
|
|
)
|
|
parser.add_argument(
|
|
"--exclude",
|
|
"-x",
|
|
action="append",
|
|
default=None,
|
|
help="Exclure les repos par nom (sous-chaine, insensible a la casse). Repeatable.",
|
|
)
|
|
parser.add_argument(
|
|
"--sort",
|
|
"-s",
|
|
choices=["name", "issues", "release", "activity"],
|
|
default="name",
|
|
help="Critere de tri des repos (defaut: name).",
|
|
)
|
|
parser.add_argument(
|
|
"--format",
|
|
"-f",
|
|
choices=["table", "json"],
|
|
default="table",
|
|
dest="format",
|
|
help="Format de sortie (defaut: table).",
|
|
)
|
|
parser.add_argument(
|
|
"--health",
|
|
action="store_true",
|
|
default=False,
|
|
help="Verifie la connexion Gitea et affiche la version. Exit 0 si OK, 1 sinon.",
|
|
)
|
|
parser.add_argument(
|
|
"--no-desc",
|
|
action="store_true",
|
|
default=False,
|
|
help="Masque la colonne Description dans le tableau.",
|
|
)
|
|
parser.add_argument(
|
|
"--config",
|
|
default=None,
|
|
help="Chemin vers un fichier de configuration YAML alternatif.",
|
|
)
|
|
parser.add_argument(
|
|
"--milestones",
|
|
action="store_true",
|
|
default=False,
|
|
help="Affiche la vue milestones au lieu du dashboard repos.",
|
|
)
|
|
parser.add_argument(
|
|
"--columns",
|
|
default=None,
|
|
help="Colonnes a afficher (separees par virgules). Prefixe '-' pour exclure. 'help' pour lister.",
|
|
)
|
|
return parser.parse_args(argv)
|
|
|
|
|
|
def _resolve_config(args: argparse.Namespace) -> argparse.Namespace:
|
|
"""Resout la configuration en appliquant la priorite CLI > env > config > defaults.
|
|
|
|
1. Charge le fichier config (args.config ou chemin par defaut)
|
|
2. Lit les variables d'environnement pertinentes
|
|
3. Fusionne avec les valeurs CLI
|
|
4. Retourne un Namespace enrichi
|
|
"""
|
|
file_config = load_config(args.config)
|
|
|
|
env_vars: dict = {}
|
|
gitea_url_env = os.environ.get("GITEA_URL")
|
|
if gitea_url_env:
|
|
env_vars["url"] = gitea_url_env
|
|
gitea_auth_env = os.environ.get("GITEA_TOKEN")
|
|
if gitea_auth_env:
|
|
env_vars["auth"] = gitea_auth_env
|
|
|
|
cli_args: dict = {}
|
|
if args.sort != "name":
|
|
cli_args["sort"] = args.sort
|
|
if args.repo is not None:
|
|
cli_args["include"] = args.repo
|
|
if args.exclude is not None:
|
|
cli_args["exclude"] = args.exclude
|
|
if args.no_desc:
|
|
cli_args["no_desc"] = True
|
|
|
|
defaults = {
|
|
"url": _DEFAULT_URL,
|
|
"sort": "name",
|
|
"no_desc": False,
|
|
}
|
|
|
|
merged = merge_config(cli_args, env_vars, file_config, defaults)
|
|
|
|
args.resolved_url = merged.get("url", _DEFAULT_URL)
|
|
args.resolved_auth = merged.get("auth")
|
|
args.sort = merged.get("sort", args.sort)
|
|
if merged.get("include") and args.repo is None:
|
|
args.repo = merged["include"]
|
|
if merged.get("exclude") and args.exclude is None:
|
|
args.exclude = merged["exclude"]
|
|
if merged.get("no_desc") and not args.no_desc:
|
|
args.no_desc = merged["no_desc"]
|
|
|
|
return args
|
|
|
|
|
|
def _run_health_check(client: GiteaClient, console: Console) -> None:
|
|
"""Execute le health check et affiche les resultats.
|
|
|
|
1. Appelle client.get_version() -> affiche "Gitea vX.Y.Z"
|
|
2. Appelle client.get_repos() -> affiche "N repos accessibles"
|
|
"""
|
|
version_info = client.get_version()
|
|
version = version_info.get("version", "inconnue")
|
|
console.print(f"Gitea v{version}")
|
|
|
|
repos = client.get_repos()
|
|
console.print(f"{len(repos)} repos accessibles")
|
|
|
|
|
|
def _print_columns_help(console: Console) -> None:
|
|
"""Affiche les colonnes disponibles."""
|
|
console.print("Colonnes disponibles :")
|
|
for name, desc in AVAILABLE_COLUMNS.items():
|
|
console.print(f" {name:15s} {desc}")
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> None:
|
|
"""Point d'entree principal.
|
|
|
|
Args:
|
|
argv: Arguments CLI. Si None, utilise sys.argv (via argparse).
|
|
|
|
1. Parse les options CLI
|
|
2. Resout la configuration (CLI > env > config > defaults)
|
|
3. Cree le GiteaClient
|
|
4. Route vers le mode appropriate (health, milestones, dashboard)
|
|
5. Gere les erreurs : config manquante, connexion refusee, timeout
|
|
"""
|
|
args = parse_args(argv)
|
|
console = Console(stderr=True)
|
|
|
|
try:
|
|
args = _resolve_config(args)
|
|
except (FileNotFoundError, ValueError) as exc:
|
|
console.print(f"[red]Erreur config : {exc}[/red]")
|
|
sys.exit(1)
|
|
|
|
# Handle --columns help before auth check
|
|
if args.columns is not None:
|
|
cols = parse_columns(args.columns, no_desc=args.no_desc)
|
|
if cols == ["__help__"]:
|
|
_print_columns_help(Console())
|
|
return
|
|
else:
|
|
cols = None
|
|
|
|
auth = args.resolved_auth if hasattr(args, "resolved_auth") and args.resolved_auth else None
|
|
if not auth:
|
|
auth = os.environ.get("GITEA_TOKEN")
|
|
if not auth:
|
|
console.print(
|
|
"[red]Erreur : GITEA_TOKEN non defini. Exportez la variable d'environnement.[/red]"
|
|
)
|
|
sys.exit(1)
|
|
|
|
url = (
|
|
args.resolved_url
|
|
if hasattr(args, "resolved_url")
|
|
else os.environ.get("GITEA_URL", _DEFAULT_URL)
|
|
)
|
|
|
|
client = GiteaClient(url, auth)
|
|
|
|
try:
|
|
if args.health:
|
|
_run_health_check(client, console)
|
|
return
|
|
|
|
if args.milestones:
|
|
milestones = collect_milestones(client, include=args.repo, exclude=args.exclude)
|
|
if args.format == "json":
|
|
print(export_milestones_json(milestones)) # noqa: T201
|
|
else:
|
|
render_milestones(milestones)
|
|
return
|
|
|
|
repos = collect_all(client, include=args.repo, exclude=args.exclude)
|
|
except requests.ConnectionError:
|
|
console.print("[red]Erreur : connexion refusee. Verifiez l'URL et le serveur Gitea.[/red]")
|
|
sys.exit(1)
|
|
except requests.Timeout:
|
|
console.print(
|
|
"[red]Erreur : delai d'attente depasse. Le serveur Gitea ne repond pas.[/red]"
|
|
)
|
|
sys.exit(1)
|
|
except requests.RequestException as exc:
|
|
# Ne jamais afficher le token dans les messages d'erreur
|
|
msg = str(exc)
|
|
if auth in msg:
|
|
msg = msg.replace(auth, "***")
|
|
console.print(f"[red]Erreur API : {msg}[/red]")
|
|
sys.exit(1)
|
|
|
|
if args.format == "json":
|
|
sorted_repos = sort_repos(repos, args.sort)
|
|
print(export_json(sorted_repos)) # noqa: T201
|
|
else:
|
|
# Resolve columns for dashboard
|
|
active_cols = cols if cols is not None else parse_columns(None, no_desc=args.no_desc)
|
|
render_dashboard(
|
|
repos,
|
|
sort_key=args.sort,
|
|
show_description="description" in active_cols,
|
|
columns=active_cols,
|
|
)
|