From c34cc41496b7c994f4909be5b392a39623eff6ad Mon Sep 17 00:00:00 2001 From: Gato Date: Sun, 7 Jun 2026 08:05:53 +0200 Subject: [PATCH] ci: pipeline Gitea Actions build & push Docker sur push main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dockerfile multi-stage (build eclipse-temurin:25-jdk → runtime) - CI : tests via actions/setup-java puis build & push vers registry Gitea - Trigger Watchtower après push sur main - CLAUDE.md + rules projet (.claude/rules/) - Version build.gradle : 0.0.1-SNAPSHOT → 0.0.1 Co-Authored-By: Claude Sonnet 4.6 --- .claude/rules/architecture.md | 53 +++++++++++++++++++++++ .claude/rules/commits-versions.md | 36 ++++++++++++++++ .claude/rules/documentation.md | 40 ++++++++++++++++++ .claude/rules/issues-gitea.md | 19 +++++++++ .claude/rules/tests.md | 63 ++++++++++++++++++++++++++++ .claude/rules/webapp-integration.md | 33 +++++++++++++++ .gitea/workflows/ci.yml | 65 +++++++++++++++++++++++++++++ CLAUDE.md | 49 ++++++++++++++++++++++ Dockerfile | 18 ++++++++ build.gradle | 2 +- 10 files changed, 377 insertions(+), 1 deletion(-) create mode 100644 .claude/rules/architecture.md create mode 100644 .claude/rules/commits-versions.md create mode 100644 .claude/rules/documentation.md create mode 100644 .claude/rules/issues-gitea.md create mode 100644 .claude/rules/tests.md create mode 100644 .claude/rules/webapp-integration.md create mode 100644 .gitea/workflows/ci.yml create mode 100644 CLAUDE.md create mode 100644 Dockerfile diff --git a/.claude/rules/architecture.md b/.claude/rules/architecture.md new file mode 100644 index 0000000..17737ad --- /dev/null +++ b/.claude/rules/architecture.md @@ -0,0 +1,53 @@ +# Architecture — Clean Architecture (Hexagonale) + +## Structure des couches + +L'application suit une **Clean Architecture** avec le pattern Ports & Adapters : + +``` +src/main/java/com/olhar/olharapi/ + domain/ ← Entités métier et exceptions (aucune dépendance framework) + model/ + exception/ + application/ ← Cas d'usage (logique métier), ports d'entrée et de sortie + port/ + in/ ← Interfaces des cas d'usage (appelées par interfaces/) + out/ ← Interfaces de sortie (implémentées par infrastructure/) + usecase/ ← Implémentations des cas d'usage + infrastructure/ ← Adaptateurs sortants (BDD, sécurité, configuration) + persistence/ + entity/ ← Entités JPA (distinctes des entités domaine) + mapper/ ← Conversion domaine ↔ JPA + repository/ ← Adaptateurs JPA implémentant les ports out + security/ + config/ + interfaces/ ← Adaptateurs entrants (REST) + rest/ + controller/ + dto/ + request/ + response/ + mapper/ ← Conversion domaine ↔ DTO REST + exception/ ← Handlers d'exception globaux +``` + +## Règles par couche + +- **`domain/`** : Java pur — pas de Spring, pas de JPA, pas de Lombok avancé. Entités et exceptions métier uniquement. +- **`application/`** : interfaces des ports (`port/in`, `port/out`) + services implémentant les cas d'usage. Pas de dépendance à Spring Web ou JPA. +- **`infrastructure/`** : adaptateurs Spring/JPA/sécurité. Implémente les ports `out`. Annoté Spring (`@Service`, `@Repository`, `@Component`). +- **`interfaces/`** : contrôleurs REST et DTOs. Invoque les ports `in`. Annoté `@RestController`. Pas de logique métier inline. + +## Interdictions + +- Jamais d'import de `infrastructure/` ou `interfaces/` depuis `domain/` ou `application/`. +- Jamais de logique métier dans un contrôleur (`interfaces/rest/controller/`). +- Jamais d'entité JPA (`infrastructure/persistence/entity/`) retournée directement par un contrôleur. +- Jamais d'appel direct à un repository JPA depuis `application/usecase/` — passer par les ports `out`. +- Les mappers (`UserPersistenceMapper`, `UserRestMapper`) restent dans leur couche respective, pas dans `domain/`. + +## Règles de code + +- Utiliser **Lombok** pour les entités, DTOs et mappers (mais pas dans `domain/model/` si les entités sont simples). +- Pas de commentaires explicatifs sur ce que fait le code — seulement sur le **pourquoi** si non évident. +- Pas d'abstraction prématurée : implémenter ce qui est demandé, pas ce qui pourrait être utile plus tard. diff --git a/.claude/rules/commits-versions.md b/.claude/rules/commits-versions.md new file mode 100644 index 0000000..9702deb --- /dev/null +++ b/.claude/rules/commits-versions.md @@ -0,0 +1,36 @@ +# Commits et versioning — Olhar-API + +## Scopes Conventional Commits + +Le format global est défini dans les règles workspace (`commit.md`). Scopes spécifiques à ce projet : + +| Scope | Usage | +|-------|-------| +| `auth` | Authentification, JWT, sécurité | +| `user` | Entité utilisateur, inscription, profil | +| `photo` | Gestion des photos (upload, listing, métadonnées) | +| `db` | Migrations Flyway, schéma | +| `config` | Configuration Spring, OpenAPI, CORS | +| `ci` | Workflows Gitea Actions | +| `docs` | Documentation technique ou fonctionnelle | + +## Versioning dans build.gradle + +La version est définie dans `build.gradle` : + +```groovy +version = '0.1.0' +``` + +Règles de bump (semver) : +- `PATCH` : correction de bug, ajout mineur, refactoring +- `MINOR` : nouvelle fonctionnalité complète et fonctionnelle +- `MAJOR` : changement cassant d'API ou livraison majeure + +Mettre à jour `version` dans `build.gradle` à chaque commit significatif. + +## Convention de branches + +- `main` : code stable, toujours fonctionnel et buildable +- `feat/` : nouvelles fonctionnalités +- Merger dans `main` uniquement quand les tests passent. diff --git a/.claude/rules/documentation.md b/.claude/rules/documentation.md new file mode 100644 index 0000000..c66ee0b --- /dev/null +++ b/.claude/rules/documentation.md @@ -0,0 +1,40 @@ +# Documentation — Olhar-API + +## Wiki du projet + +La documentation de Olhar-API est maintenue dans `../Olhar-API.wiki/` (chemin relatif à la racine du projet). + +Les règles générales de mise à jour du wiki sont définies dans les règles workspace (`wiki.md`). + +## Fichiers du wiki + +| Fichier | Contenu | +|---------|---------| +| `Specification-Fonctionnelle.md` | Comportement visible : endpoints, règles métier, cas d'usage | +| `Specification-Technique.md` | Architecture, entités, flux de données, choix techniques | +| `Changelog.md` | Historique des modifications (entrée en tête, plus récent en premier) | + +## Quand mettre à jour + +- **Nouvel endpoint** → mettre à jour les deux specs (fonctionnelle et technique). +- **Nouvelle entité ou modification de schéma** → mettre à jour la spec technique. +- **Changement de règle métier** → mettre à jour la spec fonctionnelle. +- **Ajout de dépendance ou changement d'architecture** → mettre à jour la spec technique. + +## Format des endpoints dans la spec technique + +```markdown +### POST /auth/register + +**Corps de la requête :** +\`\`\`json +{ "email": "...", "password": "..." } +\`\`\` + +**Réponse (201) :** +\`\`\`json +{ "token": "...", "user": { "id": 1, "email": "..." } } +\`\`\` + +**Erreurs :** 409 si email déjà utilisé, 400 si validation échoue. +``` diff --git a/.claude/rules/issues-gitea.md b/.claude/rules/issues-gitea.md new file mode 100644 index 0000000..a58a6ce --- /dev/null +++ b/.claude/rules/issues-gitea.md @@ -0,0 +1,19 @@ +# Issues Gitea — Suivi du projet Olhar-API + +## Dépôt principal + +Issues du projet : **https://git.goutailler-olivier.com/Gato/Olhar-API** + +- Lire les issues ouvertes avant de commencer une tâche. +- Les implémenter par ordre de priorité (labels, milestones, ordre de création). +- Fermer l'issue correspondante après implémentation et push. + +## Dépôt frontend + +Issues côté frontend : **https://git.goutailler-olivier.com/Gato/Olhar** + +- Créer une issue ici quand un changement d'API impacte Olhar-webapp (voir `webapp-integration.md`). + +## Utilisation du skill Gitea + +Utiliser le skill `gitea-issue` pour lire et créer des issues sans quitter le contexte de travail. diff --git a/.claude/rules/tests.md b/.claude/rules/tests.md new file mode 100644 index 0000000..7e9d46f --- /dev/null +++ b/.claude/rules/tests.md @@ -0,0 +1,63 @@ +# Tests — Olhar-API + +## Couverture minimale : 90% + +La couverture (lignes, branches, méthodes) doit être **≥ 90%** en permanence, vérifiée via JaCoCo. +Le seuil global est défini dans les règles workspace (`tests.md`). + +## Types de tests + +| Type | Outil | Suffixe de classe | +|------|-------|-------------------| +| Unitaire | JUnit 5 | `*Test` | +| Intégration | JUnit 5 + Testcontainers + PostgreSQL | `*IT` | + +Les tests d'intégration étendent `AbstractIntegrationTest` qui démarre un conteneur PostgreSQL via Testcontainers. + +## Structure attendue + +Pour chaque classe de production, une classe de test correspondante : +- `JwtService` → `JwtServiceTest` (unitaire, pas de Spring context) +- `AuthController` → `AuthControllerIT` (intégration, `@SpringBootTest`) +- `UserRepositoryAdapter` → test d'intégration avec BDD réelle + +## Exécution via Podman (obligatoire) + +Voir `java-env.md` (règles workspace) pour les contraintes. Commande standard : + +```bash +podman run --rm -v $(pwd):/workspace:Z -w /workspace eclipse-temurin:25-jdk ./gradlew test --no-daemon +``` + +Testcontainers nécessite le socket Docker/Podman : + +```bash +podman run --rm \ + -v $(pwd):/workspace:Z \ + -v /run/user/$(id -u)/podman/podman.sock:/var/run/docker.sock \ + -w /workspace \ + eclipse-temurin:25-jdk \ + ./gradlew test --no-daemon +``` + +## JaCoCo dans build.gradle + +```groovy +test { + useJUnitPlatform() + finalizedBy jacocoTestReport +} + +jacocoTestReport { + reports { xml.required = true } + dependsOn test +} + +jacocoTestCoverageVerification { + violationRules { + rule { + limit { minimum = 0.90 } + } + } +} +``` diff --git a/.claude/rules/webapp-integration.md b/.claude/rules/webapp-integration.md new file mode 100644 index 0000000..c5c81e7 --- /dev/null +++ b/.claude/rules/webapp-integration.md @@ -0,0 +1,33 @@ +# Intégration avec Olhar-webapp (frontend) + +## Principe + +Olhar-API est le backend consommé par **Olhar-webapp** (Angular). Tout changement d'API susceptible d'impacter le frontend doit être signalé. + +## Quand créer une issue sur Olhar-webapp + +Créer une issue sur **https://git.goutailler-olivier.com/Gato/Olhar** lorsque : +- Un endpoint change de signature (URL, méthode, corps, réponse) +- Un nouveau champ est ajouté à une réponse existante (à exploiter côté frontend) +- Un endpoint est supprimé ou déprécié +- Un comportement d'erreur change (code HTTP, format du message) + +## Contenu de l'issue + +L'issue doit décrire : +- L'endpoint concerné (méthode + URL) +- Ce qui a changé dans l'API +- L'action attendue côté frontend (adapter l'appel, afficher un nouveau champ, etc.) +- Si un feature flag côté frontend couvrait l'absence de cet endpoint, préciser qu'il peut être **retiré** maintenant que l'API est disponible (nettoyage du code mock et du flag correspondant) + +## CORS et sécurité + +- Les origines autorisées sont configurées dans `SecurityConfig`. +- En développement : `http://localhost:4200` (Angular dev server). +- Ne pas ouvrir `*` en production. + +## Contrat d'API + +- Les DTOs de réponse (`interfaces/rest/dto/response/`) constituent le contrat public. +- Ne jamais modifier un champ existant d'un DTO sans créer une issue sur Olhar-webapp. +- Ajouter des champs est non-cassant ; en supprimer ou en renommer est cassant. diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..12e91d9 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,65 @@ +name: CI — Tests & Docker Build + +on: + push: + branches: + - main + - 'feat/**' + pull_request: + +jobs: + test: + name: Tests + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Java 25 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '25' + + - name: Lancer les tests + run: ./gradlew test --no-daemon + + build-and-push: + name: Build & push image Docker + needs: test + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Lire la version depuis build.gradle + id: version + run: echo "VERSION=$(grep '^version' build.gradle | awk -F"'" '{print $2}')" >> $GITHUB_OUTPUT + + - name: Setup Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login registry Gitea + uses: docker/login-action@v3 + with: + registry: git.goutailler-olivier.com + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Build & push image + uses: docker/build-push-action@v5 + with: + context: . + file: Dockerfile + push: true + tags: | + git.goutailler-olivier.com/gato/olhar-api:latest + git.goutailler-olivier.com/gato/olhar-api:${{ steps.version.outputs.VERSION }} + + - name: Trigger déploiement Watchtower + run: | + wget -q --post-data='' \ + --header="Authorization: Bearer ${{ secrets.WATCHTOWER_TOKEN }}" \ + https://watchtower.goutailler-olivier.com/v1/update \ + -O /dev/null diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..ae5f03b --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,49 @@ +# CLAUDE.md — Olhar-API + +## Contexte du projet + +API REST Spring Boot 3 (Java 25) pour l'application Olhar de gestion de photos personnelles. +- **Architecture** : Clean Architecture hexagonale (Ports & Adapters) +- **Stack** : Spring Boot 3, Spring Security, JPA/PostgreSQL, Flyway, JWT, Testcontainers +- **Build** : Gradle (Groovy DSL), exécuté dans un container Podman (`eclipse-temurin:25-jdk`) +- **Git remote** : `ssh://git@git.goutailler-olivier.com:2222/Gato/Olhar-API.git` +- **Frontend consommateur** : `Olhar-PWA` — dépôt `Gato/Olhar` sur Gitea + +## Commandes + +```bash +# Compiler +podman run --rm -v $(pwd):/workspace:Z -w /workspace eclipse-temurin:25-jdk ./gradlew compileJava --no-daemon + +# Tests +podman run --rm -v $(pwd):/workspace:Z -w /workspace eclipse-temurin:25-jdk ./gradlew test --no-daemon + +# Package +podman run --rm -v $(pwd):/workspace:Z -w /workspace eclipse-temurin:25-jdk ./gradlew bootJar --no-daemon + +# Démarrer en dev (BDD via docker-compose) +docker compose up +``` + +## Règles + +Les règles spécifiques au projet sont dans `.claude/rules/` : + +- **[Architecture](.claude/rules/architecture.md)** — couches hexagonales, règle de dépendance, interdictions +- **[Tests](.claude/rules/tests.md)** — JUnit 5, JaCoCo ≥ 90%, Testcontainers, Podman +- **[Commits & versions](.claude/rules/commits-versions.md)** — scopes Conventional Commits, versioning dans `build.gradle` +- **[Documentation](.claude/rules/documentation.md)** — wiki Olhar-API, specs fonctionnelle et technique +- **[Intégration webapp](.claude/rules/webapp-integration.md)** — contrat d'API, issues Olhar-PWA, feature flags +- **[Issues Gitea](.claude/rules/issues-gitea.md)** — suivi du projet, dépôts Olhar-API et Olhar + +Les règles workspace partagées (build Java/Podman, commits, wiki, tests) sont dans `../.claude/rules/`. + +## Skills à utiliser + +| Skill | Doc | Quand l'utiliser | +|-------|-----|-----------------| +| `/gitea-issue` | [~/.claude/commands/gitea-issue.md](/home/Gato/.claude/commands/gitea-issue.md) | Lire, créer, commenter ou fermer une issue sur Olhar-API ou Olhar-PWA | +| `/create-issue` | [~/.claude/commands/create-issue.md](/home/Gato/.claude/commands/create-issue.md) | Créer rapidement une issue avec titre + description | +| `/code-review` | skill natif Claude Code | Relire un diff avant commit ou pour une PR | +| `/verify` | skill natif Claude Code | Vérifier qu'un endpoint fonctionne après implémentation | +| `/run` | skill natif Claude Code | Démarrer l'application et observer son comportement réel | diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..730b0ec --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM eclipse-temurin:25-jdk AS builder +WORKDIR /workspace + +COPY gradlew . +COPY gradle/ gradle/ +COPY build.gradle settings.gradle gradle.properties ./ +RUN ./gradlew dependencies --no-daemon -q + +COPY src/ src/ +RUN ./gradlew bootJar --no-daemon -x test + +FROM eclipse-temurin:25-jdk AS runtime +WORKDIR /app + +COPY --from=builder /workspace/build/libs/olhar-api.jar app.jar + +EXPOSE 8080 +ENTRYPOINT ["java", "-jar", "app.jar"] diff --git a/build.gradle b/build.gradle index 0c40928..daa73a8 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { } group = 'com.olhar' -version = '0.0.1-SNAPSHOT' +version = '0.0.1' java { toolchain {