- 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>
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
GiteaApiExceptionavec 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— appelleGET /api/v1/versionpour vérifier la connexionlistRepositories(): array— liste les repos accessibles (pour sélection dans le projet)getDefaultBranch(project): string— récupère la branche par défaut du repo viaGET /api/v1/repos/{owner}/{repo}createBranch(project, task, type, baseBranch): string— crée une branche<type>/CODE-NUM-slugà partir d'une branche de baselistBranches(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 parheadbranch (paramètre?head=supporté par Gitea)getPullRequestChecks(project, prNumber): array— statut CI/CD d'une PRcopyBranchName(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 ticketGET /api/tasks/{id}/gitea/branches— lister branches liées au ticketGET /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/repoune 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
SodiumEncryptoravec cléGITEA_ENCRYPTION_KEY - L'endpoint
GET /api/settings/gitearetourneurl+hasToken: bool, jamais le token en clair - Seuls les
ROLE_ADMINpeuvent 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)