Retour aux articles

Mettre en place une CI/CD avec GitLab pour des projets Full Stack Spring Boot et Angular

Mettre en place une CI/CD avec GitLab pour des projets Full Stack Spring Boot et Angular | Laty Gueye Samba - Développeur Full Stack Dakar Sénégal, Expert Java Spring Boot Angular
```html Mettre en place une CI/CD avec GitLab pour des projets Full Stack Spring Boot et Angular

Objectif : une CI/CD fiable pour un stack Spring Boot + Angular

Une approche CI/CD avec GitLab permet d’automatiser la compilation, les tests et le déploiement d’une application Full Stack composée d’un backend Spring Boot et d’un frontend Angular. L’objectif est de garantir des pipelines reproductibles, traçables et adaptés aux environnements (dev, staging, prod).

Architecture recommandée du dépôt

La structure ci-dessous aide à séparer clairement les responsabilités et à faciliter l’exécution des jobs en parallèle.

my-app/ backend/ pom.xml src/... frontend/ angular.json package.json src/... .gitlab-ci.yml README.md Dockerfile docker-compose.yml k8s/ deployment.yaml .env.example

Prérequis

GitLab et registres d’images

Le déploiement moderne repose généralement sur des conteneurs. Un registre d’images (GitLab Container Registry, ECR, etc.) doit être configuré, ainsi que les variables d’authentification dans GitLab.

Variables sensibles

Les secrets (ex. tokens, mots de passe base de données) doivent être stockés dans CI/CD Variables côté projet ou groupe. Par exemple :

  • DOCKER_REGISTRY
  • DOCKER_USERNAME
  • DOCKER_PASSWORD
  • SPRING_PROFILES_ACTIVE
  • SPRING_DATASOURCE_URL
  • SPRING_DATASOURCE_USERNAME
  • SPRING_DATASOURCE_PASSWORD

Modèle de pipeline GitLab : étapes et bonnes pratiques

Un pipeline robuste se découpe généralement en étapes : build, test, package, docker, puis deploy. La séparation backend/front permet d’optimiser le temps de calcul.

Exemple complet de .gitlab-ci.yml

Le modèle ci-dessous illustre une configuration typique. Les images de base et les commandes doivent être ajustées selon les versions (JDK, Node, Angular CLI, Maven/Gradle).

stages: - lint - test - build - docker - deploy variables: MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository" NODE_ENV: "production" APP_VERSION: "$CI_COMMIT_SHORT_SHA" cache: key: "$CI_PROJECT_NAME" paths: - .m2/repository/ - frontend/node_modules/ - frontend/.angular/cache/ # ----------------------------- # Backend: Lint + Tests # ----------------------------- backend_test: stage: test image: maven:3.9-eclipse-temurin-17 rules: - changes: - backend/**/* script: - cd backend - mvn -B -ntp test # ----------------------------- # Frontend: Lint + Tests # ----------------------------- frontend_test: stage: test image: node:20 rules: - changes: - frontend/**/* script: - cd frontend - npm ci - npm run lint --if-present - npm test -- --watch=false --browsers=ChromeHeadless # ----------------------------- # Build backend (jar) # ----------------------------- backend_build: stage: build image: maven:3.9-eclipse-temurin-17 needs: ["backend_test"] rules: - changes: - backend/**/* script: - cd backend - mvn -B -ntp package -DskipTests artifacts: paths: - backend/target/*.jar expire_in: 1 week # ----------------------------- # Build frontend (dist) # ----------------------------- frontend_build: stage: build image: node:20 needs: ["frontend_test"] rules: - changes: - frontend/**/* script: - cd frontend - npm ci - npm run build -- --configuration production artifacts: paths: - frontend/dist/ expire_in: 1 week # ----------------------------- # Docker: images backend + frontend (option) # ----------------------------- docker_backend: stage: docker image: docker:27 services: - name: docker:27-dind command: ["--tls=false"] needs: ["backend_build"] rules: - if: '$CI_COMMIT_BRANCH == "main"' script: - docker login -u "$DOCKER_USERNAME" -p "$DOCKER_PASSWORD" "$DOCKER_REGISTRY" - docker build -t "$DOCKER_REGISTRY/$CI_PROJECT_PATH/backend:$APP_VERSION" -f Dockerfile.backend . - docker push "$DOCKER_REGISTRY/$CI_PROJECT_PATH/backend:$APP_VERSION" docker_frontend: stage: docker image: docker:27 services: - name: docker:27-dind command: ["--tls=false"] needs: ["frontend_build"] rules: - if: '$CI_COMMIT_BRANCH == "main"' script: - docker login -u "$DOCKER_USERNAME" -p "$DOCKER_PASSWORD" "$DOCKER_REGISTRY" - docker build -t "$DOCKER_REGISTRY/$CI_PROJECT_PATH/frontend:$APP_VERSION" -f Dockerfile.frontend . - docker push "$DOCKER_REGISTRY/$CI_PROJECT_PATH/frontend:$APP_VERSION" # ----------------------------- # Deploy (exemple sur environnement) # ----------------------------- deploy_staging: stage: deploy image: alpine:3.20 needs: ["docker_backend", "docker_frontend"] rules: - if: '$CI_COMMIT_BRANCH == "main"' environment: name: staging url: https://staging.example.com script: - echo "Déploiement sur staging via outil (kubectl/helm/ssh) ..." # Exemples possibles: # - utiliser kubectl/helm # - mettre à jour un chart Helm # - exécuter un script de déploiement via SSH

Dockerfiles typiques

Dockerfile.backend (Spring Boot)

La stratégie recommandée consiste à produire un JAR, puis à créer une image légère (ex. base JRE). Le snippet ci-dessous illustre un pattern standard.

# Dockerfile.backend FROM eclipse-temurin:17-jre WORKDIR /app COPY backend/target/*.jar app.jar EXPOSE 8080 ENTRYPOINT ["java","-jar","/app/app.jar"]

Dockerfile.frontend (Angular)

Le frontend Angular est empaqueté en statique (répertoire dist) et servi par un serveur web.

# Dockerfile.frontend FROM nginx:alpine COPY frontend/dist/ /usr/share/nginx/html EXPOSE 80

Déploiement : stratégies à envisager

Pour un déploiement cohérent, plusieurs options existent : Kubernetes (Helm/Kustomize), Docker Compose sur une VM, ou encore déploiement via plateformes PaaS.

Déploiement sur Kubernetes (recommandé en entreprise)

Le pipeline peut mettre à jour les images dans les manifests (ou via Helm), puis déclencher un déploiement contrôlé : rollouts, readiness probes, et gestion des versions.

Déploiement sur VM via SSH (plus simple)

Le pipeline exécute un script distant qui : pull les images, redémarre les services et valide un endpoint. Cette approche reste valable pour des environnements légers.

Gestion des environnements et des branches

Une convention fréquente : main → staging (ou prod selon politique), release/* → production, merge requests → exécution du pipeline de validation sans déploiement.

Des règles rules dans GitLab permettent de conditionner les jobs à la présence de changements dans backend/** ou frontend/**, tout en réduisant les exécutions inutiles.

Qualité : lint, coverage, artefacts et notifications

Lint et tests de bout en bout

Les jobs de lint minimisent les erreurs précoces. Les tests E2E (ex. Playwright/Cypress) peuvent être ajoutés en option, exécutés uniquement sur merge requests.

Artefacts

Les artefacts (jar, dist) permettent d’inspecter rapidement les résultats et de tracer ce qui a été construit. Leur durée (expire_in) doit être raisonnable.

Reporting

GitLab peut intégrer des rapports (JUnit pour Maven/Surefire, coverage, etc.) afin de rendre la qualité visible dans l’interface.

Contrats API et intégration frontend/backend

Une approche efficace consiste à : valider la compatibilité via tests contractuels (ex. OpenAPI), ou à utiliser un pipeline qui génère le client/les modèles à partir des schémas. Cela réduit les risques de décalage entre backend et frontend.

Conclusion

Mettre en place une CI/CD avec GitLab pour Spring Boot et Angular consiste à : séparer les builds, enchaîner les tests, packager en artefacts, puis déployer de manière contrôlée (images Docker, Kubernetes/VM). Une configuration basée sur des règles de changement et des environnements clairs améliore fortement la fiabilité et la maintenabilité.

À propos de l'auteur

Laty Gueye Samba est développeur Full Stack basé à Dakar, Sénégal. Spécialiste des écosystèmes Java / Spring Boot et Angular.

Contact : latygueyesamba@gmail.com  |  Dakar, Sénégal