diff --git a/docs/superpowers/specs/2026-03-13-gitea-integration-design.md b/docs/superpowers/specs/2026-03-13-gitea-integration-design.md index c3297bb..9c41dfa 100644 --- a/docs/superpowers/specs/2026-03-13-gitea-integration-design.md +++ b/docs/superpowers/specs/2026-03-13-gitea-integration-design.md @@ -24,13 +24,17 @@ Lier les tickets Lesstime à Gitea pour : ### Backend -#### Configuration globale — table `Setting` +#### Configuration globale — entité `GiteaConfiguration` -Nouvelle entité `Setting` (clé-valeur) pour stocker : -- `gitea_url` — URL de l'instance Gitea (ex: `https://git.malio.fr`) -- `gitea_token` — Token API Gitea (accès repos) +Entité dédiée (singleton en base, un seul enregistrement) : -Alternative : utiliser `config/reference.php` avec des variables d'environnement. La table `Setting` est préférable car configurable depuis l'admin sans redéploiement. +``` +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 @@ -45,33 +49,48 @@ Nullable car tous les projets ne sont pas forcément liés à un repo. Service Symfony qui encapsule les appels HTTP vers l'API Gitea REST v1. -Méthodes : -- `listRepositories(): array` — liste les repos accessibles (pour sélection dans le projet) -- `createBranch(project, task, type, baseBranch): string` — crée une branche `/CODE-NUM-slug` à partir d'une branche de base -- `listBranches(project, taskCode): array` — liste les branches matchant le pattern `*/CODE-NUM*` -- `listCommits(project, branch): array` — liste les commits d'une branche -- `listPullRequests(project, taskCode): array` — liste les PRs dont la branche source matche le code ticket -- `getPullRequestChecks(project, prNumber): array` — statut CI/CD d'une PR +**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). -Le service utilise `HttpClientInterface` de Symfony. Le token est envoyé en header `Authorization: token `. +**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 `/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 (custom operations ou controllers si nécessaire) : +Nouveaux endpoints : -- `GET /api/settings/gitea` — récupérer la config Gitea (admin only) +- `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/info` — récupérer branches + commits + PRs + CI 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 ou section dans l'admin pour configurer : +Nouveau tab `GiteaAdminTab.vue` dans l'admin pour configurer : - URL du serveur Gitea -- Token API +- Token API (champ password, affiche seulement si un token est configuré) - Bouton "Tester la connexion" #### ProjectDrawer — Config repo @@ -82,37 +101,45 @@ Ajout de champs dans le drawer de projet : #### TaskModal — Section Git -Nouvelle section dans la TaskModal (visible si le projet a un repo configuré) : +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** : +**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 stocké chiffré en base (ou au minimum non exposé dans les réponses API) -- L'endpoint `GET /api/settings/gitea` retourne l'URL mais masque le token (ex: `****`) -- Seuls les admins peuvent configurer le serveur Gitea -- Les utilisateurs authentifiés peuvent créer des branches et voir les infos git +- 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}` | -| Lister PRs | `GET /api/v1/repos/{owner}/{repo}/pulls?state=all` | +| 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