Aller au contenu
gaetancottrez.dev

Mettre en place une CI GitHub Actions

Published:le  à 22:00 | (11 min de lecture)
Mettre en place une CI GitHub Actions

Table des matières

Ouvrir table des matières

Introduction

Quand tu fais du code, tu as énormément de choses à penser et à faire. Certaines d’entre elles sont répétitives.

Imagine automatiser tes tests, tes déploiements, et même tes tâches administratives, le tout depuis le confort de ton propre dépôt GitHub.

Si cela te semble séduisant, tu es au bon endroit !

Je vais te guider pas à pas dans la configuration d’une CI (Continuous Integration) qui couvrira 80% de tes besoins avec un outil gratuit présent sur ton dépôt GitHub : GitHub Actions.

Comprendre GitHub Actions 🤖

GitHub Actions est un système d’automatisation intégré directement dans ton dépôt GitHub.

Le fonctionnement de GitHub Actions est assez simple : en suivant les instructions de ton workflow, un conteneur Docker est provisionné pour exécuter ton action, étape par étape.

Il te permet de créer, tester et déployer ton code dès que tu effectues des changements. Chaque “action” est définie dans un fichier YAML, situé dans le dossier .github/workflows de ton dépôt.

Tu peux avoir plusieurs fichiers YAML de workflow dans un même dépôt, par exemple un pour les tests et un autre pour le déploiement. Mais tu peux aussi regrouper tout cela dans un seul et même workflow.

GitHub Actions est très populaire, et de nombreux outils tiers ont développé des packages pour être utilisés dans GitHub Actions, facilitant ainsi l’intégration de ta stack avec aisance.

En explorant la documentation 🔗, tu verras que tu peux tout faire avec GitHub Actions.

Créons ton premier Workflow

Lire toute la documentation pour produire un workflow complet et efficace pour ton dépôt n’est peut-être pas très attrayant.

Je te propose donc un workflow plutôt complet pour lancer le linter, le build, les tests et le coverage.

Pour te montrer chaque étape, je vais me baser sur ce dépôt 🔗 fraîchement créé lors de mon mon guide sur les monorepo.

Création du dossier Workflows

Avant tout, il faut créer la structure de dossier pour accueillir un fichier de workflow. Localement, à la racine du dépôt, crée deux dossiers, puis le fichier de workflow :

mkdir .github
cd .github
mkdir workflows
cd workflows
touch ci.yml

Spécification des instructions dans le fichier YAML

Nous allons spécifier dans le fichier YAML les instructions que GitHub doit suivre. On commence par lui dire dans quelles conditions il doit s’exécuter :

on:
  - push
  - workflow_dispatch

Le workflow se déclenche dans deux cas :

Nom du workflow et concurrence

name: CI
concurrency:
  group: ${{ github.ref }}
  cancel-in-progress: true

Définition du Job

jobs:
  global:
    name: Lint, Build, Test & Analyze
    runs-on: ubuntu-latest
    steps:

Le job est intitulé “Lint, Build, Test & Analyze” et s’exécute sur l’environnement ubuntu-latest.

La ligne global: représente l’ID du job dans GitHub Actions. Il doit être unique dans le même workflow.

Les Étapes du Workflow

Chaque job est divisé en plusieurs étapes (la ligne steps:) :

Étape 1 : Setup Node.js

- name: ⚙️ Setup Node.js
  uses: actions/setup-node@v4
  with:
    node-version: 20

Cette étape installe Node.js version 20, nécessaire pour exécuter les commandes qui suivent. On utilise un package officiel de GitHub Actions que tu peux retrouver ici 🔗 pour connaître toutes les options de paramètrages.

Tu peux adapter le paramètre node-version pour ton dépôt si tu utilise une autre version.

Étape 2 : Installation de pnpm

- name: ⚙️ Install pnpm
  uses: pnpm/action-setup@v4
  with:
    version: 9
    run_install: false

Me basant sur ce dépôt 🔗 l’installation de Node n’est pas suffisante. En effet, le dépôt utilise PNPM, un gestionnaire de paquets alternatif à npm.

Cette étape va installé PNPM dans sa version 9.

La commande run_install est définie sur false, ce qui signifie que l’installation des dépendances sera effectuée manuellement plus tard parce que à cette étape, j’ai juste un container Node.js avec PNPM mais sans le dépôt.

Tu peux adapter le paramètre version pour ton dépôt si tu utilise une autre version de PNPM.

Si tu utilise que NPM, tu peux supprimer cette step.

Dans le cas où tu utilises Yarn, tu peux adapter cette step comme ça en adaptant le paramètre version pour ton dépôt :

- name: ⚙️ Install yarn
  uses: threeal/setup-yarn-action@v2.0.0 # package disponible https://github.com/threeal/setup-yarn-action
  with:
    version: latest

Étape 3 : Charger le Dépôt

- name: 🔄 Load repository
  uses: actions/checkout@master

Le dépôt est cloné sur l’environnement CI pour que les fichiers du projet soient disponibles pour les étapes suivantes.

Étape 4 : Création du Fichier d’Environnement

- name: "Create env.local file in apps/web"
  run: |
    touch .env.local
    echo MY_ENVIRONMENT_VAR=${{ secrets.MY_ENVIRONMENT_VAR }} >> .env.local
    echo NEXT_PUBLIC_MY_ENVIRONMENT_VAR=${{ secrets.NEXT_PUBLIC_MY_ENVIRONMENT_VAR }} >> .env.local
    cat .env.local
  working-directory: apps/web

Un fichier .env.local est créé pour stocker les variables d’environnement nécessaires. Ces variables sont récupérées à partir des secrets GitHub 🔗, un moyen sécurisé de stocker des informations sensibles pour ne pas qu’il soit affiché en clair dans la codebase.

Le mot-clé working-directory permet de spécifier dans quel dossier la step doit travailler. Ici je lui dit que c’est dans le dossier apps/web qui contient une application Next.js car je suis en monorepo. Si tu veux créer un fichier .env à la racine de ton projet, il suffit de supprimer la ligne working-directory.

Étape 5 : Installation des Dépendances

- name: 🛠️ Install dependencies
  run: pnpm i # Si tu utilises npm adapte avec npm i et pour yarn adapte avec yarn install

Les dépendances du projet sont installées à l’aide de pnpm avec la commande pnpm i.

Étape 6 : Linting

- name: 🖋️ Linting
  run: pnpm run lint # Si tu utilises npm adapte avec npm run lint et pour yarn adapte avec yarn run lint

Un contrôle de style (linting) est effectué pour s’assurer que le code respecte les règles définies dans le projet. Si des erreurs sont détectées, le processus échoue ici.

Pour lancer cette commande, tu dois bien entendu avoir une ligne script nommé lint. Dans mon cas, voici la ligne : “lint”: “turbo lint”, car je suis en monorepo.

Logiquement dans ton projet tu devras avoir une ligne de script lint qui ressemble à ça : eslint --config .eslintrc.js --fix.

Si tu n’as pas ou que tu ne souhaitez pas lancer le lint, alors tu peux supprimer cette étape.

Étape 7 : Construction

- name: 🏗️ Build
  env:
    NODE_OPTIONS: "--max_old_space_size=4096"
  run: pnpm run build # Si tu utilises npm adapte avec npm run build et pour yarn adapte avec yarn run build

Le projet est construit (compilé) avec ta fameuse commande de build que tu dois forcément avoir.

La variable d’environnement NODE_OPTIONS augmente la mémoire disponible pour Node.js, ce qui est particulièrement utile pour les projets volumineux comme un monorepo.

Étape 8 : Exécution des Tests

- name: 🧪 Run tests
  env:
    NODE_OPTIONS: "--max_old_space_size=4096"
  run: pnpm run test # Si tu utilises npm adapte avec npm run test et pour yarn adapte avec yarn run test

Les tests unitaires ou d’intégration sont exécutés.

Le même paramètre de mémoire est utilisé pour éviter les erreurs liées à la taille des processus.

Si tu n’as pas de tests, tu peux supprimer cette partie.

Étape 9 : Envoi du rapport de coverage vers CodeCov

- name: Upload coverage reports to Codecov
  uses: codecov/codecov-action@v4.0.1
  with:
    token: ${{ secrets.CODECOV_TOKEN }}
    slug: GaetanCottrez/tuto-monorepo-nextjs-nestjs-clerk-tinacms # Remplacer le slug par le nom de votre utilisateur suivi du nom de votre dépôt

Les rapports de couverture de tests sont envoyés à Codecov, qui est un service d’analyse du coverage de ton code pour évaluer le pourcentage de code couvert par les tests. Le token de Codecov est récupéré via les secrets GitHub.

Pour trouver le CODECOV_TOKEN, il faudra te créer un compte sur CodeCov 🔗, ajouter l’accès au dépôt en question et le configurer :

Pour tomber sur une page pour récupérer le token :

interface codecov configuration

Tester localement ton Workflow

On pourrait push et voir ce qui se passe avec notre workflow directement sur Github mais ce n’est pas l’idéal pour 3 raisons :

Heureusement, il existe un outil nommé act 🔗 qui permet de lancer localement ta futur Github Actions.

Il se base sur Docker 🔗 pour fonctionner bien entendu, donc veiller à ce qu’il soit installer.

Pour installer act, rien de plus simple avec cette commande :

curl--proto '=https' --tlsv1.2 - sSf https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash

Il est possible bien évidemment de gérer les fameux SECRETS de Github.

Pour se faire rien de plus simple. Il suffit de créer un fichier .secrets et de placer les variables d’environnements dedans (comme le fichier .env) :

MY_ENVIRONMENT_VAR=value_my_environment_var
NEXT_PUBLIC_MY_ENVIRONMENT_VAR=next_public_my_environment_var
CODECOV_TOKEN=140bbb5a-****-****-****-************

Une fois installé, il ne te reste plus qu’à lancer la commande suivante pour lancer ta CI Github Actions :

act --container-architecture linux/amd64

Lorsque tu vas lancer la commande pour la première fois, act va effectuer un pull de son image pour produire le container qui va lancer ta CI. Suivant ta machine et ta connexion internet, cela prendra plus ou moins de temps.

Si tu as une erreur, elle se manifestera de la façon suivante :

[CI / Lint, Build, Test & Analyze] 🏁  Job failed
Error: Job 'Lint, Build, Test & Analyze' failed

Si tout est ok, tu auras le message suivant :

[CI/Lint, Build, Test & Analyze] Cleaning up container for job Lint, Build, Test & Analyze
[CI/Lint, Build, Test & Analyze] 🏁  Job succeeded

A partir de là, tu peux considérer que la CI va fonctionner sur Github.

Déployer ton Workflow

Une fois ton workflow en place et testé, il est temps de le déployer.

Il suffit d’effectuer un commit et un push de tes modifications vers la branche spécifiée (dans ce cas, main).

Puis il suffira de ce rendre dans la partie Actions de ton dépôt pour voir le workflow en action.

Tu devrais voir les jobs s’exécuter et, normalement, réussir sans erreur!

résultat workflow github actions

Bonus: SonarCloud

SonarCloud c’est la version cloud de SonarQube et c’est un peu de CodeCov mais en beaucoup plus complet. En plus du Coverage, il va te permettre de détecter des bugs, des vulnérabilités, des problèmes de sécurité, ainsi que des mauvaises pratiques dans le code.

Un outil complet pour améliorer ton dépôt mais aussi ta façon de coder. L’outil est gratuit tant que tes dépôts sont en public. Pour tes dépôts privées, il faudra passer à la caisse !

Il faudra te créer un compte sur SonarCloud 🔗, ajouter l’accès au dépôt en question et lancer l’analyse initiale pour le créer dans l’outil.

Ajout de l’étape dans la CI

En parallèle de cela, il faut ajouter une nouvelle étape pour SonarCloud dans la CI :

- name: SonarCloud Scan
  uses: SonarSource/sonarcloud-github-action@master
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    SONAR_TOKEN: ${{ secrets.SONARCLOUD_TOKEN }}

Pour le secret du GITHUB_TOKEN, il sera provisionné automatiquement via ton compte Github donc tu n’as besoin de ne rien faire.

Récupération du token SonarCloud

Pour SONARCLOUD_TOKEN, il faudra se rendre dans son compte SonarCloud. Il suffit de se rendre dans la partie sécurité 🔗 de ton compte, puis de générer ton token et l’ajouter dans les secrets de ton repository Github :

interface sonarcloud création token

Il faudra également désactiver l’analyse automatique sinon ta Github Action ne pourra pas demander une analyse à SonarCloud (et cela ferait doublon).

Pour désactiver l’option, il suffit de se rendre dans la partie Administration de ton projet sous SonarCloud puis Analysis Method :

interface sonarcloud méthode analyse

Fichier de configuration

Il faut également créer un fichier sonar-project.properties à la racine de ton dépôt. Grâce à ce fichier, tu vas pouvoir paramétrer SonarCloud suivant tes besoins mais surtout lui dire sur quel projet tu travailles :

sonar.projectKey=*********
sonar.organization=*******

Pour trouver la valeur de projectKey, il suffit de se rendre dans la partie Administration de ton projet sous SonarCloud puis Update Key:

interface sonarcloud clé du projet

Pour trouver la valeur de organisation, il suffit de se rendre dans la partie Organisation de ton compte sous SonarCloud puis Administration:

interface sonarcloud clé organisation

Il faut également lui indiquer le(s) lien(s) de tes différents coverages surtout si tu es en monorepo

sonar.javascript.lcov.reportPaths=apps/api/coverage/lcov.info

Si tu es sur un dépôt standard, remplace apps/api/coverage/lcov.info par coverage/lcov.info.

Il suffit ensuite de faire un commit et un push pour relancer la CI :

résultat github actions avec sonarcloud

Et le résultat dans SonarCloud :

interface sonarcloud résultat analyse

Conclusion

Comme tu l’as vu, cette CI est une bonne base pour ton projet TypeScript et elle couvrira la plupart de tes besoins.

Tu peux retrouver le code complet de la CI sur ce dépôt GitHub : https://github.com/GaetanCottrez/tuto-monorepo-nextjs-nestjs-clerk-tinacms 🔗

As-tu trouvé ceci utile? N’hésite pas à me poser des questions en commentaire ou me proposer un cas de figure spécifique à tes besoins.

Vous pourriez aussi aimer

Guide Complet pour Configurer un Monorepo TypeScript avec Turbo

Guide Complet pour Configurer un Monorepo TypeScript avec Turbo

NestJS : un framework Backend JavaScript pour réaliser des API

NestJS : un framework Backend JavaScript pour réaliser des API

Article suivant
Guide Complet pour Configurer un Monorepo TypeScript avec Turbo