Files
Lesstime/docs/superpowers/specs/2026-03-13-gitea-integration-design.md
Matthieu aa5f6cc7c1 docs : address spec review feedback for Gitea integration
- Use dedicated GiteaConfiguration entity instead of generic Setting table
- Add token encryption with SodiumEncryptor + GITEA_ENCRYPTION_KEY
- Split aggregated /gitea/info into separate /branches and /pull-requests endpoints
- Fix branch pattern matching to avoid PROJ-420 matching PROJ-42
- Add error handling strategy with GiteaApiException and degraded UI state
- Add slug generation via AsciiSlugger with 50 char limit
- Add test connection endpoint
- Extract TaskGitSection.vue component
- Add frontend service layer (gitea.ts)
- Add i18n consideration
- Add "copy branch name" feature

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 12:16:29 +01:00

7.0 KiB

Intégration Gitea — Design Spec

Objectif

Lier les tickets Lesstime à Gitea pour :

  • Créer une branche depuis un ticket (avec choix du type : feature, fix, refactor, etc.)
  • Voir les branches, commits, PRs et statut CI liés à un ticket
  • Le tout à la demande (pas de webhook, pas de cron, pas de stockage git en base)

Décisions

Question Décision
Interaction Depuis Lesstime uniquement (bouton + affichage)
Repos Un projet = un repo Gitea
Nommage branches <type>/PROJ-42-titre-en-slug (type choisi par l'utilisateur)
Détection commits Par la branche (tous les commits d'une branche liée au ticket)
Données affichées Branches + commits + PRs + statut CI/CD
Config serveur Globale (admin) : URL + token API
Config repo Par projet : owner + repo name
Synchronisation À la demande (appel API Gitea en temps réel)

Architecture

Backend

Configuration globale — entité GiteaConfiguration

Entité dédiée (singleton en base, un seul enregistrement) :

id: int
url: string|null        (ex: "https://git.malio.fr")
token: string|null       (token API personnel Gitea, chiffré via SodiumEncryptor)

Le token est chiffré au repos avec une clé symétrique définie en variable d'environnement (GITEA_ENCRYPTION_KEY). L'endpoint GET ne retourne jamais le token en clair — seulement un booléen hasToken.

Entité Project — nouveaux champs

gitea_owner: string|null  (ex: "malio")
gitea_repo: string|null   (ex: "lesstime")

Nullable car tous les projets ne sont pas forcément liés à un repo.

Service GiteaApiService

Service Symfony qui encapsule les appels HTTP vers l'API Gitea REST v1.

Configuration HTTP :

  • Timeout : 10s par requête
  • En cas d'erreur Gitea (timeout, 5xx, réseau), le service lève une GiteaApiException avec un message clair. Le frontend affiche un état dégradé (message d'erreur dans la section Git, le reste de la TaskModal fonctionne normalement).

Méthodes :

  • testConnection(): bool — appelle GET /api/v1/version pour vérifier la connexion
  • listRepositories(): array — liste les repos accessibles (pour sélection dans le projet)
  • getDefaultBranch(project): string — récupère la branche par défaut du repo via GET /api/v1/repos/{owner}/{repo}
  • createBranch(project, task, type, baseBranch): string — crée une branche <type>/CODE-NUM-slug à partir d'une branche de base
  • listBranches(project, taskCode): array — liste les branches matchant le pattern */CODE-NUM-* ou */CODE-NUM (délimité pour éviter de matcher PROJ-420 quand on cherche PROJ-42)
  • listCommits(project, branch): array — liste les commits d'une branche (paginé, max 30)
  • listPullRequests(project, taskCode): array — liste les PRs en filtrant par head branch (paramètre ?head= supporté par Gitea)
  • getPullRequestChecks(project, prNumber): array — statut CI/CD d'une PR
  • copyBranchName(task, type): string — génère le nom de branche sans appeler Gitea (pour le bouton "copier")

Slug : généré côté backend avec Symfony\Component\String\Slugger\AsciiSlugger — gère les accents français, tronqué à 50 caractères max pour le slug (le nom complet de branche reste sous 80 chars).

Endpoints API Platform

Nouveaux endpoints :

  • GET /api/settings/gitea — récupérer la config Gitea (admin only, retourne url + hasToken)
  • PUT /api/settings/gitea — sauvegarder la config Gitea (admin only)
  • POST /api/settings/gitea/test — tester la connexion Gitea (admin only)
  • GET /api/gitea/repositories — lister les repos disponibles (pour config projet)
  • POST /api/tasks/{id}/gitea/branches — créer une branche pour un ticket
  • GET /api/tasks/{id}/gitea/branches — lister branches liées au ticket
  • GET /api/tasks/{id}/gitea/pull-requests — lister PRs liées au ticket (avec statut CI inclus)

Les endpoints branches et PRs sont séparés pour permettre un chargement progressif côté frontend et éviter de fan-out trop de requêtes Gitea en un seul appel.

Frontend

Service frontend/services/gitea.ts

Nouveau service API encapsulant les appels, cohérent avec le pattern existant (frontend/services/).

Admin — Config Gitea

Nouveau tab GiteaAdminTab.vue dans l'admin pour configurer :

  • URL du serveur Gitea
  • Token API (champ password, affiche seulement si un token est configuré)
  • Bouton "Tester la connexion"

ProjectDrawer — Config repo

Ajout de champs dans le drawer de projet :

  • Sélecteur de repo Gitea (dropdown alimenté par GET /api/gitea/repositories)
  • Affiche owner/repo une fois sélectionné

TaskModal — Section Git

Nouveau composant TaskGitSection.vue intégré dans la TaskModal (visible et chargé uniquement si le projet a un repo configuré).

Bouton "Créer une branche" :

  • Sélecteur de type : feature, fix, refactor, hotfix, chore
  • Sélecteur de branche de base (default: branche par défaut du repo)
  • Preview du nom : feature/PROJ-42-titre-de-la-tache
  • Bouton de confirmation
  • Bouton "Copier le nom" (génère le nom sans appeler Gitea, pour création locale)

Affichage des infos Git (chargement progressif) :

  • Liste des branches liées (avec statut : active / mergée / supprimée)
  • Pour chaque branche : derniers commits (hash court, message, auteur, date)
  • PRs associées (titre, statut : open/merged/closed, reviewers)
  • Statut CI/CD par PR (checks : success/failure/pending)
  • Liens directs vers Gitea pour chaque élément
  • En cas d'erreur Gitea : message d'erreur dans la section, le reste de la modal reste fonctionnel

Sécurité

  • Le token Gitea est chiffré en base via SodiumEncryptor avec clé GITEA_ENCRYPTION_KEY
  • L'endpoint GET /api/settings/gitea retourne url + hasToken: bool, jamais le token en clair
  • Seuls les ROLE_ADMIN peuvent configurer le serveur Gitea et les repos
  • Les utilisateurs authentifiés peuvent créer des branches et voir les infos git pour les tâches qu'ils ont le droit de voir

i18n

Toutes les chaînes UI (labels, messages d'erreur, types de branche) passent par le système i18n existant (frontend/i18n/locales/fr.json et en.json).

API Gitea — Endpoints utilisés

Action Méthode Gitea API
Tester connexion GET /api/v1/version
Info repo (branche défaut) GET /api/v1/repos/{owner}/{repo}
Lister repos GET /api/v1/repos/search
Lister branches GET /api/v1/repos/{owner}/{repo}/branches
Créer branche POST /api/v1/repos/{owner}/{repo}/branches
Lister commits GET /api/v1/repos/{owner}/{repo}/commits?sha={branch}&limit=30
Lister PRs par branche GET /api/v1/repos/{owner}/{repo}/pulls?state=all&head={branch}
Statut CI GET /api/v1/repos/{owner}/{repo}/commits/{sha}/statuses

Hors scope

  • Webhooks Gitea → Lesstime
  • Stockage des commits/PRs en base
  • Création de PR depuis Lesstime
  • Lien multi-repo par projet
  • Synchronisation périodique (cron/polling)