Leçon 5 / 6
Leçon 05 · Partie 2 — Aller plus loin

Déployer automatiquement

Stratégies de déploiement

GitHub Actions peut déclencher un déploiement après chaque push sur main. Selon ta cible, plusieurs stratégies sont disponibles :

📄

GitHub Pages

Idéal pour les sites statiques. Intégré nativement à GitHub, zéro infra à gérer.

statique · gratuit
🖥️

SSH vers VPS

Tu pousses ton code ou ton image Docker sur un serveur que tu contrôles via SSH.

VPS · Linux · rsync
🐳

Docker Hub + restart

Build et push d'une image Docker, puis pull et redémarrage du conteneur sur le serveur.

Docker · GHCR
🚀

Fly.io / Vercel

Plateformes cloud avec CLI intégrée à GitHub Actions. Déploiement en une commande.

PaaS · cloud

Déploiement GitHub Pages

GitHub Pages permet d'héberger un site statique directement depuis ton dépôt. Le workflow officiel utilise actions/upload-pages-artifact et actions/deploy-pages.

.github/workflows/deploy-pages.yml
name: Deploy to GitHub Pages

on:
  push:
    branches:
      - main                      # déclenché seulement sur main

permissions:
  contents: read
  pages: write                   # permission nécessaire pour Pages
  id-token: write               # requis par deploy-pages

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}

    steps:
      - name: Checkout
        uses: actions/checkout@v4

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

      - name: Install dependencies
        run: npm ci

      - name: Build site
        run: npm run build         # génère le dossier _site/

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: "_site"             # dossier de sortie du build

      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4
💡

Pour activer GitHub Pages, va dans Settings → Pages de ton dépôt et sélectionne la source GitHub Actions. Une fois activé, chaque push sur main déclenchera un build et mettra à jour ton site automatiquement.

Déploiement SSH vers un VPS

Pour déployer sur ton propre serveur, tu as besoin d'une clé SSH stockée en secret. L'action appleboy/ssh-action permet d'exécuter des commandes à distance en une étape.

.github/workflows/deploy-vps.yml
name: Deploy to VPS

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Sync files via rsync
        uses: appleboy/ssh-action@v1
        with:
          host:     ${{ secrets.SSH_HOST }}      # IP ou domaine du VPS
          username: ${{ secrets.SSH_USER }}      # ex: deploy
          key:      ${{ secrets.SSH_PRIVATE_KEY }} # clé privée Ed25519
          port:     ${{ secrets.SSH_PORT }}      # par défaut 22
          script: |
            # Aller dans le dossier de l'appli
            cd /var/www/mon-app

            # Récupérer le dernier code
            git pull origin main

            # Reinstaller les dépendances si besoin
            npm ci --production

            # Redémarrer l'application via PM2
            pm2 restart mon-app
⚠️

Ne stocke jamais une clé SSH dans ton code. Génère une paire de clés dédiée (ssh-keygen -t ed25519 -C "github-actions"), ajoute la clé publique dans ~/.ssh/authorized_keys sur ton serveur, et stocke la clé privée dans un secret GitHub (SSH_PRIVATE_KEY).

Variante : Docker pull + restart

Si tu déploies via Docker, le workflow peut builder l'image, la pousser sur GHCR, puis ordonner au serveur de la récupérer et redémarrer le conteneur :

Étape SSH — Docker pull & restart
      - name: Deploy via Docker
        uses: appleboy/ssh-action@v1
        with:
          host:     ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          key:      ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            # Tirer la nouvelle image
            docker pull ghcr.io/monorg/mon-app:latest

            # Redémarrer le conteneur
            docker compose -f /var/www/mon-app/compose.yaml up -d --no-deps app

Workflow complet : tests → build → deploy

Un pipeline de CI/CD professionnel enchaîne les étapes : on teste d'abord, on ne déploie que si tout est vert, et seulement depuis main.

.github/workflows/ci-cd.yml — pipeline complet
name: CI/CD — Test & Deploy

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

jobs:

  # ─────────────────────────────────────────
  # JOB 1 : Tests (toujours exécuté)
  # ─────────────────────────────────────────
  test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"            # cache node_modules entre les runs

      - name: Install dependencies
        run: npm ci

      - name: Run linter
        run: npm run lint

      - name: Run tests
        run: npm test

  # ─────────────────────────────────────────
  # JOB 2 : Build de l'image Docker
  # ─────────────────────────────────────────
  build:
    runs-on: ubuntu-latest
    needs: test                  # attend que les tests passent
    if: github.ref == 'refs/heads/main'  # seulement sur main

    permissions:
      contents: read
      packages: write             # pour pousser sur GHCR

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Log in to GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}  # token auto-fourni

      - name: Build and push image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            ghcr.io/${{ github.repository }}:latest
            ghcr.io/${{ github.repository }}:${{ github.sha }}

  # ─────────────────────────────────────────
  # JOB 3 : Déploiement en production
  # ─────────────────────────────────────────
  deploy:
    runs-on: ubuntu-latest
    needs: build                 # attend le build Docker
    if: github.ref == 'refs/heads/main'
    environment: production      # environnement protégé

    steps:
      - name: Deploy to VPS
        uses: appleboy/ssh-action@v1
        with:
          host:     ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          key:      ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            docker pull ghcr.io/${{ github.repository }}:latest
            docker compose -f /var/www/mon-app/compose.yaml up -d --no-deps app
            echo "Déploiement terminé ✓"

Le mot-clé needs crée une dépendance entre jobs. Si le job test échoue, le job build ne démarre pas — et donc deploy non plus. C'est la clé du déploiement conditionnel : on ne déploie jamais un code cassé.

Déploiement conditionnel

Plusieurs mécanismes permettent de contrôler quand un job s'exécute :

Conditions courantes
# Seulement sur la branche main
if: github.ref == 'refs/heads/main'

# Seulement si le job précédent a réussi (comportement par défaut avec needs:)
if: ${{ success() }}

# Seulement sur un push (pas sur une PR)
if: github.event_name == 'push'

# Seulement si un fichier précis a changé
if: contains(github.event.commits[0].message, '[deploy]')

# Sur un tag de version (ex: v1.2.3)
if: startsWith(github.ref, 'refs/tags/v')

Environnements et protections

GitHub permet de définir des environnements (staging, production…) avec des règles de protection. Un environnement peut exiger une approbation manuelle avant tout déploiement.

Déclarer un environnement dans le job
  deploy:
    runs-on: ubuntu-latest
    needs: build
    environment: production      # réf. à l'environnement GitHub

    steps:
      # Si "Required reviewers" est activé sur cet environnement,
      # le workflow s'arrête ici et attend l'approbation manuelle.
      - name: Deploy
        run: ./deploy.sh
💡

Configure les environnements dans Settings → Environments de ton dépôt. Tu peux y définir des Required reviewers (approbation manuelle obligatoire), des Wait timer (délai avant déploiement) et des secrets spécifiques à cet environnement, différents des secrets globaux du dépôt.

// À retenir
  • GitHub Pages, SSH/rsync, Docker+GHCR et les PaaS (Fly.io, Vercel) sont les principales cibles de déploiement depuis Actions.
  • Stocke la clé SSH privée dans un secret GitHub — jamais dans le code.
  • needs: enchaîne les jobs : si les tests échouent, le déploiement ne démarre pas.
  • if: github.ref == 'refs/heads/main' limite le déploiement à la branche principale.
  • environment: production active les protections GitHub (approbation manuelle, secrets dédiés).