TL;DR — Résumé Rapide

GitHub Actions: créez des pipelines de déploiement automatisés avec protection des environnements, gestion des secrets, cibles multiples et rollback.

PIPELINE DE DÉPLOIEMENT AUTOMATISÉ — GITHUB ACTIONS git push déclencheur Lint + Test qualité Build artefacts Approbation gate Déployer production Notifier Slack push → lint → test → build → approuver → déployer → notifier

Déployer manuellement — se connecter en SSH à un serveur, lancer des commandes de build, croiser les doigts — est source d’erreurs, intraçable et ne passe pas à l’échelle. GitHub Actions permet de définir tout le pipeline de déploiement sous forme de code : lint, tests, build, approbation, déploiement et notification — le tout déclenché automatiquement à chaque push sur main. Ce guide couvre la création d’un workflow de déploiement automatisé en production, du premier fichier YAML aux déploiements multi-cibles, à la protection des environnements et aux stratégies de rollback.

Prérequis

Avant de créer votre workflow de déploiement, assurez-vous d’avoir en place :

  • Un dépôt GitHub avec du code applicatif (exemples Node.js tout au long du guide)
  • Un environnement cible — compte Cloudflare Workers, un VPS avec accès SSH, ou un runner auto-hébergé Windows pour IIS
  • Une connaissance de base du YAML — les workflows GitHub Actions sont des fichiers YAML où l’indentation est importante
  • L’accès aux paramètres du dépôt GitHub pour stocker des secrets et créer des environnements

Concepts de Base de GitHub Actions

Un workflow GitHub Actions est un fichier YAML stocké dans .github/workflows/. Il est déclenché par un ou plusieurs événements (push, pull request, planification, déclenchement manuel). Un workflow contient un ou plusieurs jobs. Chaque job s’exécute sur un runner — une machine virtuelle hébergée par GitHub (Ubuntu, Windows, macOS) ou auto-hébergée. Chaque job contient des steps séquentiels, chacun exécutant soit une commande shell (run), soit une unité réutilisable appelée action (uses).

Les blocs fondamentaux :

name: Deploy                      # nom du workflow dans l'onglet Actions

on:                               # déclencheur
  push:
    branches: [main]

jobs:
  deploy:                         # ID du job
    runs-on: ubuntu-latest        # runner
    steps:
      - uses: actions/checkout@v4 # action (récupère le code source)
      - run: npm ci               # commande shell
      - run: npm run build

Les jobs s’exécutent en parallèle par défaut. Utilisez needs: [job-id] pour imposer un ordre.

Votre Premier Workflow de Déploiement

Voici un workflow complet pour un projet Node.js qui déploie sur Cloudflare Workers avec Wrangler. Il exécute d’abord lint et tests, puis build, puis déploie — uniquement sur les pushs vers main.

name: Déployer sur Cloudflare Workers

on:
  push:
    branches: [main]

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

jobs:
  lint-and-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "npm"
      - run: npm ci
      - run: npm run lint
      - run: npm test

  build:
    needs: lint-and-test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "npm"
      - run: npm ci
      - run: npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist/

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4
      - uses: actions/download-artifact@v4
        with:
          name: dist
          path: dist/
      - name: Déployer avec Wrangler
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CF_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
      - name: Notifier le succès sur Slack
        if: success()
        run: |
          curl -s -X POST "${{ secrets.SLACK_WEBHOOK }}" \
            -H "Content-Type: application/json" \
            -d "{\"text\":\"Déployé ${{ github.sha }} en production par ${{ github.actor }}\"}"

Le bloc concurrency en haut empêche deux déploiements de se chevaucher. Si vous faites deux pushs rapidement, la première exécution est annulée avant que la seconde commence.

Règles de Protection des Environnements

Pousser directement en production sans vérification humaine est risqué pour les systèmes critiques. Les environnements GitHub permettent d’ajouter des règles de protection à tout job qui les cible.

Pour configurer la protection d’environnement :

  1. Allez dans Settings → Environments de votre dépôt
  2. Cliquez sur New environment et nommez-le production
  3. Activez Required reviewers — ajoutez un ou plusieurs utilisateurs ou équipes GitHub
  4. Définissez optionnellement un Wait timer (par exemple, 10 minutes) avant que le job puisse s’exécuter
  5. Restreignez optionnellement les branches pouvant déployer dans cet environnement (par exemple, uniquement main)

Une fois configuré, tout job avec environment: production se mettra en pause et attendra l’approbation d’un relecteur avant de continuer. Le relecteur voit le déploiement en attente dans l’interface GitHub et peut approuver ou rejeter avec un commentaire.

Gestion des Secrets

Ne codez jamais en dur des tokens API, clés SSH ou mots de passe dans les fichiers de workflow. GitHub propose trois niveaux de stockage des secrets :

Secrets de dépôt — Disponibles pour tous les workflows du repo. Allez dans Settings → Secrets and variables → Actions → New repository secret.

Secrets d’environnement — Limités à un environnement spécifique (par exemple, production). Exposés uniquement lorsqu’un job cible cet environnement.

Secrets d’organisation — Partagés entre plusieurs dépôts d’une organisation GitHub, idéaux pour les tokens utilisés par de nombreux projets.

Référencez les secrets dans les workflows avec la syntaxe ${{ secrets.NOM_SECRET }} :

- name: Déployer avec Wrangler
  uses: cloudflare/wrangler-action@v3
  with:
    apiToken: ${{ secrets.CF_API_TOKEN }}
    accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}

OIDC (OpenID Connect) est une alternative aux secrets de longue durée pour les fournisseurs cloud qui le supportent (AWS, GCP, Azure). Au lieu de stocker un token statique, le runner demande une credential de courte durée au fournisseur à l’exécution.

Déploiement vers Différentes Cibles

Cloudflare Workers avec Wrangler

L’action cloudflare/wrangler-action gère l’authentification et exécute n’importe quelle commande Wrangler :

- uses: cloudflare/wrangler-action@v3
  with:
    apiToken: ${{ secrets.CF_API_TOKEN }}
    accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
    command: deploy --env production

VPS via SSH et rsync

Pour un VPS Linux, utilisez SSH pour vous connecter et rsync pour transférer les fichiers :

- name: Déployer sur VPS
  uses: appleboy/ssh-action@v1
  with:
    host: ${{ secrets.DEPLOY_HOST }}
    username: ${{ secrets.DEPLOY_USER }}
    key: ${{ secrets.DEPLOY_SSH_KEY }}
    script: |
      rsync -az --delete dist/ /var/www/myapp/dist/
      systemctl restart myapp

Stockez la clé SSH privée dans les secrets GitHub. Ajoutez la clé publique correspondante dans ~/.ssh/authorized_keys sur le serveur. Utilisez un utilisateur de déploiement dédié avec des permissions minimales — jamais root.

IIS via Runner Auto-Hébergé

Pour les déploiements Windows IIS, exécutez un runner auto-hébergé directement sur le serveur :

deploy-iis:
  needs: build
  runs-on: [self-hosted, iis-deploy]
  environment: production
  steps:
    - uses: actions/download-artifact@v4
      with:
        name: dist
        path: C:\inetpub\wwwroot\myapp
    - name: Redémarrer le site IIS
      run: |
        Import-Module WebAdministration
        Restart-WebItem 'IIS:\Sites\MyApp'
      shell: powershell

Cache et Performance

Cache des Dépendances

Re-télécharger les packages npm à chaque exécution est une perte de temps. Mettez-les en cache :

- uses: actions/setup-node@v4
  with:
    node-version: "22"
    cache: "npm"

Pour .NET :

- uses: actions/cache@v4
  with:
    path: ~/.nuget/packages
    key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
    restore-keys: ${{ runner.os }}-nuget-

Groupes de Concurrence

Les groupes de concurrence évitent les déploiements parallèles — source fréquente de race conditions :

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

Comparatif : GitHub Actions vs GitLab CI vs Jenkins

FonctionnalitéGitHub ActionsGitLab CIJenkins
HébergementGitHub (SaaS)GitLab ou auto-hébergéAuto-hébergé uniquement
Fichier de config.github/workflows/*.yml.gitlab-ci.ymlJenkinsfile (Groovy)
Plan gratuit2 000 min/mois (privé)400 min/mois (privé)Illimité (votre infra)
Marketplace20 000+ actionsLimité1 800+ plugins
Gates d’environnementNatifs (gratuit pour le public)Natifs (Ultimate pour les règles)Via plugins
OIDCAWS, GCP, Azure, CloudflareAWS, GCP, AzureVia plugins
Courbe d’apprentissageFaibleFaibleÉlevée (DSL Groovy)
Idéal pourProjets sur GitHubMonorepos sur GitLabPipelines legacy complexes

Scénario Réel : Pipeline Complet

Un pipeline de production pour Node.js avec notifications Slack :

name: Déploiement Production

on:
  push:
    branches: [main]

concurrency:
  group: production-deploy
  cancel-in-progress: true

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "npm"
      - run: npm ci
      - run: npm run lint

  test:
    needs: lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "npm"
      - run: npm ci
      - run: npm test -- --coverage

  build:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "npm"
      - run: npm ci
      - run: npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: dist-${{ github.sha }}
          path: dist/
          retention-days: 14

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4
      - uses: actions/download-artifact@v4
        with:
          name: dist-${{ github.sha }}
          path: dist/
      - uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CF_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
      - name: Notifier le succès
        if: success()
        run: |
          curl -s -X POST "${{ secrets.SLACK_WEBHOOK }}" \
            -d "{\"text\":\"Déployé ${{ github.sha }} par ${{ github.actor }}\"}"
      - name: Notifier l'échec
        if: failure()
        run: |
          curl -s -X POST "${{ secrets.SLACK_WEBHOOK }}" \
            -d "{\"text\":\"ÉCHEC du déploiement pour ${{ github.sha }}\"}"

Badges de Statut

Ajoutez un badge de statut en direct à votre README :

![Deploy](https://github.com/votre-org/votre-repo/actions/workflows/deploy.yml/badge.svg)

Pièges et Cas Limites

Le masquage des secrets ne fonctionne que pour les correspondances exactes. GitHub masque la valeur littérale du secret dans les logs, mais si le secret est encodé en base64 ou URL avant utilisation, la valeur originale peut apparaître sans masque.

Les changements d’image de runner cassent les versions d’outils épinglées. GitHub met à jour ubuntu-latest périodiquement. Si votre workflow dépend d’une version spécifique d’un outil préinstallé, installez-la explicitement avec une action de setup.

cancel-in-progress: true annule l’exécution en cours, pas celle qui arrive. Le nouveau push l’emporte. C’est presque toujours ce que vous voulez pour les déploiements.

Les secrets d’environnement ne sont pas disponibles pour les pull requests de forks. C’est intentionnel — les forks ne peuvent pas accéder aux secrets pour prévenir les fuites.

Résolution des Problèmes

Le workflow ne se déclenche pas sur un push vers main. Vérifiez le filtre on.push.branches — le nom de la branche doit correspondre exactement. Confirmez que le fichier de workflow est bien dans .github/workflows/.

Le secret est disponible mais l’authentification Wrangler échoue. Confirmez que le nom du secret correspond exactement (sensible à la casse). Vérifiez que le token API Cloudflare a les permissions correctes : Workers Scripts: Edit et Account: Read.

Le job attend indéfiniment l’approbation de l’environnement. Vérifiez que vous êtes listé comme relecteur requis de l’environnement. Confirmez que le nom de l’environnement dans le YAML correspond exactement au nom dans Settings.

Résumé

  • Définissez les workflows sous forme de fichiers YAML dans .github/workflows/ — ils sont versionnés avec votre code
  • Structurez les pipelines en jobs séquentiels : lint → test → build → deploy, en utilisant needs pour imposer l’ordre
  • Utilisez environment: production avec des relecteurs requis pour des gates d’approbation humaine
  • Stockez tous les secrets dans le gestionnaire de secrets GitHub — jamais dans le fichier YAML
  • Les déploiements Cloudflare Workers utilisent cloudflare/wrangler-action ; les cibles VPS utilisent SSH/rsync ; IIS utilise un runner auto-hébergé
  • Ajoutez des groupes de concurrency avec cancel-in-progress: true pour éviter les déploiements qui se chevauchent
  • Mettez en cache les dépendances avec actions/setup-node cache: npm pour des exécutions plus rapides
  • Nommez les artefacts avec ${{ github.sha }} pour faciliter le rollback en relançant le workflow sur n’importe quel commit précédent

Articles Connexes