Aller au contenu principal

CI/CD

Objectifs

  • Estimer son travail
  • Ajouter des tests unitaires en Python
  • Créer une CI/CD pipeline sur GitLab

Rendu

  • Rapport individuel en Markdown à rendre avant le prochain cours
  • Délai: 2 semaines

Tâches

Estimer son travail

  • Estimer le temps nécessaire pour réaliser ce travail.
    • Découper le travail en tâches pour faciliter l'estimation.
  • Une fois terminé, comparer le temps estimé avec le temps réellement passé.
TâcheTemps estiméTemps passéCommentaire
Estimation10m15m...
............
Total2h1h30...

Git

  • Reprendre son projet GitLab du laboratoire précédent (DOP Python).
  • Travailler sur une nouvelle branche feature/04-cicd.
    • Faire une merge request (MR) sur main une fois terminé et demander une revue.
    • Une fois qu'une MR est acceptée, la merge sur main.
  • Séparer son travail en commits cohérents avec des messages de commit clairs et concis.

Tester le backend

  • Ajouter les dépendances de développement poetry add -G dev pytest pytest-cov httpx.
    • Une dépendance de développement est une dépendance qui n'est pas nécessaire en production, par exemple uniquement pour les tests.
    • pytest est le framework de test.
    • pytest-cov permet de générer un rapport de couverture de code.
    • httpx permet de faire des requêtes HTTP dans les tests.
  • Ajouter ou modifier les fichiers suivants (inspirés de cette documentation) :
/backend/backend/main.py
from os import getenv
from sys import modules

from fastapi import Depends, FastAPI, HTTPException
from sqlalchemy.orm import Session

from . import models, schemas
from .database import SessionLocal, engine

if "pytest" not in modules:
models.Base.metadata.create_all(bind=engine)

app = FastAPI(root_path=getenv("ROOT_PATH"))

...
  • Pour lancer les tests : poetry run pytest --cov

GitLab CI/CD

Créer une pipeline sur GitLab CI/CD qui :

  • a les 3 stages :
  • est déclenchée à chaque push sur n'importe quelle branche.
    • le stage deploy n'est exécuté que sur main.
  • Le frontend et le backend doivent être dans des jobs séparés et en parallèle.

Proposition

Beaucoup de changements sur la pipeline vont être testés, une manière d'éviter d'avoir plein de commit est d'en avoir qu'un seul au final (à éviter sur main ou develop) : git commit --amend --all --no-edit && git push --force-with-lease

Commencer par le frontend (commencer le script par cd frontend/) :

  • Le job build-frontend utilise l'image node:lts, exécute npm ci et npm run build.
    • Le résultat du build est gardé dans un artefact pour être utilisé par le job deploy-frontend.
    • Ajouter le cache.
  • Le job deploy-frontend utilise l'image docker avec le service docker:dind, exécute docker build -t ${CI_REGISTRY_IMAGE}/frontend:latest . et docker push ${CI_REGISTRY_IMAGE}/frontend:latest.
Solution .gitlab-ci.yml
build-frontend:
stage: build
image: node:lts
cache:
key:
files:
- frontend/package-lock.json
paths:
- frontend/.npm/
before_script:
- cd frontend/
script:
- npm ci --cache .npm --prefer-offline
- npm run build
artifacts:
paths:
- frontend/dist/

deploy-frontend:
stage: deploy
image: docker
services:
- docker:dind
dependencies:
- build-frontend
variables:
REGISTRY_IMAGE: ${CI_REGISTRY_IMAGE}/frontend
before_script:
- cd frontend/
- echo "$CI_REGISTRY_PASSWORD" | docker login $CI_REGISTRY --username $CI_REGISTRY_USER --password-stdin
script:
- docker pull $REGISTRY_IMAGE:latest || true
- docker build --cache-from $REGISTRY_IMAGE:latest -t $REGISTRY_IMAGE:latest .
- docker push $REGISTRY_IMAGE:latest

Puis le backend (similairement au frontend) :

  • Le job build-backend utilise l'image python:3.11, installe Poetry et les dépendances en les cachant pour les prochains jobs.
build-backend:
stage: build
image: python:3.11
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
cache:
paths:
- .cache/pip
- backend/.venv/
before_script:
- cd backend/
- pip install poetry
- poetry config virtualenvs.in-project true
script:
- poetry install
  • Le job test-backend reprend le cache du job build-backend et exécute poetry run pytest --cov.
test-backend:
stage: test
image: python:3.11
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
cache:
paths:
- .cache/pip
- backend/.venv/
before_script:
- cd backend/
- pip install poetry
- poetry config virtualenvs.in-project true
script:
- poetry run pytest --cov
Solution .gitlab-ci.yml
build-backend:
stage: build
image: python:3.11
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
cache:
paths:
- .cache/pip
- backend/.venv/
before_script:
- cd backend/
- pip install poetry
- poetry config virtualenvs.in-project true
script:
- poetry install

test-backend:
stage: test
image: python:3.11
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
cache:
paths:
- .cache/pip
- backend/.venv/
before_script:
- cd backend/
- pip install poetry
- poetry config virtualenvs.in-project true
script:
- poetry run pytest --cov --junitxml="rspec.xml" --cov-report term --cov-report xml:coverage.xml
coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
artifacts:
paths:
- backend/rspec.xml
reports:
junit: backend/rspec.xml
coverage_report:
coverage_format: cobertura
path: backend/coverage.xml

deploy-backend:
stage: deploy
image: docker
services:
- docker:dind
variables:
REGISTRY_IMAGE: ${CI_REGISTRY_IMAGE}/backend
before_script:
- cd backend/
- echo "$CI_REGISTRY_PASSWORD" | docker login $CI_REGISTRY --username $CI_REGISTRY_USER --password-stdin
script:
- docker pull $REGISTRY_IMAGE:latest || true
- docker build --cache-from $REGISTRY_IMAGE:latest -t $REGISTRY_IMAGE:latest .
- docker push $REGISTRY_IMAGE:latest

Références