Lecture : 10 minutes · Niveau : intermédiaire · Mise à jour : avril 2026
GitHub Actions est devenu la plateforme CI/CD la plus accessible pour les équipes hébergées sur GitHub. Quota mensuel généreux, écosystème immense d’actions réutilisables, intégration native avec les pull requests : un setup minimal couvre déjà la grande majorité des besoins courants. Ce guide montre comment structurer des workflows propres pour une PME, sans tomber dans les pièges classiques.
Voir aussi → DevOps moderne pour PME : guide CI/CD et IaC.
Sommaire
- Anatomie d’un workflow
- Premier pipeline test + lint
- Cache des dépendances
- Matrices de tests multi-versions
- Secrets et variables d’environnement
- Déploiement sur serveur via SSH
- Build et push d’images Docker
- Notifications Slack et email
- Optimisation des coûts
- FAQ
1. Anatomie d’un workflow
Un workflow GitHub Actions est un fichier YAML dans .github/workflows/. Chaque workflow définit :
on: les événements déclencheurs (push, pull_request, schedule, workflow_dispatch pour manuel)jobs: un ou plusieurs jobs qui s’exécutent en parallèle par défautsteps: la suite ordonnée d’étapes dans un jobruns-on: le type de runner (ubuntu-latest, macos-latest, windows-latest, self-hosted)
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "Hello CI"
Un job tourne dans un environnement éphémère propre. Tout ce qui doit persister entre jobs passe par des artifacts (actions/upload-artifact) ou un cache. Les jobs s’enchaînent via la directive needs:.
2. Premier pipeline test + lint
Pipeline minimal pour une application Node.js :
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Tests
run: npm test
- name: Build
run: npm run build
npm ci (au lieu de npm install) garantit que package-lock.json est respecté à la lettre — indispensable en CI pour la reproductibilité.
L’équivalent Python :
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- run: pip install -r requirements.txt
- run: ruff check .
- run: pytest
3. Cache des dépendances
L’installation des dépendances domine souvent le temps total du pipeline. Le cache réduit ces minutes à quelques secondes.
actions/setup-node et actions/setup-python intègrent un cache automatique via cache: 'npm' ou cache: 'pip'. Pour un cache custom :
- uses: actions/cache@v4
with:
path: |
~/.npm
node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
La clé inclut le hash du fichier de lock : si rien ne change, hit immédiat. Si le lock change, fallback sur la clé précédente puis re-cache.
Pour un projet avec dépendances lourdes, le passage au cache peut diviser par 5 le temps de pipeline. À mettre en place dès le début.
4. Matrices de tests multi-versions
Tester sur plusieurs versions de runtime, plusieurs OS, ou plusieurs versions de dépendance se fait via la directive matrix.
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
node: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm ci
- run: npm test
Cette matrice lance 6 jobs parallèles. fail-fast: false permet à tous de s’exécuter même si un échoue (utile pour identifier exactement quelle combinaison casse).
Pour une PME, tester sur 2-3 versions du runtime principal suffit largement. Pas besoin d’élargir au-delà du raisonnable.
5. Secrets et variables d’environnement
GitHub propose plusieurs niveaux de stockage de secrets :
- Repository secrets : visibles à tout workflow du dépôt
- Environment secrets : limités à un environnement nommé (production, staging) avec règles d’approbation possibles
- Organization secrets : partagés entre plusieurs dépôts
Configuration via Settings → Secrets and variables → Actions.
Usage dans un workflow :
- name: Deploy
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
run: ./scripts/deploy.sh
GitHub masque automatiquement les secrets dans les logs (remplacés par ***). Mais attention : un secret affiché en transformé (en base64 par exemple) ne sera pas masqué. Ne jamais echo un secret, même décodé.
Pour les environnements protégés (production), activer les règles : approbation manuelle d’un reviewer, branches autorisées (main seulement), wait timer optionnel. Cela donne un contrôle supplémentaire sur les déploiements sensibles.
6. Déploiement sur serveur via SSH
Cas typique : déployer sur un serveur Linux après tests passés.
deploy:
needs: ci
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment: production
steps:
- uses: actions/checkout@v4
- name: Setup SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H ${{ secrets.SERVER_HOST }} >> ~/.ssh/known_hosts
- name: Deploy
run: |
ssh -i ~/.ssh/id_ed25519 deploy@${{ secrets.SERVER_HOST }} \
"cd /opt/app && git pull && docker compose up -d --build"
Préférer une clé SSH dédiée au déploiement plutôt que la clé personnelle d’un développeur. La clé doit avoir des permissions limitées au strict nécessaire (un user deploy avec sudo ciblé sur quelques commandes via sudoers).
Alternative plus structurée : utiliser une action dédiée comme appleboy/ssh-action qui factorise la setup SSH :
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: deploy
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /opt/app
git pull
docker compose up -d --build
7. Build et push d’images Docker
Pour une PME qui utilise des conteneurs, builder et pousser des images Docker dans un registre est un workflow récurrent.
build-image:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Buildx
uses: docker/setup-buildx-action@v3
- 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
Plusieurs choses à noter :
ghcr.io(GitHub Container Registry) est gratuit pour les dépôts publics et inclus dans les forfaits payants pour les privés- Tagger avec
latestET le SHA du commit donne une référence stable au commit exact - Le cache
type=ghaaccélère significativement les builds suivants
Pour pousser vers Docker Hub, AWS ECR, GCP Artifact Registry : changer le registre dans login-action et le tag.
8. Notifications Slack et email
Recevoir un signal quand un pipeline échoue est essentiel.
Slack via webhook
Créer un webhook Slack dans Apps → Incoming Webhooks. Stocker l’URL en secret SLACK_WEBHOOK.
- name: Notify Slack on failure
if: failure()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "Pipeline failed: ${{ github.workflow }} on ${{ github.ref_name }}",
"username": "GitHub Actions",
"icon_emoji": ":warning:"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
if: failure() cible les notifications uniquement aux échecs.
Email simple
GitHub envoie déjà des emails sur les échecs de workflow vers les commiters et les watchers. Pour une notification email plus riche, des actions tierces existent comme dawidd6/action-send-mail.
9. Optimisation des coûts
Pour les dépôts privés, GitHub facture les minutes consommées au-delà du quota inclus dans le forfait. Quelques pratiques limitent la facture :
- Cache des dépendances : déjà mentionné, gain le plus important
- Conditions sur les workflows : éviter de relancer inutilement avec
paths:filters etconcurrency:groups - Annuler les workflows obsolètes : si un nouveau push arrive sur une PR, annuler l’ancien
- Self-hosted runners : pour de gros volumes, héberger les runners sur ses propres serveurs élimine les coûts par minute (mais ajoute la maintenance)
# Annuler les workflows en cours sur la même PR
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# Ne déclencher que si certains chemins changent
on:
push:
paths:
- 'src/**'
- 'package*.json'
- '.github/workflows/**'
Voir aussi → Docker en production pour PME pour optimiser les builds Docker eux-mêmes.
10. FAQ
Workflow déclenché manuellement, comment ?
Avec workflow_dispatch: :
on:
workflow_dispatch:
inputs:
environment:
type: choice
options: [staging, production]
L’utilisateur peut alors lancer le workflow depuis l’onglet Actions avec choix de paramètres.
Comment partager des étapes communes entre workflows ?
Trois approches : composite actions (factoriser plusieurs steps dans une action réutilisable du même dépôt), reusable workflows (factoriser un workflow complet appelable par d’autres workflows), ou simplement extraire un script bash que plusieurs workflows appellent. Reusable workflows est l’approche la plus structurée pour des organisations avec plusieurs dépôts similaires.
Le pipeline tourne mais le déploiement échoue silencieusement, que vérifier ?
Activer la vérification d’exit code en cas de pipe : set -euo pipefail au début des scripts shell. Sinon une commande qui échoue dans un pipe ne fait pas échouer le step. Vérifier aussi les logs du serveur cible côté SSH : le script: peut s’exécuter mais l’application peut planter au démarrage sans que GitHub Actions le voie.
Combien de temps doit prendre un pipeline CI raisonnable ?
Cible : moins de 10 minutes pour le feedback complet sur une PR, moins de 5 minutes idéalement. Au-delà, les développeurs commencent à attendre, à perdre le contexte, à pousser sans attendre. Optimiser activement quand la durée dépasse 8-10 minutes.
Self-hosted runners ou hosted runners par défaut ?
Hosted (GitHub-fournis) par défaut. Self-hosted devient pertinent à partir d’un certain volume mensuel (typiquement plus de 5000-10000 minutes) ou de besoins spécifiques (GPUs, accès réseau privé, OS particulier). La maintenance des self-hosted runners n’est pas anodine.
Comment éviter de versionner accidentellement les node_modules dans le cache ?
Le cache GitHub Actions est séparé du dépôt git, donc pas de risque côté git. Mais attention à cache-key : si la clé est trop large (ex: ne contient pas le hash du lockfile), un cache obsolète peut polluer les builds.
Articles liés (cluster DevOps moderne)
- 👉 DevOps moderne pour PME : guide CI/CD et IaC (pillar)
- 👉 Terraform et Ansible : infrastructure as code en pratique
- 👉 Docker en production pour PME
Article mis à jour le 25 avril 2026. Pour signaler une erreur ou suggérer une amélioration, écrivez-nous.