CI/CD avec GitHub Actions : Pipelines Modernes pour Next.js
CI/CD
GitHub Actions
DevOps
Next.js

CI/CD avec GitHub Actions : Pipelines Modernes pour Next.js

FS
Fernand SOUALO
·
6 min read

CI/CD avec GitHub Actions : Pipelines Modernes pour Next.js

Table of Contents#

  1. Pourquoi GitHub Actions ?
  2. Anatomie d'un Workflow
  3. Pipeline CI Complet
  4. Tests et Qualité
  5. Preview Deployments
  6. Déploiement Production
  7. Docker Build et Registry
  8. Secrets et Sécurité
  9. Optimisations Avancées
  10. Conclusion

Pourquoi GitHub Actions ?#

GitHub Actions est devenu le standard de facto pour le CI/CD moderne. Intégré nativement à GitHub, il offre un écosystème de workflows réutilisables et une simplicité redoutable.

Génération du diagramme…
Pipeline CI/CD complet : du push au déploiement production.

Anatomie d'un Workflow#

Un workflow GitHub Actions est un fichier YAML dans .github/workflows/ :

YAML
# .github/workflows/ci.yml
name: CI Pipeline

# Quand déclencher
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

# Variables globales
env:
  NODE_VERSION: "20"
  PNPM_VERSION: "9"

# Les jobs
jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup pnpm
        uses: pnpm/action-setup@v4
        with:
          version: ${{ env.PNPM_VERSION }}

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: pnpm

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Run checks
        run: |
          pnpm lint
          pnpm typecheck
          pnpm test

Pipeline CI Complet#

Stratégie multi-jobs parallèles#

YAML
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

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

jobs:
  # Job 1: Lint et TypeCheck (rapide)
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm lint
      - run: pnpm tsc --noEmit

  # Job 2: Tests unitaires (parallèle)
  unit-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm test:unit -- --coverage
      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}

  # Job 3: Build (dépend de quality)
  build:
    needs: [quality]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm build
      - name: Upload build artifact
        uses: actions/upload-artifact@v4
        with:
          name: nextjs-build
          path: .next/
          retention-days: 1

  # Job 4: Tests E2E (dépend de build)
  e2e-tests:
    needs: [build]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - name: Download build
        uses: actions/download-artifact@v4
        with:
          name: nextjs-build
          path: .next/
      - name: Install Playwright
        run: npx playwright install --with-deps chromium
      - name: Run E2E tests
        run: pnpm test:e2e
      - name: Upload test report
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: playwright-report
          path: playwright-report/

Tests et Qualité#

Matrice de tests multi-environnement#

YAML
test-matrix:
  strategy:
    matrix:
      node-version: [18, 20, 22]
      os: [ubuntu-latest, windows-latest]
    fail-fast: false
  runs-on: ${{ matrix.os }}
  steps:
    - uses: actions/checkout@v4
    - uses: pnpm/action-setup@v4
    - uses: actions/setup-node@v4
      with:
        node-version: ${{ matrix.node-version }}
        cache: pnpm
    - run: pnpm install --frozen-lockfile
    - run: pnpm test

Gate de qualité obligatoire#

YAML
# Protéger la branche main
quality-gate:
  needs: [quality, unit-tests, build, e2e-tests]
  runs-on: ubuntu-latest
  if: always()
  steps:
    - name: Check all jobs
      run: |
        if [ "${{ needs.quality.result }}" != "success" ] || \
           [ "${{ needs.unit-tests.result }}" != "success" ] || \
           [ "${{ needs.build.result }}" != "success" ] || \
           [ "${{ needs.e2e-tests.result }}" != "success" ]; then
          echo "One or more jobs failed"
          exit 1
        fi

Preview Deployments#

Déploiement de preview automatique sur PR#

YAML
preview:
  if: github.event_name == 'pull_request'
  needs: [build]
  runs-on: ubuntu-latest
  environment:
    name: preview-${{ github.event.number }}
    url: ${{ steps.deploy.outputs.url }}
  steps:
    - uses: actions/checkout@v4

    - name: Deploy to Vercel Preview
      id: deploy
      uses: amondnet/vercel-action@v25
      with:
        vercel-token: ${{ secrets.VERCEL_TOKEN }}
        vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
        vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}

    - name: Comment PR with preview URL
      uses: actions/github-script@v7
      with:
        script: |
          github.rest.issues.createComment({
            issue_number: context.issue.number,
            owner: context.repo.owner,
            repo: context.repo.repo,
            body: `## 🚀 Preview Deployment\n\n**URL:** ${{ steps.deploy.outputs.url }}\n**Status:** ✅ Deployed`
          })

Déploiement Production#

YAML
deploy-production:
  if: github.ref == 'refs/heads/main' && github.event_name == 'push'
  needs: [quality-gate]
  runs-on: ubuntu-latest
  environment:
    name: production
    url: https://fygs.dev
  steps:
    - uses: actions/checkout@v4

    - name: Deploy to Vercel Production
      uses: amondnet/vercel-action@v25
      with:
        vercel-token: ${{ secrets.VERCEL_TOKEN }}
        vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
        vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
        vercel-args: "--prod"

    - name: Notify deployment
      if: success()
      uses: actions/github-script@v7
      with:
        script: |
          const { data: commit } = await github.rest.repos.getCommit({
            owner: context.repo.owner,
            repo: context.repo.repo,
            ref: context.sha
          });
          console.log(`Deployed: ${commit.commit.message}`);

Docker Build et Registry#

YAML
docker:
  if: github.ref == 'refs/heads/main'
  needs: [quality-gate]
  runs-on: ubuntu-latest
  permissions:
    contents: read
    packages: write
  steps:
    - uses: actions/checkout@v4

    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v3

    - name: Login to GitHub Container Registry
      uses: docker/login-action@v3
      with:
        registry: ghcr.io
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}

    - name: Build and push
      uses: docker/build-push-action@v5
      with:
        context: .
        push: true
        tags: |
          ghcr.io/${{ github.repository }}:latest
          ghcr.io/${{ github.repository }}:${{ github.sha }}
        cache-from: type=gha
        cache-to: type=gha,mode=max

Secrets et Sécurité#

YAML
# Utiliser les secrets correctement
env:
  DATABASE_URL: ${{ secrets.DATABASE_URL }}

# Scanner les dépendances
security-scan:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - name: Run Trivy vulnerability scanner
      uses: aquasecurity/trivy-action@master
      with:
        scan-type: "fs"
        scan-ref: "."
        severity: "CRITICAL,HIGH"
        exit-code: "1"

Règle d'or : Ne jamais mettre de secrets dans le code. Utilisez ${{ secrets.* }} pour toutes les valeurs sensibles.

Optimisations Avancées#

Cache intelligent#

YAML
# Cache pnpm store
- uses: actions/cache@v4
  with:
    path: |
      ~/.pnpm-store
      .next/cache
    key: ${{ runner.os }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
    restore-keys: |
      ${{ runner.os }}-pnpm-

Workflows réutilisables#

YAML
# .github/workflows/reusable-test.yml
name: Reusable Test Workflow
on:
  workflow_call:
    inputs:
      node-version:
        type: string
        default: "20"
    secrets:
      codecov-token:
        required: false

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node-version }}
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm test -- --coverage

# Utilisation
# .github/workflows/ci.yml
jobs:
  test:
    uses: ./.github/workflows/reusable-test.yml
    with:
      node-version: "20"
    secrets:
      codecov-token: ${{ secrets.CODECOV_TOKEN }}

Conclusion#

Un pipeline CI/CD bien conçu est la colonne vertébrale d'un projet professionnel :

  • Jobs parallèles pour réduire le temps total du pipeline
  • Concurrency pour annuler les runs obsolètes
  • Preview deployments pour faciliter la revue de code
  • Quality gates pour protéger la branche principale
  • Cache pour accélérer chaque run
  • Workflows réutilisables pour maintenir la cohérence entre projets
  • Secrets correctement gérés pour la sécurité

Le CI/CD n'est pas une option — c'est l'infrastructure qui rend le développement rapide et fiable.

Cet article vous a été utile ?

6 min read
0 vues
0 j'aime
0 partages