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​