Pourquoi CI/CD pour Next.js avec GitHub Actions ?
La continuité d'intégration et la livraison continue (CI/CD) sont essentielles dans le développement moderne des applications web, notamment pour les projets basés sur React. La mise en place de pipelines CI/CD permet d'automatiser les tests, la construction et la déploiement de votre application à chaque modification du code source. Cela réduit significativement les erreurs humaines, accélère le feedback des utilisateurs et augmente la qualité globale des applications.
Un cas concret est un projet de gestionnaire de tâches. En mettant en place une CI/CD avec GitHub Actions, chaque fois que vous pushez une modification dans votre dépôt Git, le pipeline vérifie automatiquement si le code compile, s'il passe les tests unitaires et si l'application peut être déployée sur un environnement de production. Cela permet d'identifier rapidement les problèmes et de corriger les bugs avant qu'ils ne soient vus par les utilisateurs.
Prerequis
- Connaissance du langage JavaScript et React
- Compréhension de Git et des bases de la gestion de dépôts
- Un compte GitHub
- Node.js et npm installés (version 14 ou supérieure)
- Yarn installé (optionnel)
Concepts fondamentaux
Workflow
Un workflow est une série d'étapes automatisées qui définissent comment un événement spécifique doit être géré. Par exemple, chaque push sur la branche principale déclenche un workflow de déploiement.
## Exemple de workflow simple dans .github/workflows/deploy.yml
name: Deploy
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Node.js 14
uses: actions/setup-node@v2
with:
node-version: '14'
- run: npm install
- run: npm run build
Actions
Les actions sont les unités de travail qui peuvent être exécutées dans un workflow. Elles peuvent être écrites en JavaScript ou utiliser des actions pré-existantes.
## Exemple d'action utilisant une action pré-existante pour installer Node.js
- name: Set up Node.js 14
uses: actions/setup-node@v2
with:
node-version: '14'
Étapes
Les étapes sont les tâches individuelles qui forment un job. Elles peuvent exécuter des commandes shell, installer des dépendances ou appeler d'autres actions.
## Exemple de étape pour installer les dépendances du projet
- run: npm install
Mise en pratique : projet fil rouge
Nous allons créer un mini-projet complet et réaliste : une API de blog. Ce projet comprendra la création, la lecture, la mise à jour et la suppression d'articles.
Étape 1 : Initialisation du projet
Créez un nouveau dépôt GitHub et clonez-le localement. Initialisez le projet Next.js.
npx create-next-app@latest blog-api
cd blog-api
Étape 2 : Configuration de la base de données
Nous utiliserons Prisma pour gérer la base de données SQLite.
npm install prisma --save-dev
npx prisma init
Editez prisma/schema.prisma :
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model Post {
id Int @id @default(autoincrement())
title String @db.VarChar(100)
content String
createdAt DateTime@db.Timestamp(@default(now()))
}
Créez un fichier .env à la racine du projet avec :
DATABASE_URL=file:./dev.db
Étape 3 : Création des modèles Prisma
Générez le client Prisma et créez les fichiers de modèle.
npx prisma migrate dev --name init
Étape 4 : Création des API CRUD
Créez un fichier pages/api/posts.js pour gérer les opérations CRUD :
// pages/api/posts.js
import prisma from '../../lib/prisma'
export default async function handler(req, res) {
const { method } = req
switch (method) {
case 'GET':
// Récupère tous les articles
const posts = await prisma.post.findMany()
res.status(200).json(posts)
break
case 'POST':
// Crée un nouvel article
const { title, content } = req.body
const newPost = await prisma.post.create({
data: {
title,
content,
},
})
res.status(201).json(newPost)
break
default:
res.setHeader('Allow', ['GET', 'POST'])
res.status(405).end(`Method ${method} Not Allowed`)
}
}
Étape 5 : Création de l'interface utilisateur
Créez un fichier pages/index.js pour afficher les articles et créer de nouveaux :
// pages/index.js
import { useState, useEffect } from 'react'
import axios from 'axios'
function Home() {
const [posts, setPosts] = useState([])
const [title, setTitle] = useState('')
const [content, setContent] = useState('')
useEffect(() => {
fetchPosts()
}, [])
async function fetchPosts() {
const response = await axios.get('/api/posts')
setPosts(response.data)
}
async function createPost(event) {
event.preventDefault()
await axios.post('/api/posts', { title, content })
setTitle('')
setContent('')
fetchPosts()
}
return (
<div>
<h1>Blog API</h1>
<form onSubmit={createPost}>
<input
type="text"
placeholder="Title"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<textarea
placeholder="Content"
value={content}
onChange={(e) => setContent(e.target.value)}
/>
<button type="submit">Create Post</button>
</form>
<ul>
{posts.map((post) => (
<li key={post.id}>
<h2>{post.title}</h2>
<p>{post.content}</p>
<p>Created at: {post.createdAt.toString()}</p>
</li>
))}
</ul>
</div>
)
}
export default Home
Étape 6 : Ajout de la gestion des erreurs
Ajoutez une gestion des erreurs dans le fichier pages/index.js :
// pages/index.js (avec gestion des erreurs)
async function createPost(event) {
event.preventDefault()
try {
await axios.post('/api/posts', { title, content })
setTitle('')
setContent('')
fetchPosts()
} catch (error) {
console.error('Error creating post:', error)
alert('Failed to create post. Please try again.')
}
}
Étape 7 : Ajout d'un workflow GitHub Actions
Créez un fichier .github/workflows/deploy.yml pour définir le pipeline CI/CD :
name: Deploy
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Node.js 14
uses: actions/setup-node@v2
with:
node-version: '14'
- run: npm install
- run: npm run build
- name: Deploy to Vercel
uses: vercel/actions/deploy@v18
with:
apiToken: $secrets.VERCEL_API_TOKEN
projectRoot: .
scope: your-vercel-scope
alias: blog-api
Ajoutez votre token Vercel à vos variables d'environnement GitHub :
gh secret set VERCEL_API_TOKEN -b $(cat ~/.vercel/api-key)
Erreurs frequentes et debugging
1. Erreur : Error: Cannot find module 'prisma-client-js'
Code incorrect :
// pages/index.js (mauvais import)
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
Code correct :
// pages/index.js (correct import)
import prisma from '../../lib/prisma'
2. Erreur : Error: connect ECONNREFUSED 127.0.0.1:3306
Code incorrect :
## .github/workflows/deploy.yml (mauvaise configuration de la base de données)
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
Code correct :
## .github/workflows/deploy.yml (correcte configuration de la base de données)
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
3. Erreur : Error: ENOENT: no such file or directory, open 'dev.db'
Code incorrect :
## .github/workflows/deploy.yml (mauvaise configuration de la base de données)
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
Code correct :
## .github/workflows/deploy.yml (correcte configuration de la base de données)
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
Pour aller plus loin
- Tests automatisés avec Jest et Supertest : Ajoutez des tests pour vous assurer que votre API fonctionne correctement.
- Déploiement sur Vercel : Utilisez Vercel pour déploiement continu en utilisant des secrets GitHub Actions.
- Intégration avec Docker : Créez un fichier
Dockerfilepour containeriser votre application et faciliter le déploiement.
Défi pratique
Créez une application de todo list complète en suivant les mêmes étapes que dans ce tutoriel, mais cette fois-ci utilisez MongoDB comme base de données.