Cours Next.js · Module 8: Déploiement et production
CI/CD et automatisation du déploiement
Dans cette leçon12
Dans le développement logiciel moderne, l'intégration continue (Continuous Integration, CI) et le déploiement continu (Continuous Deployment, CD) sont des pratiques clés qui permettent de livrer les applications plus rapidement et de façon plus fiable. Next.js, en tant que framework moderne, s'intègre parfaitement avec divers outils CI/CD, ce qui permet d'automatiser le processus de test, de build et de déploiement des applications.
Qu'est-ce que le CI/CD ?
Intégration continue (CI)
L'intégration continue est une pratique où les développeurs intègrent régulièrement leurs modifications de code dans la branche principale du dépôt. Chaque intégration est automatiquement vérifiée par :
- L'exécution des tests unitaires et d'intégration
- L'analyse statique du code (linting)
- La vérification de la qualité du code
- La vérification des types (dans le cas de TypeScript)
La CI permet de détecter tôt les erreurs et garantit que le code dans la branche principale reste stable.
Déploiement continu (CD)
Le déploiement continu va un pas plus loin - après avoir passé avec succès l'étape CI, il déploie automatiquement les changements dans l'environnement de production ou de test. Le processus CD comprend :
- Le build automatique de l'application
- Le déploiement dans un environnement choisi (staging, production)
- Les tests E2E automatiques après le déploiement
- Le monitoring du déploiement
Outils CI/CD populaires pour les applications Next.js
Pour les applications Next.js, nous pouvons utiliser divers outils CI/CD :
- GitHub Actions - intégré à GitHub, offre une excellente intégration avec le dépôt
- GitLab CI/CD - intégré à GitLab, permet de créer des pipelines CI/CD
- CircleCI - plateforme CI/CD populaire avec une configuration simple
- Jenkins - serveur d'automatisation ouvert et extensible
- Vercel CI/CD - solution native pour les applications Next.js
- Netlify CI/CD - alternative avec des fonctionnalités similaires
- Travis CI - l'une des plus anciennes plateformes CI/CD
Configuration du CI/CD avec GitHub Actions
La façon la plus simple d'implémenter le CI/CD pour un projet Next.js est d'utiliser GitHub Actions si ton dépôt est hébergé sur GitHub.
1. Configuration du workflow de test
Crée le fichier .github/workflows/ci.yml dans ton dépôt :
1name: CI
2
3on:
4 push:
5 branches: [main, development]
6 pull_request:
7 branches: [main, development]
8
9jobs:
10 test:
11 runs-on: ubuntu-latest
12
13 strategy:
14 matrix:
15 node-version: [18.x]
16
17 steps:
18 - uses: actions/checkout@v3
19
20 - name: Use Node.js ${{ matrix.node-version }}
21 uses: actions/setup-node@v3
22 with:
23 node-version: ${{ matrix.node-version }}
24 cache: 'npm'
25
26 - name: Install dependencies
27 run: npm ci
28
29 - name: Lint
30 run: npm run lint
31
32 - name: Type check
33 run: npm run type-check
34
35 - name: Run tests
36 run: npm test
37
38 - name: Build
39 run: npm run buildCe workflow s'exécute à chaque push sur les branches main et development et lors de la création d'une Pull Request vers ces branches.
2. Configuration du workflow de déploiement Vercel
Crée le fichier .github/workflows/deploy.yml :
1name: Deploy to Production
2
3on:
4 push:
5 branches: [main]
6
7jobs:
8 deploy:
9 runs-on: ubuntu-latest
10
11 steps:
12 - uses: actions/checkout@v3
13
14 - name: Deploy to Vercel
15 uses: amondnet/vercel-action@v20
16 with:
17 vercel-token: ${{ secrets.VERCEL_TOKEN }}
18 vercel-org-id: ${{ secrets.ORG_ID }}
19 vercel-project-id: ${{ secrets.PROJECT_ID }}
20 vercel-args: '--prod'Pour que cela fonctionne, tu dois configurer les secrets suivants dans les paramètres de ton dépôt :
VERCEL_TOKEN- Token de l'API VercelORG_ID- ID de l'organisation dans VercelPROJECT_ID- ID du projet dans Vercel
3. Configuration du workflow pour l'environnement de staging
1name: Deploy to Staging
2
3on:
4 push:
5 branches: [development]
6
7jobs:
8 deploy:
9 runs-on: ubuntu-latest
10
11 steps:
12 - uses: actions/checkout@v3
13
14 - name: Deploy to Vercel (Preview)
15 uses: amondnet/vercel-action@v20
16 with:
17 vercel-token: ${{ secrets.VERCEL_TOKEN }}
18 vercel-org-id: ${{ secrets.ORG_ID }}
19 vercel-project-id: ${{ secrets.PROJECT_ID }}
20 github-comment: trueConfiguration du CI/CD avec GitLab CI
Si tu utilises GitLab, tu peux configurer le CI/CD à l'aide de .gitlab-ci.yml :
1image: node:18-alpine
2
3stages:
4 - test
5 - build
6 - deploy_staging
7 - deploy_production
8
9cache:
10 key: ${CI_COMMIT_REF_SLUG}
11 paths:
12 - node_modules/
13 - .next/cache/
14
15test:
16 stage: test
17 script:
18 - npm ci
19 - npm run lint
20 - npm run type-check
21 - npm test
22 except:
23 - tags
24
25build:
26 stage: build
27 script:
28 - npm ci
29 - npm run build
30 artifacts:
31 paths:
32 - .next/
33 except:
34 - tags
35
36deploy_staging:
37 stage: deploy_staging
38 script:
39 - npm install -g vercel
40 - vercel --token ${VERCEL_TOKEN} --confirm
41 environment:
42 name: staging
43 url: ${CI_PROJECT_NAME}-git-${CI_COMMIT_REF_NAME}-${CI_PROJECT_NAMESPACE}.vercel.app
44 only:
45 - development
46
47deploy_production:
48 stage: deploy_production
49 script:
50 - npm install -g vercel
51 - vercel --token ${VERCEL_TOKEN} --prod --confirm
52 environment:
53 name: production
54 url: ${CI_PROJECT_NAME}.vercel.app
55 only:
56 - mainConfiguration du CI/CD pour une application Next.js avec Circle CI
Pour configurer CircleCI, crée le fichier .circleci/config.yml :
1version: 2.1
2
3orbs:
4 node: circleci/node@5.0.3
5
6jobs:
7 test:
8 docker:
9 - image: cimg/node:18.15.0
10 steps:
11 - checkout
12 - node/install-packages:
13 pkg-manager: npm
14 - run:
15 name: Run tests
16 command: npm test
17 - run:
18 name: Run linter
19 command: npm run lint
20 - run:
21 name: Check types
22 command: npm run type-check
23
24 build:
25 docker:
26 - image: cimg/node:18.15.0
27 steps:
28 - checkout
29 - node/install-packages:
30 pkg-manager: npm
31 - run:
32 name: Build application
33 command: npm run build
34 - persist_to_workspace:
35 root: .
36 paths:
37 - .next
38 - node_modules
39 - package.json
40
41 deploy-staging:
42 docker:
43 - image: cimg/node:18.15.0
44 steps:
45 - checkout
46 - attach_workspace:
47 at: .
48 - run:
49 name: Install Vercel CLI
50 command: npm install -g vercel
51 - run:
52 name: Deploy to Staging
53 command: vercel --token ${VERCEL_TOKEN} --confirm
54
55 deploy-production:
56 docker:
57 - image: cimg/node:18.15.0
58 steps:
59 - checkout
60 - attach_workspace:
61 at: .
62 - run:
63 name: Install Vercel CLI
64 command: npm install -g vercel
65 - run:
66 name: Deploy to Production
67 command: vercel --token ${VERCEL_TOKEN} --prod --confirm
68
69workflows:
70 version: 2
71 test-build-deploy:
72 jobs:
73 - test
74 - build:
75 requires:
76 - test
77 - deploy-staging:
78 requires:
79 - build
80 filters:
81 branches:
82 only: development
83 - deploy-production:
84 requires:
85 - build
86 filters:
87 branches:
88 only: mainConfiguration du CI/CD avec Jenkins
Jenkins est un outil CI/CD puissant, mais il demande plus de configuration. Voici un exemple de Jenkinsfile pour un pipeline CI/CD d'une application Next.js :
1pipeline {
2 agent {
3 docker {
4 image 'node:18-alpine'
5 }
6 }
7
8 stages {
9 stage('Install Dependencies') {
10 steps {
11 sh 'npm ci'
12 }
13 }
14
15 stage('Lint') {
16 steps {
17 sh 'npm run lint'
18 }
19 }
20
21 stage('Type Check') {
22 steps {
23 sh 'npm run type-check'
24 }
25 }
26
27 stage('Test') {
28 steps {
29 sh 'npm test'
30 }
31 }
32
33 stage('Build') {
34 steps {
35 sh 'npm run build'
36 }
37 }
38
39 stage('Deploy to Staging') {
40 when {
41 branch 'development'
42 }
43 steps {
44 sh 'npm install -g vercel'
45 sh 'vercel --token ${VERCEL_TOKEN} --confirm'
46 }
47 }
48
49 stage('Deploy to Production') {
50 when {
51 branch 'main'
52 }
53 steps {
54 sh 'npm install -g vercel'
55 sh 'vercel --token ${VERCEL_TOKEN} --prod --confirm'
56 }
57 }
58 }
59
60 post {
61 always {
62 cleanWs()
63 }
64 }
65}Tester dans le processus CI/CD
Un élément important du processus CI/CD est le test automatique. Pour les applications Next.js, nous pouvons configurer différents types de tests :
1. Tests unitaires avec Jest et React Testing Library
1// __tests__/components/Header.test.tsx
2import { render, screen } from '@testing-library/react';
3import Header from '@/components/Header';
4
5describe('Header Component', () => {
6 it('renders the logo', () => {
7 render(<Header />);
8 const logo = screen.getByAltText('Logo');
9 expect(logo).toBeInTheDocument();
10 });
11
12 it('has the correct navigation links', () => {
13 render(<Header />);
14 expect(screen.getByText('Home')).toBeInTheDocument();
15 expect(screen.getByText('About')).toBeInTheDocument();
16 expect(screen.getByText('Contact')).toBeInTheDocument();
17 });
18});2. Tests d'intégration pour les API Routes
1// __tests__/api/user.test.ts
2import { createMocks } from 'node-mocks-http';
3import userHandler from '@/app/api/user/route';
4
5describe('/api/user endpoint', () => {
6 it('returns a user when provided with a valid ID', async () => {
7 const { req, res } = createMocks({
8 method: 'GET',
9 query: { id: '123' },
10 });
11
12 await userHandler(req, res);
13
14 expect(res._getStatusCode()).toBe(200);
15 expect(JSON.parse(res._getData())).toEqual(
16 expect.objectContaining({
17 id: '123',
18 name: expect.any(String),
19 })
20 );
21 });
22
23 it('returns 404 for non-existent user', async () => {
24 const { req, res } = createMocks({
25 method: 'GET',
26 query: { id: 'non-existent' },
27 });
28
29 await userHandler(req, res);
30
31 expect(res._getStatusCode()).toBe(404);
32 });
33});3. Tests End-to-End avec Cypress
1// cypress/integration/navigation.spec.js
2describe('Navigation', () => {
3 it('should navigate to the about page', () => {
4 cy.visit('/');
5
6 cy.get('a[href*="about"]').click();
7
8 cy.url().should('include', '/about');
9 cy.get('h1').contains('About');
10 });
11
12 it('should navigate to the contact page', () => {
13 cy.visit('/');
14
15 cy.get('a[href*="contact"]').click();
16
17 cy.url().should('include', '/contact');
18 cy.get('h1').contains('Contact');
19 });
20});Ajoute la configuration Cypress à GitHub Actions :
1# .github/workflows/e2e-tests.yml
2name: E2E Tests
3
4on:
5 deployment_status:
6
7jobs:
8 e2e-tests:
9 if: github.event.deployment_status.state == 'success'
10 runs-on: ubuntu-latest
11 steps:
12 - uses: actions/checkout@v3
13
14 - name: Install dependencies
15 run: npm ci
16
17 - name: Cypress run
18 uses: cypress-io/github-action@v5
19 with:
20 record: true
21 env:
22 CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
23 CYPRESS_BASE_URL: ${{ github.event.deployment_status.target_url }}Automatisation des tâches de développement
Au-delà du CI/CD, nous pouvons automatiser diverses autres tâches de développement :
1. Formatage automatique du code avec Husky et lint-staged
Installe les paquets nécessaires :
1npm install --save-dev husky lint-staged prettierMets à jour package.json :
1{
2 "scripts": {
3 "prepare": "husky install"
4 },
5 "lint-staged": {
6 "*.{js,jsx,ts,tsx}": [
7 "prettier --write",
8 "eslint --fix"
9 ],
10 "*.{json,md}": [
11 "prettier --write"
12 ]
13 }
14}Crée un hook pre-commit :
1npx husky add .husky/pre-commit "npx lint-staged"2. Versionnage automatique avec Semantic Release
Installe les paquets nécessaires :
1npm install --save-dev semantic-release @semantic-release/git @semantic-release/changelogCrée le fichier .releaserc.json :
1{
2 "branches": ["main"],
3 "plugins": [
4 "@semantic-release/commit-analyzer",
5 "@semantic-release/release-notes-generator",
6 "@semantic-release/changelog",
7 "@semantic-release/npm",
8 ["@semantic-release/git", {
9 "assets": ["package.json", "CHANGELOG.md"],
10 "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
11 }],
12 "@semantic-release/github"
13 ]
14}Ajoute un workflow pour semantic-release :
1# .github/workflows/release.yml
2name: Release
3
4on:
5 push:
6 branches: [main]
7
8jobs:
9 release:
10 runs-on: ubuntu-latest
11 steps:
12 - uses: actions/checkout@v3
13 - name: Setup Node.js
14 uses: actions/setup-node@v3
15 with:
16 node-version: '18'
17 cache: 'npm'
18 - name: Install dependencies
19 run: npm ci
20 - name: Release
21 env:
22 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
23 NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
24 run: npx semantic-release3. Génération automatique de la documentation
Pour les projets TypeScript, tu peux automatiser la génération de la documentation :
1# .github/workflows/docs.yml
2name: Generate Documentation
3
4on:
5 push:
6 branches: [main]
7
8jobs:
9 docs:
10 runs-on: ubuntu-latest
11 steps:
12 - uses: actions/checkout@v3
13 - name: Setup Node.js
14 uses: actions/setup-node@v3
15 with:
16 node-version: '18'
17 cache: 'npm'
18 - name: Install dependencies
19 run: npm ci
20 - name: Generate docs
21 run: npx typedoc --out docs src
22 - name: Deploy docs to GitHub Pages
23 uses: peaceiris/actions-gh-pages@v3
24 with:
25 github_token: ${{ secrets.GITHUB_TOKEN }}
26 publish_dir: ./docsStratégies de déploiement et de rollback
Lors du déploiement d'applications, il vaut la peine d'implémenter des stratégies de déploiement sûres :
1. Blue-Green Deployment
Le déploiement Blue-Green consiste à maintenir deux environnements de production identiques, dont un seul est actif. Dans le cas de Vercel, tu peux utiliser leur fonctionnalité intégrée « Preview Deployments » et l'aliasing de domaine :
1// scripts/blue-green-deploy.js
2const { execSync } = require('child_process');
3
4// Deploy to new environment (but not live yet)
5const output = execSync('vercel --prod').toString();
6const deploymentUrl = output.match(/https:\/\/[\w.-]+/)[0];
7
8// Run smoke tests against new deployment
9const smokeTestResult = execSync(`npm run test:smoke -- --url=${deploymentUrl}`).toString();
10
11if (smokeTestResult.includes('All tests passed')) {
12 // Switch traffic to new deployment
13 execSync(`vercel alias set ${deploymentUrl} your-production-domain.com`);
14 console.log('Successfully switched to new deployment');
15} else {
16 console.error('Smoke tests failed, aborting deployment');
17 process.exit(1);
18}2. Canary Releases
Dans la stratégie Canary, une nouvelle version est déployée progressivement, d'abord seulement pour un petit pourcentage d'utilisateurs. Dans Next.js, tu peux y parvenir à l'aide de feature flags et de la répartition du trafic :
1// lib/feature-flags.ts
2import { useEffect, useState } from 'react';
3
4const CANARY_PERCENTAGE = 10; // 10% of users will get the new version
5
6export function useCanaryFeature(featureName: string): boolean {
7 const [isEnabled, setIsEnabled] = useState(false);
8
9 useEffect(() => {
10 // Generate a random number for the user and save it in local storage
11 let userBucket = localStorage.getItem('userBucket');
12 if (!userBucket) {
13 userBucket = Math.floor(Math.random() * 100).toString();
14 localStorage.setItem('userBucket', userBucket);
15 }
16
17 // Check if the user is in the canary group
18 const inCanaryGroup = parseInt(userBucket) < CANARY_PERCENTAGE;
19
20 // Check if the feature is active for this user
21 fetch('/api/feature-flags')
22 .then(res => res.json())
23 .then(flags => {
24 // If the flag exists and the user is in the canary group, enable the feature
25 if (flags[featureName] && inCanaryGroup) {
26 setIsEnabled(true);
27 }
28 });
29 }, [featureName]);
30
31 return isEnabled;
32}3. Rollback automatique
Le rollback automatique du déploiement quand des problèmes surviennent est une pratique clé :
1# .github/workflows/deploy-monitor-rollback.yml
2name: Deploy, Monitor & Rollback
3
4on:
5 push:
6 branches: [main]
7
8jobs:
9 deploy:
10 runs-on: ubuntu-latest
11 outputs:
12 deployment_id: ${{ steps.deploy.outputs.deployment_id }}
13 steps:
14 - uses: actions/checkout@v3
15 - name: Deploy to Vercel
16 id: deploy
17 uses: amondnet/vercel-action@v20
18 with:
19 vercel-token: ${{ secrets.VERCEL_TOKEN }}
20 vercel-org-id: ${{ secrets.ORG_ID }}
21 vercel-project-id: ${{ secrets.PROJECT_ID }}
22 vercel-args: '--prod'
23
24
25 monitor:
26 needs: deploy
27 runs-on: ubuntu-latest
28 steps:
29 - name: Wait for 2 minutes
30 run: sleep 120
31
32 - name: Check for errors
33 id: check_errors
34 run: |
35 ERROR_COUNT=$(curl -s "https://api.example.com/monitoring/errors?since=$(date -u -d '2 minutes ago' +%Y-%m-%dT%H:%M:%SZ)" | jq '.count')
36 echo "::set-output name=error_count::$ERROR_COUNT"
37 if [ "$ERROR_COUNT" -gt "10" ]; then
38 echo "Error count too high ($ERROR_COUNT). Failing job."
39 exit 1
40 fi
41
42 - name: Monitor Core Web Vitals
43 id: web_vitals
44 run: |
45 LCP=$(curl -s "https://api.example.com/monitoring/web-vitals" | jq '.lcp')
46 if [ "$LCP" -gt "2500" ]; then
47 echo "LCP too high ($LCP ms). Failing job."
48 exit 1
49 fi
50
51 rollback:
52 needs: [deploy, monitor]
53 runs-on: ubuntu-latest
54 if: ${{ failure() }}
55 steps:
56 - name: Rollback to previous deployment
57 uses: amondnet/vercel-action@v20
58 with:
59 vercel-token: ${{ secrets.VERCEL_TOKEN }}
60 vercel-org-id: ${{ secrets.ORG_ID }}
61 vercel-project-id: ${{ secrets.PROJECT_ID }}
62 vercel-args: '--prod --scope myorg' # Rollback to previous deploymentGérer les variables d'environnement
La gestion sécurisée des variables d'environnement est un aspect critique du CI/CD :
1. Stocker les variables d'environnement dans le système CI/CD
La plupart des systèmes CI/CD proposent un stockage sécurisé des secrets, par ex. GitHub Secrets :
1# .github/workflows/deploy.yml
2name: Deploy
3
4on:
5 push:
6 branches: [main]
7
8jobs:
9 deploy:
10 runs-on: ubuntu-latest
11 steps:
12 - uses: actions/checkout@v3
13
14 - name: Create .env file
15 run: |
16 cat > .env.production << EOL
17 DATABASE_URL=${{ secrets.DATABASE_URL }}
18 API_KEY=${{ secrets.API_KEY }}
19 NEXT_PUBLIC_ANALYTICS_ID=${{ secrets.NEXT_PUBLIC_ANALYTICS_ID }}
20 EOL
21
22 - name: Deploy
23 run: npm run deploy2. Rotation automatique des secrets
Pour les applications critiques, il vaut la peine d'implémenter une rotation automatique des secrets :
1# .github/workflows/rotate-secrets.yml
2name: Rotate Secrets
3
4on:
5 schedule:
6 - cron: '0 0 1 * *' # Every month
7
8jobs:
9 rotate:
10 runs-on: ubuntu-latest
11 steps:
12 - name: Generate new API key
13 id: new_key
14 run: echo "::set-output name=api_key::$(openssl rand -base64 32)"
15
16 - name: Update API service with new key
17 run: |
18 curl -X POST -H "Authorization: Bearer ${{ secrets.ADMIN_TOKEN }}" -d '{"api_key": "${{ steps.new_key.outputs.api_key }}"}' https://api.example.com/admin/update-key
19
20 - name: Update GitHub secret
21 uses: gliech/create-github-secret-action@v1
22 with:
23 name: API_KEY
24 value: ${{ steps.new_key.outputs.api_key }}
25 pa_token: ${{ secrets.PA_TOKEN }}Monitoring du déploiement
Un élément clé du processus CI/CD est le monitoring du déploiement, pour détecter rapidement les problèmes potentiels et y réagir :
1. Intégration avec les services de monitoring
1# .github/workflows/deploy-and-monitor.yml
2name: Deploy and Monitor
3
4on:
5 push:
6 branches: [main]
7
8jobs:
9 deploy:
10 runs-on: ubuntu-latest
11 steps:
12 - uses: actions/checkout@v3
13 - name: Deploy to Vercel
14 uses: amondnet/vercel-action@v20
15 with:
16 vercel-token: ${{ secrets.VERCEL_TOKEN }}
17 vercel-org-id: ${{ secrets.ORG_ID }}
18 vercel-project-id: ${{ secrets.PROJECT_ID }}
19 vercel-args: '--prod'
20
21 - name: Notify Datadog of deployment
22 run: |
23 curl -X POST "https://api.datadoghq.com/api/v1/events" -H "Content-Type: application/json" -H "DD-API-KEY: ${{ secrets.DATADOG_API_KEY }}" -d '{"title":"Deployment to production","text":"Deployed commit ${{ github.sha }} to production","tags":["environment:production","team:frontend"],"alert_type":"info"}'2. Tests automatiques après déploiement
1# .github/workflows/post-deploy-tests.yml
2name: Post-deployment Tests
3
4on:
5 deployment_status:
6
7jobs:
8 smoke-tests:
9 if: github.event.deployment_status.state == 'success'
10 runs-on: ubuntu-latest
11 steps:
12 - uses: actions/checkout@v3
13
14 - name: Run smoke tests
15 run: |
16 npm ci
17 npm run test:smoke -- --url=${{ github.event.deployment_status.target_url }}
18
19 - name: Post results to Slack
20 if: always()
21 uses: slackapi/slack-github-action@v1
22 with:
23 payload: |
24 {
25 "text": "Smoke test results: ${{ job.status }}\nDeployment URL: ${{ github.event.deployment_status.target_url }}"
26 }
27 env:
28 SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}Résumé
Implémenter le CI/CD pour les applications Next.js est un élément clé du processus moderne de développement logiciel, qui apporte de nombreux bénéfices :
- Déploiement plus rapide des changements - l'automatisation du processus réduit le temps nécessaire pour livrer de nouvelles fonctionnalités
- Meilleure qualité du code - les tests automatiques et l'analyse du code garantissent une meilleure qualité
- Moins de risque d'erreurs - l'automatisation élimine les erreurs humaines
- Feedback plus rapide - les développeurs reçoivent un retour immédiat sur la qualité du code
- Productivité accrue - les développeurs se concentrent sur le développement de fonctionnalités, pas sur des processus manuels
Un pipeline CI/CD correctement configuré est un investissement qui se rentabilise en temps gagné, en meilleure qualité de code et en déploiements plus fiables. Quelle que soit la plateforme choisie (GitHub Actions, GitLab CI, CircleCI, Jenkins), l'essentiel est d'adapter le processus aux besoins spécifiques du projet et de l'équipe.
Code de cette leçon : App.tsx
1import React, { useState } from 'react';
2
3// Demo : CI/CD et automatisation du déploiement
4interface PipelineStage {
5 id: string;
6 name: string;
7 icon: string;
8 status: 'pending' | 'running' | 'success' | 'failed' | 'skipped';
9 duration?: number;
10 steps: string[];
11}
12
13function CICDDemo() {
14 const [stages, setStages] = useState<PipelineStage[]>([
15 { id: 'install', name: 'Install', icon: 'I', status: 'pending', steps: ['npm ci', 'Cache dependencies'] },
16 { id: 'lint', name: 'Lint', icon: 'L', status: 'pending', steps: ['ESLint', 'Prettier check', 'TypeScript check'] },
17 { id: 'test', name: 'Test', icon: 'T', status: 'pending', steps: ['Unit tests', 'Integration tests', 'Coverage report'] },
18 { id: 'build', name: 'Build', icon: 'B', status: 'pending', steps: ['next build', 'Bundle analysis', 'Optimize assets'] },
19 { id: 'deploy', name: 'Deploy', icon: 'D', status: 'pending', steps: ['Deploy to staging', 'Health check', 'Promote to production'] },
20 ]);
21 const [activeProvider, setActiveProvider] = useState<'github' | 'gitlab' | 'vercel'>('github');
22 const [isRunning, setIsRunning] = useState(false);
23 const [failAt, setFailAt] = useState<string>('none');
24
25 const statusColors: Record<string, string> = {
26 pending: '#64748b', running: '#f59e0b', success: '#10b981', failed: '#ef4444', skipped: '#8892b0',
27 };
28
29 const statusIcons: Record<string, string> = {
30 pending: 'o', running: '~', success: 'V', failed: 'X', skipped: '-',
31 };
32
33 const runPipeline = async () => {
34 setIsRunning(true);
35 setStages(prev => prev.map(s => ({ ...s, status: 'pending', duration: undefined })));
36
37 for (let i = 0; i < stages.length; i++) {
38 const stage = stages[i];
39
40 setStages(prev => prev.map((s, idx) => idx === i ? { ...s, status: 'running' } : s));
41
42 const duration = Math.floor(Math.random() * 3000) + 1000;
43 await new Promise(r => setTimeout(r, duration));
44
45 if (failAt === stage.id) {
46 setStages(prev => prev.map((s, idx) =>
47 idx === i ? { ...s, status: 'failed', duration } :
48 idx > i ? { ...s, status: 'skipped' } : s
49 ));
50 setIsRunning(false);
51 return;
52 }
53
54 setStages(prev => prev.map((s, idx) => idx === i ? { ...s, status: 'success', duration } : s));
55 }
56
57 setIsRunning(false);
58 };
59
60 const ciConfigs: Record<string, string> = {
61 github: "# .github/workflows/deploy.yml\nname: Deploy\non:\n push:\n branches: [main]\n\njobs:\n deploy:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-node@v4\n - run: npm ci\n - run: npm run lint\n - run: npm run test\n - run: npm run build\n - run: npx vercel --prod",
62 gitlab: "# .gitlab-ci.yml\nstages:\n - install\n - lint\n - test\n - build\n - deploy\n\ninstall:\n stage: install\n script: npm ci\n cache:\n paths: [node_modules/]\n\ntest:\n stage: test\n script: npm run test\n\nbuild:\n stage: build\n script: npm run build",
63 vercel: "// vercel.json\n{\n \"buildCommand\": \"npm run build\",\n \"devCommand\": \"npm run dev\",\n \"installCommand\": \"npm ci\",\n \"framework\": \"nextjs\",\n \"github\": {\n \"autoAlias\": true,\n \"silent\": true\n }\n}",
64 };
65
66 return (
67 <div style={{ background: '#0f0f23', minHeight: '100vh', padding: '20px', color: '#e0e0e0', fontFamily: 'system-ui' }}>
68 <h1 style={{ color: '#64ffda', marginBottom: '8px' }}>CI/CD Pipeline</h1>
69 <p style={{ color: '#8892b0', marginBottom: '24px' }}>Automatisation du déploiement d'applications Next.js</p>
70
71 <div style={{ display: 'flex', gap: '8px', marginBottom: '20px' }}>
72 {[
73 { id: 'github' as const, label: 'GitHub Actions' },
74 { id: 'gitlab' as const, label: 'GitLab CI' },
75 { id: 'vercel' as const, label: 'Vercel' },
76 ].map(p => (
77 <button key={p.id} onClick={() => setActiveProvider(p.id)} style={{
78 padding: '8px 16px', borderRadius: '8px', border: 'none', cursor: 'pointer',
79 background: activeProvider === p.id ? '#64ffda' : '#1a1a2e', color: activeProvider === p.id ? '#0f0f23' : '#8892b0', fontWeight: 'bold',
80 }}>{p.label}</button>
81 ))}
82 </div>
83
84 <div style={{ background: '#1a1a2e', padding: '20px', borderRadius: '12px', border: '1px solid #333', marginBottom: '24px' }}>
85 <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '20px' }}>
86 <h3 style={{ color: '#64ffda', margin: 0 }}>Pipeline Stages</h3>
87 <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
88 <select value={failAt} onChange={e => setFailAt(e.target.value)} style={{ padding: '6px 10px', borderRadius: '6px', background: '#0f0f23', color: '#fff', border: '1px solid #333', fontSize: '12px' }}>
89 <option value="none">No failure</option>
90 {stages.map(s => <option key={s.id} value={s.id}>Fail at {s.name}</option>)}
91 </select>
92 <button onClick={runPipeline} disabled={isRunning} style={{ padding: '8px 16px', borderRadius: '6px', background: isRunning ? '#333' : '#64ffda', color: '#0f0f23', border: 'none', cursor: isRunning ? 'default' : 'pointer', fontWeight: 'bold' }}>
93 {isRunning ? 'Running...' : 'Run Pipeline'}
94 </button>
95 </div>
96 </div>
97
98 <div style={{ display: 'flex', gap: '16px', alignItems: 'flex-start', flexWrap: 'wrap' }}>
99 {stages.map((stage, i) => (
100 <React.Fragment key={stage.id}>
101 <div style={{ textAlign: 'center', minWidth: '120px' }}>
102 <div style={{
103 width: '50px', height: '50px', borderRadius: '50%', margin: '0 auto 8px',
104 border: '3px solid ' + statusColors[stage.status],
105 display: 'flex', alignItems: 'center', justifyContent: 'center',
106 background: stage.status === 'running' ? statusColors[stage.status] + '30' : 'transparent',
107 fontSize: '18px', fontWeight: 'bold', color: statusColors[stage.status],
108 }}>
109 {statusIcons[stage.status]}
110 </div>
111 <div style={{ fontWeight: 'bold', fontSize: '14px', color: statusColors[stage.status] }}>{stage.name}</div>
112 {stage.duration && <div style={{ fontSize: '11px', color: '#8892b0' }}>{(stage.duration / 1000).toFixed(1)}s</div>}
113 <div style={{ marginTop: '8px' }}>
114 {stage.steps.map((step, j) => (
115 <div key={j} style={{ fontSize: '11px', color: '#555', padding: '1px 0' }}>{step}</div>
116 ))}
117 </div>
118 </div>
119 {i < stages.length - 1 && <div style={{ paddingTop: '20px', color: '#333', fontSize: '20px' }}>-></div>}
120 </React.Fragment>
121 ))}
122 </div>
123 </div>
124
125 <div style={{ background: '#1a1a2e', padding: '16px', borderRadius: '8px', border: '1px solid #333' }}>
126 <h3 style={{ color: '#f59e0b', fontSize: '14px', marginTop: 0 }}>Configuration ({activeProvider}) :</h3>
127 <pre style={{ fontSize: '12px', color: '#e0e0e0', margin: 0, overflow: 'auto', whiteSpace: 'pre-wrap' }}>{ciConfigs[activeProvider]}</pre>
128 </div>
129 </div>
130 );
131}
132
133export default CICDDemo;Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. CI/CD signifie :
2. Quelle est la différence entre Continuous Delivery et Continuous Deployment ?
Tâches pratiques dans le jeu
- Éditeur de code
Crée un workflow GitHub Actions déclenché par un push sur la branche main : checkout, setup-node, installation des dépendances, tests, build, puis déploiement sur Vercel avec le jeton du secret VERCEL_TOKEN.
- Tri vertical
Place les éléments dans le bon ordre : push du code → lancer les tests → builder l'app
- Tri vertical
Place la syntaxe de configuration du trigger d'un workflow GitHub Actions :
- Éditeur de code
Crée un endpoint API de health check avec connectivité à la base de données, état des services externes et métriques système