diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..f479f49 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,25 @@ +name: Build + +# Verify that the project compiles on every PR and push. +# One focused workflow per CI concern (build, test, lint) instead of a +# monolithic ci.yml (see issue #49, closed as wontfix). Test compilation +# and execution live in test.yml. + +on: + pull_request: + branches: [dev] + push: + branches: [dev, main] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "21" + cache: maven + - name: Compile the project + run: ./mvnw -B -ntp compile diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..6805d49 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,35 @@ +name: Lint + +# Code quality check (Checkstyle, Google ruleset) on every PR and push. +# Deliberately non-blocking on day one for an existing codebase: +# - checkstyle:checkstyle only generates a report, it never fails on violations +# - continue-on-error keeps even setup errors from gating merges +# The report is uploaded as a workflow artifact for review. +# Tighten later: switch the goal to checkstyle:check and set +# failOnViolation to true in pom.xml, then drop continue-on-error. + +on: + pull_request: + branches: [dev] + push: + branches: [dev, main] + +jobs: + checkstyle: + runs-on: ubuntu-latest + continue-on-error: true + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "21" + cache: maven + - name: Generate the Checkstyle report + run: ./mvnw -B -ntp checkstyle:checkstyle + - name: Upload the Checkstyle report + if: always() + uses: actions/upload-artifact@v4 + with: + name: checkstyle-report + path: target/checkstyle-result.xml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..4f26764 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,34 @@ +name: Tests + +# Run the test suite (Surefire, all *Test classes) on every PR and push. +# Integration-style tests provision a real PostgreSQL via Testcontainers, +# which uses the Docker daemon built into ubuntu-latest runners. +# No .env file is needed: @ServiceConnection wires the datasource to the +# container and the tests exclude the MongoDB autoconfiguration. + +on: + pull_request: + branches: [dev] + push: + branches: [dev, main] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "21" + cache: maven + - name: Run the test suite + run: ./mvnw -B -ntp test + # JaCoCo is bound to the test phase, so the run above already + # produced the coverage report (HTML and XML). + - name: Upload the coverage report + if: always() + uses: actions/upload-artifact@v4 + with: + name: jacoco-coverage-report + path: target/site/jacoco/ diff --git a/GLOSSAIRE.md b/GLOSSAIRE.md new file mode 100644 index 0000000..00d7073 --- /dev/null +++ b/GLOSSAIRE.md @@ -0,0 +1,202 @@ +# Glossaire + +Définitions des termes métier et techniques utilisés dans le projet learn-dev. +Pour les outils concrets et leurs versions, voir [docs/tech-stacks.md](docs/tech-stacks.md) ; +pour l'articulation des composants, voir [ARCHITECTURE.md](ARCHITECTURE.md) ; +pour la justification des décisions de conception, voir les [ADR](docs/adr/README.md). + +> [!NOTE] +> 🇬🇧 English version: [GLOSSARY.md](GLOSSARY.md). +> Les deux fichiers sont la traduction l'un de l'autre : toute entrée ajoutée, +> modifiée ou supprimée dans l'un doit l'être aussi dans l'autre. + +## Termes métier + +- **Archive (archiver)** — Dépublier un cours ou une leçon pour qu'il ne soit + plus accessible aux étudiants, sans le supprimer. +- **Course (cours)** — Une unité de contenu pédagogique appartenant à un + formateur ; contient des leçons. +- **Deactivate (désactiver)** — Neutraliser un compte (formateur ou étudiant, + par exemple) pour qu'il ne puisse plus être utilisé, sans le supprimer. + Voir aussi *compte désactivé*. +- **Drop a course (abandonner un cours)** — Le retrait d'un étudiant d'un cours + avant de l'avoir terminé. +- **Enrollment (inscription)** — La relation qui lie un étudiant à un cours + qu'il a rejoint. +- **Lesson (leçon)** — Un élément de contenu individuel au sein d'un cours. +- **Role (rôle)** — Un ensemble nommé de permissions accordées à un + utilisateur. Les rôles fournis par défaut sont `STUDENT`, `INSTRUCTOR` et + `ADMIN` ; `SUPERADMIN` est prévu (voir l'issue #65). + +## Authentification et sécurité + +- **Authority (autorité)** — Dans Spring Security, une permission unitaire + détenue par un utilisateur authentifié. Les rôles sont représentés comme des + autorités préfixées par `ROLE_` (le rôle `ADMIN` devient l'autorité + `ROLE_ADMIN`). +- **BCrypt** — Une fonction de hachage de mots de passe adaptative. Les mots de + passe sont stockés sous forme de hachés BCrypt, jamais en clair. +- **CSRF (Cross-Site Request Forgery)** — Une attaque qui pousse le navigateur + d'un utilisateur connecté à soumettre une requête à son insu. Contrée par un + jeton par formulaire (injecté par Thymeleaf) et l'attribut de cookie + `SameSite`. +- **Compte désactivé (disabled account)** — Un compte qui existe mais n'est pas + autorisé à s'authentifier (issu du drapeau `is_active = false`). À distinguer + d'un *compte verrouillé*. +- **HttpOnly** — Un attribut de cookie qui masque le cookie au JavaScript côté + client, ce qui limite le vol de session via XSS. +- **IDOR (Insecure Direct Object Reference)** — Une faille de contrôle d'accès + où un identifiant fourni par le client est utilisé sans vérification + d'autorisation. Les clés primaires UUID des utilisateurs limitent + l'énumération (voir [ADR-0003](docs/adr/0003-uuid-pk-for-users-bigint-elsewhere.md)). +- **Compte verrouillé (locked account)** — Un compte temporairement empêché de + s'authentifier (par exemple après trop d'échecs de connexion), issu du + drapeau `is_locked`. À distinguer d'un *compte désactivé*. +- **Principal** — L'entité actuellement authentifiée (en général l'utilisateur) + dans un contexte de sécurité. +- **SameSite** — Un attribut de cookie qui contrôle l'envoi du cookie par le + navigateur sur les requêtes inter-sites. Positionné sur `Lax` ici comme + défense en profondeur contre le CSRF. +- **Secure (cookie)** — Un attribut de cookie qui restreint le cookie au HTTPS. + Activé seulement lorsque l'application sera servie en TLS. +- **Session (côté serveur)** — L'état d'authentification conservé sur le + serveur et référencé par un cookie de session (`JSESSIONID`), plutôt qu'un + jeton autoporteur (voir [ADR-0001](docs/adr/0001-use-server-side-sessions-over-jwt.md)). +- **XSS (Cross-Site Scripting)** — L'injection de scripts malveillants dans des + pages vues par d'autres utilisateurs. Limitée par l'échappement automatique + de Thymeleaf et par `HttpOnly`. + +## Persistance et modélisation des données + +- **Changelog / Changeset (Liquibase)** — Un changelog est la liste ordonnée + des migrations ; un changeset est une migration atomique, identifiée par + `path::id::author`. +- **ERD (Entity-Relationship Diagram)** — Un diagramme des entités et de leurs + relations (produit ici avec Mermaid). +- **Hibernate** — L'implémentation JPA (ORM) utilisée pour faire correspondre + les entités Java aux tables. +- **JPA (Jakarta Persistence API)** — L'API Java standard du mapping + objet-relationnel ; implémentée par Hibernate. +- **JSESSIONID** — Le nom par défaut du cookie de session servlet. +- **Liquibase** — L'outil de migration du schéma de base de données. Les + migrations sont des fichiers SQL formatés écrits à la main et appliqués au + démarrage (voir [ADR-0005](docs/adr/0005-handwrite-liquibase-migrations-over-mcd-ddl.md)). +- **Merise** — Une méthode française de modélisation des données produisant + trois vues : MCD, MLD, MPD. +- **MCD (Modèle Conceptuel de Données)** — Le modèle conceptuel ; les entités + et relations, indépendamment de toute base de données. +- **MLD (Modèle Logique des Données)** — Le modèle logique ; le schéma + relationnel (tables, clés) dérivé du MCD. +- **MPD (Modèle Physique des Données)** — Le modèle physique ; le schéma + concret tel qu'implémenté dans PostgreSQL. +- **ORM (Object-Relational Mapping)** — La correspondance entre objets Java et + tables relationnelles ; assurée par Hibernate/JPA. +- **UUID** — Un identifiant sur 128 bits utilisé comme clé primaire des + utilisateurs pour éviter l'énumération d'identifiants séquentiels. + +## Build, tests et outillage + +- **ADR (Architecture Decision Record)** — Un document court, numéroté et en + ajout seul qui capture une décision de conception et ses compromis, au + format MADR. +- **Bean Validation** — Le standard Jakarta de déclaration de contraintes + (`@NotBlank`, `@Email`, `@Size`) sur les champs de formulaires/DTO, + appliquées avec `@Valid`. +- **Checkstyle** — Un outil d'analyse statique qui vérifie les sources Java + contre un référentiel de style. Exécuté ici avec le référentiel Google + fourni (`google_checks.xml`) en mode rapport seul (voir + [ADR-0011](docs/adr/0011-start-ci-quality-checks-as-advisory-reports.md)). +- **Code coverage (couverture de code)** — Le pourcentage de code exercé par + la suite de tests. Mesuré ici par JaCoCo ; rapporté, sans seuil imposé pour + l'instant. +- **DTO (Data Transfer Object)** — Un objet qui transporte des données à + travers une frontière, volontairement distinct des entités. Un DTO `...Form` + porte un formulaire HTML. +- **Failsafe** — Le plugin Maven qui exécute les tests d'intégration `*IT` + dans la phase `verify`. Ce projet ne l'utilise **pas** (voir + [ADR-0009](docs/adr/0009-run-tests-under-surefire-not-failsafe.md)). +- **FIFO (tube nommé)** — Un fichier spécial qui transmet les données à la + lecture. Le `.env` du projet est une FIFO remplie par 1Password ; le + `source` du shell ne peut pas la lire (taille nulle au `stat`). +- **HikariCP** — Le pool de connexions JDBC fourni avec Spring Boot. +- **Test d'intégration (integration test)** — Un test qui démarre un contexte + Spring et exerce plusieurs couches ensemble (ici `@SpringBootTest` contre un + vrai conteneur Postgres). +- **JaCoCo (Java Code Coverage)** — L'outil de couverture de code pour Java. + Son plugin Maven instrumente les tests (`prepare-agent`) et écrit un rapport + HTML/XML dans `target/site/jacoco/` pendant la phase `test` ; la CI le + publie comme artefact de workflow. +- **Linter** — Un outil qui signale les problèmes de style et de qualité dans + le code source sans l'exécuter (analyse statique). Le linter du projet est + Checkstyle. +- **Lombok** — Une bibliothèque qui génère le code répétitif (accesseurs, + constructeurs) à partir d'annotations, à la compilation. +- **MADR (Markdown ADR)** — Le format léger de modèle d'ADR utilisé dans + `docs/adr/`. +- **Maven Wrapper (`mvnw`)** — Un script de lancement versionné qui télécharge + et exécute la version de Maven épinglée par le projet, pour que les builds + ne dépendent pas d'un Maven installé localement (utilisé par la CI : + `./mvnw -B -ntp ...`). +- **Slice test (test de tranche)** — Un test qui ne charge qu'une couche du + contexte (par exemple `@DataJpaTest` pour la couche de persistance). +- **Smoke test (test de fumée)** — Un test minimal vérifiant que le contexte + de l'application démarre (`LearnDevApplicationTests`). +- **Surefire** — Le plugin Maven qui exécute les tests `*Test`/`*Tests` + (unitaires et d'intégration) dans la phase `test`. Tous les tests du projet + passent par Surefire. +- **Testcontainers** — Une bibliothèque qui démarre des conteneurs + Docker/Podman jetables pour les tests ; utilisée pour exécuter un vrai + PostgreSQL (voir [ADR-0006](docs/adr/0006-test-against-real-postgres-testcontainers.md)). +- **Ryuk** — Le conteneur compagnon de Testcontainers qui nettoie les + ressources ; désactivé sous Podman dans ce projet. +- **YAGNI (You Aren't Gonna Need It)** — Le principe de ne pas construire une + fonctionnalité avant d'en avoir réellement besoin (par exemple le report du + rôle `SUPERADMIN`). + +## Infrastructure et processus + +- **Advisory check (contrôle consultatif)** — Un contrôle de CI qui signale + les problèmes sans bloquer la fusion (goal en rapport seul et/ou + `continue-on-error`). Le lint et la couverture démarrent en mode consultatif + ici (voir [ADR-0011](docs/adr/0011-start-ci-quality-checks-as-advisory-reports.md)). +- **CI (intégration continue)** — Construire et tester automatiquement chaque + changement (chaque PR et chaque push) pour détecter les régressions au plus + tôt. Mise en oeuvre avec GitHub Actions (issues #45 à #48). +- **Docker Compose** — L'orchestration déclarative de plusieurs conteneurs ; + fait tourner ici Postgres et Mongo. Sur la machine de développement, + `docker` est Podman. +- **GitButler** — L'outil de gestion de versions qui enveloppe Git ; utilisé + via la CLI `but` quand la branche courante est `gitbutler/workspace`. +- **GitHub Actions** — Le service de CI de GitHub. Chaque workflow est un + fichier YAML sous `.github/workflows/` ; ce projet utilise un workflow ciblé + par préoccupation (voir [ADR-0010](docs/adr/0010-structure-ci-as-focused-workflows-per-concern.md)). +- **Podman** — Un moteur de conteneurs sans démon, utilisé comme remplaçant de + `docker`. +- **Runner (exécuteur)** — La machine qui exécute un job GitHub Actions + (`ubuntu-latest` ici) ; elle embarque un démon Docker, que Testcontainers + utilise directement. +- **Profil Spring (Spring profile)** — Un jeu de configuration nommé (par + exemple `dev`) qui sélectionne des propriétés spécifiques et des contextes + Liquibase. +- **Temurin** — La distribution Eclipse Adoptium de l'OpenJDK ; le build + Java 21 utilisé en local (via SDKMAN) et sur la CI (via + `actions/setup-java`). +- **Thymeleaf** — Le moteur de templates HTML côté serveur. Son **dialecte** + Spring Security (espace de noms `sec:`) expose l'utilisateur authentifié aux + templates. +- **Workflow (GitHub Actions)** — Un fichier YAML qui déclare quand + (déclencheurs) et comment (jobs, étapes) la CI s'exécute. Ce projet contient + `build.yml`, `test.yml`, `lint.yml` et `schema-drift.yml`. +- **Workflow artifact (artefact de workflow)** — Un fichier ou dossier publié + depuis une exécution de workflow et téléchargeable depuis la page de + l'exécution (ici : le rapport XML de Checkstyle et les rapports JaCoCo). + +## Certification + +- **CCP (Certificat de Compétences Professionnelles)** — Un bloc de + compétences d'un Titre Professionnel français ; le DWWM comprend un CCP + front-end et un CCP back-end. +- **DWWM (Développeur Web et Web Mobile)** — Le Titre Professionnel français + visé par ce projet de fin de formation. +- **REAC (Référentiel Emploi Activités Compétences)** — Le référentiel + officiel de compétences qui définit ce que la certification évalue. diff --git a/GLOSSARY.md b/GLOSSARY.md index 6ed219e..96d7fe2 100644 --- a/GLOSSARY.md +++ b/GLOSSARY.md @@ -5,6 +5,11 @@ project. For the concrete tools and versions, see [docs/tech-stacks.md](docs/tec for how the pieces fit together, see [ARCHITECTURE.md](ARCHITECTURE.md); for the rationale behind design decisions, see the [ADRs](docs/adr/README.md). +> [!NOTE] +> 🇫🇷 French version: [GLOSSAIRE.md](GLOSSAIRE.md). +> The two files are translations of each other: when you add, change, or +> remove an entry in one, apply the same change to the other. + ## Domain terms - **Archive** — Unpublish a course or lesson so it is no longer available to @@ -80,6 +85,11 @@ rationale behind design decisions, see the [ADRs](docs/adr/README.md). capturing one design decision and its trade-offs, in MADR format. - **Bean Validation** — The Jakarta standard for declaring constraints (`@NotBlank`, `@Email`, `@Size`) on form/DTO fields, enforced with `@Valid`. +- **Checkstyle** — A static-analysis tool that checks Java source against a + style ruleset. Runs here with the bundled Google ruleset (`google_checks.xml`) + in report-only mode (see [ADR-0011](docs/adr/0011-start-ci-quality-checks-as-advisory-reports.md)). +- **Code coverage** — The percentage of code exercised by the test suite. + Measured here by JaCoCo; reported, not yet enforced as a threshold. - **DTO (Data Transfer Object)** — An object carrying data across a boundary, deliberately separate from entities. A `...Form` DTO backs an HTML form. - **Failsafe** — The Maven plugin that runs `*IT` integration tests in the `verify` @@ -89,9 +99,17 @@ rationale behind design decisions, see the [ADRs](docs/adr/README.md). - **HikariCP** — The JDBC connection pool bundled with Spring Boot. - **Integration test** — A test that boots a Spring context and exercises multiple layers together (here `@SpringBootTest` against a real Postgres container). +- **JaCoCo (Java Code Coverage)** — The code-coverage tool for Java. Its Maven + plugin instruments the tests (`prepare-agent`) and writes an HTML/XML report to + `target/site/jacoco/` during the `test` phase; CI uploads it as a workflow artifact. +- **Linter** — A tool that flags style and quality issues in source code without + running it (static analysis). The project's linter is Checkstyle. - **Lombok** — A library that generates boilerplate (getters, constructors) from annotations at compile time. - **MADR (Markdown ADR)** — The lightweight ADR template format used in `docs/adr/`. +- **Maven Wrapper (`mvnw`)** — A committed launcher script that downloads and runs + the project's pinned Maven version, so builds do not depend on a locally + installed Maven (used by CI: `./mvnw -B -ntp ...`). - **Slice test** — A test that loads only one layer of the context (for example `@DataJpaTest` for the persistence layer). - **Smoke test** — A minimal test that the application context starts at all @@ -107,15 +125,33 @@ rationale behind design decisions, see the [ADRs](docs/adr/README.md). ## Infrastructure and process +- **Advisory check** — A CI check that reports problems without blocking the + merge (report-only goal and/or `continue-on-error`). Linting and coverage + start advisory here (see [ADR-0011](docs/adr/0011-start-ci-quality-checks-as-advisory-reports.md)). +- **CI (Continuous Integration)** — Automatically building and testing every + change (each PR and push) to catch regressions early. Implemented with + GitHub Actions (issues #45 to #48). - **Docker Compose** — Declarative multi-container orchestration; here it runs Postgres and Mongo. `docker` on the dev machine is Podman. - **GitButler** — The version-control tool wrapping Git; used via the `but` CLI when the current branch is `gitbutler/workspace`. +- **GitHub Actions** — GitHub's CI service. Each workflow is a YAML file under + `.github/workflows/`; this project uses one focused workflow per concern + (see [ADR-0010](docs/adr/0010-structure-ci-as-focused-workflows-per-concern.md)). - **Podman** — A daemonless container engine, used as the `docker` drop-in. +- **Runner** — The machine that executes a GitHub Actions job (`ubuntu-latest` + here); it ships with a Docker daemon, which Testcontainers uses directly. - **Spring profile** — A named configuration set (for example `dev`) selecting profile-specific properties and Liquibase contexts. +- **Temurin** — The Eclipse Adoptium distribution of the OpenJDK; the Java 21 + build used locally (via SDKMAN) and on CI (via `actions/setup-java`). - **Thymeleaf** — The server-side HTML template engine. Its Spring Security **dialect** (`sec:` namespace) exposes the authenticated user to templates. +- **Workflow (GitHub Actions)** — A YAML file declaring when (triggers) and how + (jobs, steps) CI runs. This project has `build.yml`, `test.yml`, `lint.yml`, + and `schema-drift.yml`. +- **Workflow artifact** — A file or folder uploaded from a workflow run and + downloadable from the run page (here: the Checkstyle XML and JaCoCo reports). ## Certification diff --git a/README.md b/README.md index 512e996..4991878 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,11 @@ + + +[![build status][build-image]][build-url] +[![test status][test-image]][test-url] +[![lint status][lint-image]][lint-url] +[![schema drift status][schema-drift-image]][schema-drift-url] +[![github issues][github-issues-image]][github-issues-url] + # Learn-dev: An Interactive Programming Learning Platform ## Presentation @@ -367,7 +375,8 @@ visit [this link](https://github.com/users/ebouchut/projects/7/views/3). - [ARCHITECTURE.md](ARCHITECTURE.md) — how the pieces fit together (layers, request flow, authentication, data, testing). - [docs/tech-stacks.md](docs/tech-stacks.md) — catalogue of tools, languages, and frameworks with versions used in the project. -- [GLOSSARY.md](GLOSSARY.md) — definitions of the domain and technical terms used across the project. +- [GLOSSARY.md](GLOSSARY.md) — definitions of the domain and technical terms used across the project + (🇫🇷 French version: [GLOSSAIRE.md](GLOSSAIRE.md)). - [Architecture Decision Records](docs/adr/README.md) — A list of design decisions and their trade-offs. @@ -430,3 +439,16 @@ See [LICENSE](LICENSE) for details. - [Aubry Capitone](https://www.linkedin.com/in/a-capitone/) - [Esteban Bare](https://www.linkedin.com/in/esteban-bare-337927284/), - [REAC Developpeur Web et Web mobile](https://www.francecompetences.fr/recherche/rncp/37674/) + + + +[build-image]: https://github.com/ebouchut/learn-dev/actions/workflows/build.yml/badge.svg?branch=dev&event=push +[build-url]: https://github.com/ebouchut/learn-dev/actions/workflows/build.yml +[test-image]: https://github.com/ebouchut/learn-dev/actions/workflows/test.yml/badge.svg?branch=dev&event=push +[test-url]: https://github.com/ebouchut/learn-dev/actions/workflows/test.yml +[lint-image]: https://github.com/ebouchut/learn-dev/actions/workflows/lint.yml/badge.svg?branch=dev&event=push +[lint-url]: https://github.com/ebouchut/learn-dev/actions/workflows/lint.yml +[schema-drift-image]: https://github.com/ebouchut/learn-dev/actions/workflows/schema-drift.yml/badge.svg?branch=dev&event=push +[schema-drift-url]: https://github.com/ebouchut/learn-dev/actions/workflows/schema-drift.yml +[github-issues-image]: https://img.shields.io/github/issues/ebouchut/learn-dev +[github-issues-url]: https://github.com/ebouchut/learn-dev/issues diff --git a/docs/adr/0010-structure-ci-as-focused-workflows-per-concern.md b/docs/adr/0010-structure-ci-as-focused-workflows-per-concern.md new file mode 100644 index 0000000..f2e6bb1 --- /dev/null +++ b/docs/adr/0010-structure-ci-as-focused-workflows-per-concern.md @@ -0,0 +1,57 @@ +# Structure CI as focused workflows per concern + +- Status: accepted +- Date: 2026-07-06 +- Deciders: Eric Bouchut + +## Context and Problem Statement + +The project needs continuous integration on GitHub Actions (issues #45 to #48): +build verification, test execution, linting, and coverage reporting. +Should all of this live in a single `ci.yml` workflow, or in separate workflow +files? Issue #49 (create a monolithic `ci.yml`) was closed as wontfix; this ADR +records the reasoning so the layout survives the issue tracker. + +## Decision Drivers + +- Readability of CI results on a PR (one status per concern vs one opaque status) +- Independent evolution (tighten linting without touching the test workflow) +- Consistency with the existing `schema-drift.yml` workflow +- Duplication cost (checkout and setup-java repeated per file) + +## Considered Options + +- A single monolithic `ci.yml` with multiple jobs +- One focused workflow file per concern (`build.yml`, `test.yml`, `lint.yml`) + +## Decision Outcome + +Chosen: "One focused workflow file per concern", because each PR check maps to +one small readable file, failures are identifiable at a glance from the check +name, and each workflow can change (or be made blocking) independently. +This matches the precedent set by `schema-drift.yml`. + +### Consequences + +- Good: PR checks read as Build / Tests / Lint / Schema drift; a red check + names its concern directly. +- Good: The advisory lint workflow (ADR-0011) can later become blocking + without touching build or test. +- Trade-off: The checkout + setup-java boilerplate is repeated in each file + (about 10 lines per workflow). +- Trade-off: Shared changes (bumping the Java version) must be applied in + several files. + +## Pros and Cons of the Options + +### Monolithic ci.yml + +- 👍 One file to maintain; shared setup written once +- 👎 One check status hides which concern failed; jobs still rerun setup each +- 👎 Any change risks the whole pipeline; advisory and blocking concerns are + entangled + +### One workflow per concern + +- 👍 Self-describing PR checks; independent lifecycles; matches `schema-drift.yml` +- 👎 Boilerplate repeated per file diff --git a/docs/adr/0011-start-ci-quality-checks-as-advisory-reports.md b/docs/adr/0011-start-ci-quality-checks-as-advisory-reports.md new file mode 100644 index 0000000..8f73c69 --- /dev/null +++ b/docs/adr/0011-start-ci-quality-checks-as-advisory-reports.md @@ -0,0 +1,61 @@ +# Start CI quality checks as advisory reports + +- Status: accepted +- Date: 2026-07-06 +- Deciders: Eric Bouchut + +## Context and Problem Statement + +CI adds linting (#47) and coverage (#48) to a codebase that has never been +linted and has no coverage baseline. Should these checks block merges from +day one, and should coverage go to an external service? A blocking linter on +an unlinted codebase fails every PR until a large style-only cleanup lands, +which stalls feature work. + +## Decision Drivers + +- Do not block feature PRs on pre-existing style debt +- Zero new config files and no external service accounts (solo project, + certification deadline) +- Keep a clear path to tighten later + +## Considered Options + +- Blocking checks from day one (Checkstyle `check` goal, coverage threshold + via `jacoco:check`, Codecov) +- Advisory reports first: Checkstyle report-only with the bundled + `google_checks.xml`, JaCoCo report bound to the test phase, both published + as workflow artifacts, no external service + +## Decision Outcome + +Chosen: "Advisory reports first", because it makes quality visible immediately +without gating merges on historical debt, adds zero config files (bundled +Google ruleset, plugin versions pinned in `pom.xml`: Checkstyle 3.6.0, +JaCoCo 0.8.15), and keeps secrets and accounts out of scope. The lint job also +sets `continue-on-error: true` so even setup errors stay advisory. + +### Consequences + +- Good: Every PR publishes a Checkstyle XML report and a JaCoCo HTML/XML + report as workflow artifacts. +- Good: `mvn test` locally now produces `target/site/jacoco/` with no extra + flags. +- Trade-off: Nothing enforces quality yet; violations can accumulate until + the gate is switched on. +- Follow-up: To tighten, switch the lint job to `checkstyle:check` with + `failOnViolation=true`, drop `continue-on-error`, and add a `jacoco:check` + minimum threshold. + +## Pros and Cons of the Options + +### Blocking from day one + +- 👍 Debt cannot grow +- 👎 Every PR is red until a big-bang style cleanup; discourages small PRs +- 👎 Codecov adds an account, a token, and an external dependency + +### Advisory reports first + +- 👍 Immediate visibility, zero friction, easy to tighten incrementally +- 👎 Relies on discipline until the gate exists diff --git a/docs/adr/README.md b/docs/adr/README.md index 9346b53..ca5a3fb 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -39,4 +39,6 @@ NNNN-short-title-in-kebab-case.md | [0006](0006-test-against-real-postgres-testcontainers.md) | Test the persistence layer against a real PostgreSQL (Testcontainers), not H2 | accepted | | [0007](0007-use-postgresql-over-mysql.md) | Use PostgreSQL as the relational database, not MySQL | accepted | | [0008](0008-share-singleton-testcontainers-postgres.md) | Share one Testcontainers PostgreSQL as a static singleton, not @Container | accepted | -| [0009](0009-run-tests-under-surefire-not-failsafe.md) | Run all tests under Surefire with the *Test suffix, not Failsafe/*IT | accepted | \ No newline at end of file +| [0009](0009-run-tests-under-surefire-not-failsafe.md) | Run all tests under Surefire with the *Test suffix, not Failsafe/*IT | accepted | +| [0010](0010-structure-ci-as-focused-workflows-per-concern.md) | Structure CI as focused workflows per concern, not a monolithic ci.yml | accepted | +| [0011](0011-start-ci-quality-checks-as-advisory-reports.md) | Start CI quality checks as advisory reports, gates come later | accepted | \ No newline at end of file diff --git a/docs/plans/2026-07-06-ci-test-pipeline.md b/docs/plans/2026-07-06-ci-test-pipeline.md new file mode 100644 index 0000000..c0a36c9 --- /dev/null +++ b/docs/plans/2026-07-06-ci-test-pipeline.md @@ -0,0 +1,108 @@ +# CI Test Pipeline Implementation Plan + +> **Status: executed** on 2026-07-06, branch `ci-test-pipeline`. +> This is the retrospective record of the plan as it was carried out. + +**Goal:** Give the project a GitHub Actions CI pipeline: verify the Maven +build (#45), run the test suite (#46), report code quality (#47), and publish +test coverage (#48) on every PR targeting `dev` and every push to `dev`/`main`. + +**Architecture:** One focused workflow file per concern instead of a +monolithic `ci.yml` (issue #49 was closed as wontfix; rationale recorded in +[ADR-0010](../adr/0010-structure-ci-as-focused-workflows-per-concern.md)). +Quality checks start as advisory reports, not gates +([ADR-0011](../adr/0011-start-ci-quality-checks-as-advisory-reports.md)). +All jobs run on `ubuntu-latest` with `actions/setup-java@v4` +(Temurin 21, `cache: maven`) and invoke the wrapper as `./mvnw -B -ntp`. + +**Key insight:** the tests need no `.env` on CI. Integration-style tests +extend `AbstractPostgresIT`, which provisions a real PostgreSQL 17 via +Testcontainers and wires the datasource with `@ServiceConnection` +(ADR-0006, ADR-0008); Mongo autoconfiguration is excluded in tests. The +Docker daemon built into GitHub's ubuntu runners is enough; the Podman +environment variables from the `Makefile` are local-only. + +--- + +## Version Control (GitButler) + +Same rules as every plan in this repository: the workspace is on +`gitbutler/workspace`, so all commits go through `but commit` (never +`git add`/`git commit`), and **nothing is pushed** by the agent; the user +reviews, pushes, and opens the PR targeting `dev`. + +--- + +## File Structure + +``` +.github/workflows/ +├── build.yml # Build: ./mvnw -B -ntp compile +├── test.yml # Tests: ./mvnw -B -ntp test + JaCoCo artifact upload +└── lint.yml # Lint: ./mvnw -B -ntp checkstyle:checkstyle (advisory) + +pom.xml # + maven-checkstyle-plugin 3.6.0 (report-only) + # + jacoco-maven-plugin 0.8.15 (prepare-agent, report@test) +``` + +--- + +## Task 1: Maven build verification (#45) + +- [x] Create `.github/workflows/build.yml`: checkout@v4, setup-java + (Temurin 21, Maven cache), `./mvnw -B -ntp compile` +- [x] Triggers: `pull_request: [dev]`, `push: [dev, main]` + (same as `schema-drift.yml`) +- [x] Commit `ci(build): Verify the Maven build on every PR and push` + (Fixes #45) => 9430e67 + +## Task 2: Test automation (#46) + +- [x] Create `.github/workflows/test.yml`: same setup, `./mvnw -B -ntp test` +- [x] Document in the workflow why no `.env` or Podman wiring is needed +- [x] Surefire only, all `*Test` classes; no Failsafe (ADR-0009) +- [x] Commit `ci(test): Run the test suite on every PR and push` + (Fixes #46) => 3650ab8 + +## Task 3: Code quality checks (#47) + +- [x] Add `maven-checkstyle-plugin` (pinned 3.6.0, not managed by the Boot + parent) to `pom.xml`: bundled `google_checks.xml`, `consoleOutput`, + `failOnViolation=false`, `failsOnError=false`, not bound to any phase + so `mvn test`/`mvn install` behavior is unchanged +- [x] Create `.github/workflows/lint.yml`: `checkstyle:checkstyle` + (report goal, never fails on violations), `continue-on-error: true` + on the job, upload `target/checkstyle-result.xml` as an artifact +- [x] Commit `ci(lint): Add a non-blocking Checkstyle report` + (Fixes #47) => bde8cc5 + +## Task 4: Test coverage reports (#48) + +- [x] Add `jacoco-maven-plugin` (pinned 0.8.15) to `pom.xml`: + `prepare-agent` execution plus `report` bound to the `test` phase, so + every `mvn test` produces `target/site/jacoco/` (HTML + XML) +- [x] Add an `actions/upload-artifact@v4` step (`if: always()`) to + `test.yml` publishing `target/site/jacoco/` +- [x] No external coverage service (no Codecov): artifacts only +- [x] Commit `ci(coverage): Add JaCoCo and upload the coverage report` + (Fixes #48) => 50f61d6 + +## Verification (performed) + +- [x] All three workflow files parse as valid YAML + (actionlint unavailable; parsed with a YAML library) +- [x] `make test` after the `pom.xml` changes: + `Tests run: 11, Failures: 0, Errors: 0, Skipped: 0` => BUILD SUCCESS +- [x] `target/site/jacoco/index.html` and `jacoco.xml` produced by the + test phase +- [x] `./mvnw -B -ntp checkstyle:checkstyle` => BUILD SUCCESS, + violations reported as warnings only +- [x] `but status`: exactly 4 commits on `ci-test-pipeline`, no unrelated + changes swept in + +## Deferred (deliberate) + +- Coverage threshold gate (`jacoco:check`): add when a baseline exists +- Blocking Checkstyle (`checkstyle:check`, `failOnViolation=true`, drop + `continue-on-error`): switch on once the existing violations are triaged +- Path filters on the workflows: build and tests should always run for now diff --git a/pom.xml b/pom.xml index 74210db..bc26698 100644 --- a/pom.xml +++ b/pom.xml @@ -55,6 +55,8 @@ 21 5.1.0 + 3.6.0 + 0.8.15 @@ -198,6 +200,53 @@ + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${maven-checkstyle-plugin.version} + + google_checks.xml + true + false + false + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} + + + prepare-agent + + prepare-agent + + + + report + test + + report + + + +