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

  • GitHub Classroom : https://classroom.github.com/a/s6fNQH2c
    • Rapport individuel en Markdown à rendre avant le prochain cours
    • Nom du fichier : report.md à la racine du répertoire
  • Devoir sur Cyberlearn : mettre le lien de la pull request GitLab dans le champ texte
  • Délai: 2 semaines

Tâches

Estimer son travail

  • Estimez le temps nécessaire pour réaliser ce laboratoire
    • Découpez le travail en tâches pour faciliter l'estimation
  • Lorsque vous aurez terminé le laboratoire, comparez le temps estimé avec le temps réellement passé
TâcheTemps estiméTemps réelCommentaire
Estimation10m15m...
............
Total2h1h30...

Git

  • Reprenez votre projet sur GitLab du laboratoire précédent (HEIG-VD DevOps)
  • Mettez tout votre travail sur une branche feature/04-cicd et faites une merge request (MR) sur main en m'ajoutant comme reviewer
  • Séparez votre travail en commits cohérents avec des messages de commit clairs et concis

Tester le backend

  • Ajoutez 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
  • Ajoutez/modifier les fichiers suivants (inspiré 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éez 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

Vous allez devoir tester beaucoup de changements sur la pipeline, une manière d'éviter d'avoir plein de commit est d'en utiliser qu'un seul (ne pas le faire sur main ou develop !) : git commit --amend --all --no-edit && git push --force-with-lease

Commencez par le frontend (commencez vos scripts 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 artéfact pour être utilisé par le job deploy-frontend
    • Ajoutez 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