[OK] GITHUB ACTIONS - GUIDE COMPLET POUR GRANDS DÉBUTANTS


═══════════════════════════════════════════════════════════════════
 PARTIE 1 : COMPRENDRE LES BASES (POUR VRAIS DÉBUTANTS)
═══════════════════════════════════════════════════════════════════


# === C'EST QUOI GITHUB ACTIONS ? ===

Imaginez que vous avez un robot personnel qui travaille pour vous 24h/24.
Chaque fois que vous mettez du code sur GitHub, ce robot :
  1. Télécharge votre code
  2. Lance tous vos tests automatiquement
  3. Vérifie que le code est propre
  4. Vous envoie un email pour dire si tout va bien ou s'il y a un problème

C'est exactement ça, GitHub Actions !


# === POURQUOI C'EST UTILE ? ===

SANS GitHub Actions:
  1. Vous codez une nouvelle fonctionnalité
  2. Vous faites "git push"
  3. Vous devez MANUELLEMENT :
     - Ouvrir votre terminal
     - Lancer les tests (pytest)
     - Vérifier le style de code (flake8)
     - Vérifier les types (mypy)
     - Déployer si tout est OK
  4. Si vous oubliez une étape -> BUG EN PRODUCTION ! [!]

AVEC GitHub Actions:
  1. Vous codez une nouvelle fonctionnalité
  2. Vous faites "git push"
  3. GitHub Actions fait AUTOMATIQUEMENT toutes les vérifications
  4. Vous recevez un email : [OK] "Tout est OK" ou [X] "Il y a un problème"
  5. Vous pouvez aller boire un café pendant que le robot travaille [HOT_BEVERAGE]


# === EXEMPLES CONCRETS DU QUOTIDIEN ===

Exemple 1 : Tests automatiques
  Situation : Vous modifiez du code dans une Pull Request
  Sans Actions : Votre collègue doit télécharger, installer, tester
  Avec Actions : Les tests s'exécutent automatiquement, résultat en 2 minutes

Exemple 2 : Déploiement
  Situation : Nouvelle version de votre app à déployer
  Sans Actions : Connexion SSH, git pull, redémarrer serveur... (stressant!)
  Avec Actions : Push sur main -> déploiement automatique

Exemple 3 : Publication de package
  Situation : Nouvelle version de votre bibliothèque Python
  Sans Actions : Build manuel, upload sur PyPI (10 étapes à ne pas oublier)
  Avec Actions : Créer un tag -> publication automatique sur PyPI


# === OÙ ÇA SE PASSE ? ===

Tout se passe sur GitHub.com, dans l'onglet "Actions" de votre repository.

Voici comment naviguer :
  1. Ouvrez votre repository sur GitHub.com
  2. Cliquez sur l'onglet "Actions" (entre "Pull requests" et "Projects")
  3. Vous verrez la liste de tous les workflows qui ont été exécutés
  4. Chaque ligne = une exécution (avec [OK] vert si OK, [X] rouge si problème)
  5. Cliquez sur une ligne pour voir les détails

C'est comme un historique de tout ce que le robot a fait pour vous.


# === C'EST GRATUIT ? ===

OUI, totalement gratuit si votre repository est PUBLIC !

Pour les repositories PRIVÉS :
  - Plan gratuit : 2000 minutes par mois
  - C'est largement suffisant pour débuter
  
Comment sont comptées les minutes ?
  - Linux (Ubuntu) : 1 minute réelle = 1 minute comptée
  - Windows : 1 minute réelle = 2 minutes comptées
  - macOS : 1 minute réelle = 10 minutes comptées (cher !)
  
Conseil : Utilisez toujours Linux (ubuntu-latest) sauf si vraiment nécessaire.


# === DE QUOI AI-JE BESOIN POUR COMMENCER ? ===

Checklist minimale :
  [OK] Un compte GitHub (gratuit)
  [OK] Un repository avec du code Python
  [OK] Savoir faire : git add, git commit, git push
  [OK] Avoir un fichier requirements.txt (liste de vos dépendances)
  [OK] 10 minutes de votre temps

C'est tout ! Vous n'avez besoin d'installer RIEN sur votre ordinateur.
Tout se passe sur les serveurs de GitHub.


═══════════════════════════════════════════════════════════════════
 PARTIE 2 : LES 5 CONCEPTS À COMPRENDRE
═══════════════════════════════════════════════════════════════════


# === CONCEPT 1 : LE WORKFLOW (fichier de configuration) ===

QU'EST-CE QUE C'EST ?
  Un workflow, c'est simplement un FICHIER qui dit au robot quoi faire.
  C'est comme une recette de cuisine, mais pour l'automatisation.

OÙ EST CE FICHIER ?
  Dans votre projet : .github/workflows/nom-du-fichier.yml
  
  Exemple de structure :
    mon-projet/
    ├── .github/
    │   └── workflows/
    │       ├── test.yml          <- Workflow pour les tests
    │       └── deploy.yml        <- Workflow pour le déploiement
    ├── src/
    ├── tests/
    └── requirements.txt

IMPORTANT : 
  - Le dossier DOIT s'appeler .github/workflows/ (avec le point devant!)
  - L'extension DOIT être .yml ou .yaml
  - Vous pouvez avoir autant de fichiers .yml que vous voulez


# === CONCEPT 2 : LES ÉVÉNEMENTS (quand ça se lance) ===

QU'EST-CE QUE C'EST ?
  Un événement, c'est ce qui déclenche le robot.
  C'est comme une alarme qui dit "Go ! Au travail !"

LES 4 ÉVÉNEMENTS LES PLUS COURANTS :

  1. PUSH (vous poussez du code)
     Quand : Chaque fois que vous faites "git push"
     Cas d'usage : Lancer les tests automatiquement
     
  2. PULL_REQUEST (quelqu'un crée une PR)
     Quand : Une Pull Request est créée ou modifiée
     Cas d'usage : Vérifier que la PR ne casse rien
     
  3. SCHEDULE (à intervalles réguliers)
     Quand : Tous les jours à 3h du matin (par exemple)
     Cas d'usage : Sauvegardes, nettoyage, rapports
     
  4. WORKFLOW_DISPATCH (manuel)
     Quand : Vous cliquez sur un bouton dans GitHub
     Cas d'usage : Déploiement manuel, tâches ponctuelles

ANALOGIE :
  Imaginez un système d'alarme pour votre maison.
  - PUSH = Alarme se déclenche quand quelqu'un entre
  - SCHEDULE = Alarme se déclenche tous les jours à 22h
  - WORKFLOW_DISPATCH = Vous appuyez sur un bouton pour déclencher l'alarme


# === CONCEPT 3 : LES JOBS (les tâches à faire) ===

QU'EST-CE QUE C'EST ?
  Un job, c'est un GROUPE de choses à faire ensemble.
  C'est comme une to-do list pour une tâche spécifique.

EXEMPLE CONCRET :
  Workflow "Vérifier le code" peut avoir 3 jobs :
    Job 1 : "test"      -> Lancer tous les tests
    Job 2 : "lint"      -> Vérifier le style du code
    Job 3 : "deploy"    -> Déployer si test + lint OK

IMPORTANT :
  - Plusieurs jobs s'exécutent EN PARALLÈLE (en même temps)
  - Comme si vous aviez plusieurs robots qui travaillent ensemble
  - Plus rapide qu'un seul robot qui fait tout l'un après l'autre

VISUALISATION :
  Workflow lancé à 14h00
  
  14h00:00 -> Job "test" démarre   ┐
  14h00:00 -> Job "lint" démarre   ├─-> Les 2 en même temps !
  14h01:30 -> Job "test" finit     │
  14h01:45 -> Job "lint" finit     ┘
  14h01:45 -> Job "deploy" démarre (il attendait les 2 autres)
  14h02:00 -> Job "deploy" finit
  
  Temps total : 2 minutes au lieu de 3min30 !


# === CONCEPT 4 : LES STEPS (les étapes) ===

QU'EST-CE QUE C'EST ?
  Un step, c'est une ÉTAPE INDIVIDUELLE dans un job.
  C'est comme une ligne dans votre to-do list.

EXEMPLE CONCRET :
  Job "test" contient ces steps :
    Step 1 : Télécharger le code
    Step 2 : Installer Python
    Step 3 : Installer les dépendances (pip install)
    Step 4 : Lancer pytest

IMPORTANT :
  - Les steps s'exécutent l'un APRÈS l'autre (pas en parallèle)
  - Si un step échoue, les suivants ne s'exécutent pas
  - C'est comme monter un escalier : une marche à la fois

VISUALISATION :
  Job "test" :
    ┌─ Step 1 : Checkout      [3 secondes]   [OK]
    ├─ Step 2 : Setup Python  [5 secondes]   [OK]
    ├─ Step 3 : Install deps  [30 secondes]  [OK]
    └─ Step 4 : Run tests     [45 secondes]  [X] ÉCHEC !
       └─ Les steps suivants ne s'exécutent pas


# === CONCEPT 5 : LES RUNNERS (les machines) ===

QU'EST-CE QUE C'EST ?
  Un runner, c'est l'ORDINATEUR qui fait le travail.
  C'est comme louer un ordinateur distant pour quelques minutes.

LES 3 TYPES PRINCIPAUX :

  1. ubuntu-latest (Linux)
     C'est quoi : Un ordinateur Linux
     Quand l'utiliser : 99% du temps ! (recommandé)
     Coût : Normal (1x)
     
  2. windows-latest (Windows)
     C'est quoi : Un ordinateur Windows
     Quand l'utiliser : Seulement si votre app est pour Windows
     Coût : Double ! (2x)
     
  3. macos-latest (macOS)
     C'est quoi : Un Mac
     Quand l'utiliser : Apps iOS/macOS uniquement
     Coût : 10x plus cher ! Évitez si possible

CE QUI EST DÉJÀ INSTALLÉ :
  Sur chaque runner, vous avez DÉJÀ :
    - Python (plusieurs versions : 3.8, 3.9, 3.10, 3.11, 3.12)
    - Node.js
    - Git
    - Docker
    - pip
    - npm
    - Et plein d'autres outils !
  
  Vous n'avez presque rien à installer, tout est prêt !


═══════════════════════════════════════════════════════════════════
 PARTIE 3 : VOTRE PREMIER WORKFLOW (ÉTAPE PAR ÉTAPE)
═══════════════════════════════════════════════════════════════════


# === ÉTAPE 1 : CRÉER LA STRUCTURE ===

Ouvrez un terminal dans votre projet et tapez :

mkdir -p .github/workflows

Explication ligne par ligne :
  - mkdir = "make directory" = créer un dossier
  - -p = créer aussi les dossiers parents si nécessaire
  - .github/workflows = le chemin exact requis par GitHub

Vous devriez maintenant avoir :
  mon-projet/
  └── .github/
      └── workflows/    <- Ce nouveau dossier


# === ÉTAPE 2 : CRÉER LE FICHIER WORKFLOW ===

Créez un fichier nommé "test.yml" dans le dossier workflows.

Vous pouvez le faire :
  - En ligne de commande : touch .github/workflows/test.yml
  - Avec votre éditeur : Créer nouveau fichier à cet emplacement
  - Sur GitHub.com : Add file -> Create new file

IMPORTANT : Le nom "test.yml" est un exemple, vous pouvez l'appeler :
  - ci.yml
  - tests.yml
  - my-workflow.yml
  - Peu importe ! C'est juste pour vous y retrouver


# === ÉTAPE 3 : ÉCRIRE LE WORKFLOW (VERSION MINIMALE) ===

Copiez-collez ce code dans test.yml :

name: Mon Premier Workflow
# ^ Le NOM de votre workflow (visible dans GitHub)
# Vous pouvez l'appeler comme vous voulez

on: push
# ^ QUAND le workflow se lance
# "push" = à chaque fois que vous faites "git push"

jobs:
# ^ Section qui contient tous les JOBS
# Un job = une tâche à accomplir

  dire-bonjour:
  # ^ Le NOM du job (vous choisissez)
  # Utilisez des tirets, pas d'espaces
  
    runs-on: ubuntu-latest
    # ^ La MACHINE à utiliser
    # ubuntu-latest = un ordinateur Linux (gratuit et rapide)
    
    steps:
    # ^ Les ÉTAPES de ce job
    # Elles s'exécutent l'une après l'autre
    
      - name: Dire bonjour
        # ^ Nom de l'étape (optionnel mais utile)
        
        run: echo "Bonjour le monde !"
        # ^ La COMMANDE à exécuter
        # Comme si vous la tapiez dans un terminal

FIN DU FICHIER


# === ÉTAPE 4 : COMPRENDRE CHAQUE LIGNE ===

Reprenons ligne par ligne avec encore plus de détails :

Line 1 : name: Mon Premier Workflow
  - C'est comme le titre d'un livre
  - Ce nom apparaît dans l'onglet "Actions" de GitHub
  - Si vous l'enlevez, GitHub utilise le nom du fichier (test.yml)

Line 2-3 : on: push
  - "on" = "quand"
  - "push" = quand vous faites "git push"
  - C'est le DÉCLENCHEUR du workflow

Line 4 : jobs:
  - Section obligatoire
  - Contient tous les jobs (tâches)
  - Pensez à ça comme une liste de choses à faire

Line 5 : dire-bonjour:
  - Le nom du job (vous le choisissez librement)
  - DOIT être indenté avec 2 espaces (pas de tabs!)
  - Le ":" à la fin est obligatoire

Line 6 : runs-on: ubuntu-latest
  - Indique quelle machine utiliser
  - ubuntu-latest = Linux (c'est le meilleur choix 99% du temps)
  - Autres options : windows-latest, macos-latest

Line 7 : steps:
  - Liste des étapes du job
  - Chaque étape commence par un tiret "-"

Line 8-9 : - name: Dire bonjour
  - Le tiret "-" indique une nouvelle étape
  - name: est le nom de l'étape (optionnel)
  - Utile pour voir dans les logs quelle étape fait quoi

Line 10 : run: echo "Bonjour le monde !"
  - "run:" signifie "exécute cette commande"
  - C'est comme si vous tapiez dans un terminal
  - "echo" affiche du texte
  - Le texte sera visible dans les logs


# === ÉTAPE 5 : TESTER LE WORKFLOW ===

Maintenant, envoyons ça sur GitHub !

1. Ajoutez le fichier :
   git add .github/workflows/test.yml

2. Commitez :
   git commit -m "Add my first GitHub Actions workflow"

3. Poussez sur GitHub :
   git push origin main

4. Allez sur GitHub.com et ouvrez votre repository

5. Cliquez sur l'onglet "Actions" (en haut)

6. Vous devriez voir votre workflow en cours d'exécution ! [BRAVO]


# === ÉTAPE 6 : VOIR LES RÉSULTATS ===

Dans l'onglet Actions, vous voyez :

┌─────────────────────────────────────────┐
│ All workflows                           │
├─────────────────────────────────────────┤
│ [NOIR] Mon Premier Workflow  <- En cours     │
│   #1: Add my first workflow             │
│   main · 2 minutes ago · In progress    │
└─────────────────────────────────────────┘

Cliquez dessus, vous voyez :

┌─────────────────────────────────────────┐
│ dire-bonjour                            │
│   [OK] Success in 12s                     │
├─────────────────────────────────────────┤
│ Set up job        2s                    │
│ Dire bonjour      1s  <- Cliquez ici     │
│ Complete job      1s                    │
└─────────────────────────────────────────┘

Cliquez sur "Dire bonjour", vous voyez les logs :

┌─────────────────────────────────────────┐
│ Run echo "Bonjour le monde !"           │
│ Bonjour le monde !                      │
└─────────────────────────────────────────┘

FÉLICITATIONS ! [BRAVO] Vous venez de créer votre premier workflow !


═══════════════════════════════════════════════════════════════════
 PARTIE 4 : WORKFLOW POUR PROJET PYTHON RÉEL
═══════════════════════════════════════════════════════════════════


# === WORKFLOW COMPLET EXPLIQUÉ LIGNE PAR LIGNE ===

Maintenant, créons un VRAI workflow pour tester du code Python.

Fichier : .github/workflows/test.yml

# ─────────────── SECTION 1 : ENTÊTE ───────────────

name: Tests Python
# Le nom de votre workflow
# Apparaît dans l'interface GitHub Actions

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

# EXPLICATION DÉTAILLÉE :
# 
# on: = "Quand est-ce que ce workflow doit se lancer ?"
# 
# push: = "Quand quelqu'un fait git push"
#   branches: [main, develop] = "Mais seulement sur ces branches"
#   
#   Exemple : 
#   - git push origin main -> [OK] Workflow se lance
#   - git push origin feature/test -> [X] Workflow NE se lance PAS
# 
# pull_request: = "Quand quelqu'un crée une Pull Request"
#   branches: [main] = "Vers la branche main uniquement"
#   
#   Exemple :
#   - PR de "feature" vers "main" -> [OK] Workflow se lance
#   - PR de "feature" vers "develop" -> [X] Workflow NE se lance PAS


# ─────────────── SECTION 2 : DÉFINITION DU JOB ───────────────

jobs:
  test:
    # Nom du job : "test"
    # Vous pouvez l'appeler comme vous voulez
    
    runs-on: ubuntu-latest
    # Machine Linux (gratuit, rapide, recommandé)
    
    timeout-minutes: 10
    # IMPORTANT : Limite de temps maximum
    # Si le job dépasse 10 minutes, il s'arrête automatiquement
    # Évite qu'un bug fasse tourner le workflow pendant des heures


# ─────────────── SECTION 3 : LES ÉTAPES ───────────────

    steps:
      # Liste des étapes à exécuter
      # Le tiret "-" commence chaque étape


# ─── ÉTAPE 1 : TÉLÉCHARGER LE CODE ───

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

# EXPLICATION DÉTAILLÉE :
# 
# name: Checkout code
#   = Nom de cette étape (pour les logs)
# 
# uses: actions/checkout@v4
#   = "Utilise une action pré-faite"
#   = actions/checkout = action officielle de GitHub
#   = @v4 = version 4 (toujours utiliser une version précise)
# 
# QUE FAIT CETTE ACTION ?
#   1. Se connecte à votre repository GitHub
#   2. Télécharge tout votre code
#   3. Le place dans /home/runner/work/votre-repo/votre-repo
# 
# POURQUOI C'EST IMPORTANT ?
#   Sans cette ligne, votre code n'est PAS téléchargé !
#   Le workflow ne peut pas tester du code qui n'existe pas.
# 
# [ATTENTION] CETTE ÉTAPE DOIT TOUJOURS ÊTRE EN PREMIER !


# ─── ÉTAPE 2 : INSTALLER PYTHON ───

      - name: Setup Python 3.11
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'

# EXPLICATION DÉTAILLÉE :
# 
# name: Setup Python 3.11
#   = Nom descriptif de l'étape
# 
# uses: actions/setup-python@v5
#   = Action officielle pour installer Python
#   = @v5 = version 5 de l'action
# 
# with:
#   = Paramètres qu'on passe à l'action
#   = Comme des options/arguments
# 
# python-version: '3.11'
#   = Version de Python à installer
#   = Vous pouvez choisir : '3.8', '3.9', '3.10', '3.11', '3.12'
#   = Les quotes sont importantes !
# 
# cache: 'pip'
#   = SUPER IMPORTANT pour la vitesse !
#   = Active le cache automatique de pip
#   = 1er run : Installe tout (2 minutes)
#   = Runs suivants : Réutilise le cache (10 secondes !)
# 
# QUE FAIT CETTE ACTION ?
#   1. Installe Python 3.11
#   2. Configure pip (gestionnaire de packages)
#   3. Ajoute python et pip au PATH
#   4. Configure le cache pour pip
# 
# APRÈS CETTE ÉTAPE :
#   Vous pouvez utiliser : python, pip, pytest, etc.


# ─── ÉTAPE 3 : INSTALLER LES DÉPENDANCES ───

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt

# EXPLICATION DÉTAILLÉE :
# 
# name: Install dependencies
#   = Nom de l'étape
# 
# run: |
#   = "Exécute ces commandes shell"
#   = Le symbole "|" permet d'écrire plusieurs lignes
# 
# python -m pip install --upgrade pip
#   = Met à jour pip à la dernière version
#   = python -m pip = lance pip via Python (plus sûr)
#   = --upgrade = met à jour
#   = pip = le package à mettre à jour
# 
# pip install -r requirements.txt
#   = Installe toutes les dépendances de votre projet
#   = -r = "read" = lire depuis un fichier
#   = requirements.txt = fichier avec la liste des packages
# 
# EXEMPLE DE requirements.txt :
#   flask==3.0.0
#   pytest==7.4.3
#   requests>=2.28.0
# 
# [ATTENTION] IMPORTANT :
#   Votre requirements.txt DOIT exister dans votre projet !
#   Sinon cette étape échouera avec "No such file"


# ─── ÉTAPE 4 : LANCER LES TESTS ───

      - name: Run tests
        run: pytest -v

# EXPLICATION DÉTAILLÉE :
# 
# name: Run tests
#   = Nom de l'étape
# 
# run: pytest -v
#   = Exécute pytest (outil de tests Python)
#   = -v = --verbose = mode verbeux (plus de détails)
# 
# QUE FAIT PYTEST ?
#   1. Cherche tous les fichiers test_*.py
#   2. Dans chaque fichier, trouve les fonctions test_*()
#   3. Exécute chaque test
#   4. Affiche [OK] ou [X] pour chaque test
# 
# EXEMPLE DE FICHIER DE TEST :
#   # tests/test_app.py
#   def test_addition():
#       assert 1 + 1 == 2  # [OK] Passe
#   
#   def test_soustraction():
#       assert 5 - 3 == 2  # [OK] Passe
# 
# RÉSULTAT DANS LES LOGS :
#   tests/test_app.py::test_addition PASSED
#   tests/test_app.py::test_soustraction PASSED
#   =============== 2 passed in 0.05s ===============
# 
# SI UN TEST ÉCHOUE :
#   Le workflow s'arrête et affiche [X] rouge
#   Vous recevez une notification (email/mobile)


# ─── ÉTAPE 5 : VÉRIFIER LE STYLE DE CODE ───

      - name: Lint with flake8
        run: |
          pip install flake8
          flake8 . --count --select=E9,F63,F7,F82 --show-source

# EXPLICATION DÉTAILLÉE :
# 
# name: Lint with flake8
#   = Vérifier la qualité/style du code
# 
# pip install flake8
#   = Installe flake8 (outil de linting)
#   = Linting = vérifier que le code respecte les bonnes pratiques
# 
# flake8 . --count --select=E9,F63,F7,F82 --show-source
#   = Lance flake8 sur tous les fichiers
#   = . = dossier actuel (tout le projet)
#   = --count = affiche le nombre d'erreurs
#   = --select=E9,F63,F7,F82 = vérifie seulement les erreurs graves
#   = --show-source = montre le code problématique
# 
# TYPES D'ERREURS DÉTECTÉES :
#   E9   : Erreurs de syntaxe Python
#   F63  : Test invalide (ex: assert sans condition)
#   F7   : Erreurs de syntaxe dans annotations
#   F82  : Variable non définie
# 
# EXEMPLE D'ERREUR :
#   app.py:10:5: F821 undefined name 'variable_inexistante'
#   
#   Ce qui signifie :
#   - Fichier : app.py
#   - Ligne : 10
#   - Colonne : 5
#   - Erreur : F821 = variable non définie
#   - Variable : variable_inexistante


# ─────────────── WORKFLOW COMPLET ASSEMBLÉ ───────────────

# Voici le workflow complet sans les commentaires :

name: Tests Python

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

jobs:
  test:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Setup Python 3.11
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'
      
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
      
      - name: Run tests
        run: pytest -v
      
      - name: Lint with flake8
        run: |
          pip install flake8
          flake8 . --count --select=E9,F63,F7,F82 --show-source


═══════════════════════════════════════════════════════════════════
 PARTIE 5 : METTRE EN PLACE CE WORKFLOW (GUIDE PRATIQUE)
═══════════════════════════════════════════════════════════════════


# === PRÉPARATION DE VOTRE PROJET ===

Avant de créer le workflow, assurez-vous d'avoir :

1. UN FICHIER requirements.txt

   Créez-le à la racine de votre projet :
   
   # requirements.txt
   pytest==7.4.3
   flask==3.0.0
   requests>=2.28.0
   
   Ou générez-le automatiquement :
   pip freeze > requirements.txt

2. DES TESTS PYTEST

   Créez un dossier "tests/" et des fichiers dedans :
   
   # tests/test_example.py
   def test_simple():
       assert 1 + 1 == 2
   
   def test_string():
       assert "hello".upper() == "HELLO"

3. STRUCTURE DE PROJET MINIMALE

   mon-projet/
   ├── .github/
   │   └── workflows/
   │       └── test.yml        <- Votre workflow
   ├── src/
   │   └── app.py             <- Votre code
   ├── tests/
   │   └── test_app.py        <- Vos tests
   ├── requirements.txt        <- Vos dépendances
   └── README.md


# === CRÉER LE WORKFLOW (MÉTHODE 1 : LIGNE DE COMMANDE) ===

Ouvrez un terminal dans votre projet :

# Créer le dossier
mkdir -p .github/workflows

# Créer le fichier
touch .github/workflows/test.yml

# Ouvrir avec votre éditeur
code .github/workflows/test.yml   # VS Code
nano .github/workflows/test.yml    # Terminal
vim .github/workflows/test.yml     # Vim

# Copiez-collez le workflow complet dedans

# Sauvegarder et quitter


# === CRÉER LE WORKFLOW (MÉTHODE 2 : SUR GITHUB.COM) ===

Plus facile pour les débutants !

Étape 1 : Allez sur votre repository sur GitHub.com

Étape 2 : Cliquez sur "Add file" (en haut à droite)

Étape 3 : Sélectionnez "Create new file"

Étape 4 : Dans le champ "Name your file", tapez :
          .github/workflows/test.yml
          
          [ATTENTION] IMPORTANT : Tapez exactement ça, avec les slashes !
          GitHub créera automatiquement les dossiers

Étape 5 : Dans la zone de texte, collez le workflow complet

Étape 6 : En bas, dans "Commit new file" :
          - Message : "Add CI workflow for tests"
          - Description (optionnel) : "Automatic tests on push and PR"

Étape 7 : Cliquez sur "Commit new file"

Étape 8 : Allez dans l'onglet "Actions" -> Votre workflow s'exécute ! [BRAVO]


# === VÉRIFIER QUE ÇA MARCHE ===

Méthode 1 : Automatique (après commit)
  1. Le workflow se lance automatiquement après le commit
  2. Allez dans "Actions"
  3. Vous voyez : "Tests Python" avec une icône jaune (en cours)
  4. Cliquez dessus pour voir l'avancement en temps réel
  5. Attendez 1-2 minutes
  6. Si tout est vert [OK] : Félicitations !
  7. Si c'est rouge [X] : Voir section "Comprendre les erreurs"

Méthode 2 : Faire un test (push manuel)
  1. Modifiez n'importe quel fichier dans votre projet
  2. git add .
  3. git commit -m "Test workflow"
  4. git push origin main
  5. Allez sur GitHub -> Actions
  6. Vous voyez le workflow se lancer !


# === COMPRENDRE CE QUI SE PASSE (VISUALISATION) ===

Quand vous faites "git push", voici exactement ce qui se passe :

ÉTAPE 1 : GitHub détecte le push
  Temps : Instantané
  └─ GitHub voit : "Nouveau push sur main"
  └─ GitHub cherche : Y a-t-il des workflows ?
  └─ GitHub trouve : .github/workflows/test.yml
  └─ GitHub lit : "on: push, branches: [main]"
  └─ GitHub décide : "Je dois lancer ce workflow !"

ÉTAPE 2 : Création d'une machine virtuelle
  Temps : 5-10 secondes
  └─ GitHub crée : Une machine Ubuntu toute neuve
  └─ GitHub installe : Python, Git, Docker, etc.
  └─ GitHub prépare : L'environnement

ÉTAPE 3 : Exécution des steps
  
  Step 1 : Checkout code (2-3 secondes)
    ├─ GitHub se connecte à votre repository
    ├─ GitHub télécharge tout votre code
    └─ Votre code est maintenant sur la machine
  
  Step 2 : Setup Python (5-10 secondes)
    ├─ Installe Python 3.11
    ├─ Configure pip
    ├─ Vérifie le cache
    └─ Python est prêt !
  
  Step 3 : Install dependencies (10-60 secondes)
    ├─ Met à jour pip
    ├─ Lit requirements.txt
    ├─ Télécharge chaque package depuis PyPI
    ├─ Installe chaque package
    └─ Dépendances installées !
  
  Step 4 : Run tests (10-120 secondes selon nombre de tests)
    ├─ Pytest cherche les fichiers test_*.py
    ├─ Pytest exécute chaque fonction test_*()
    ├─ Affiche les résultats
    └─ Tests terminés ! [OK] ou [X]
  
  Step 5 : Lint (3-10 secondes)
    ├─ Installe flake8
    ├─ Analyse tous les fichiers .py
    ├─ Cherche les erreurs de style
    └─ Linting terminé ! [OK] ou [X]

ÉTAPE 4 : Résultats
  Temps : Instantané
  └─ Si tout est vert : GitHub affiche [OK] Success
  └─ Si une erreur : GitHub affiche [X] Failure
  └─ GitHub vous envoie une notification (email/mobile)
  └─ GitHub détruit la machine virtuelle

TEMPS TOTAL : Environ 1-3 minutes


═══════════════════════════════════════════════════════════════════
 PARTIE 6 : COMPRENDRE ET CORRIGER LES ERREURS
═══════════════════════════════════════════════════════════════════


# === OÙ TROUVER LES LOGS D'ERREUR ===

Si votre workflow échoue ([X] rouge), voici comment trouver l'erreur :

ÉTAPE PAR ÉTAPE :

  1. Allez sur GitHub.com
  2. Ouvrez votre repository
  3. Cliquez sur "Actions" (onglet en haut)
  4. Vous voyez une liste de workflows :
  
     ┌────────────────────────────────────────┐
     │ [X] Tests Python                        │
     │    #12: Update app.py                  │
     │    main · 2 minutes ago · Failed       │
     └────────────────────────────────────────┘
  
  5. Cliquez sur la ligne rouge
  6. Vous voyez la liste des jobs :
  
     ┌────────────────────────────────────────┐
     │ [X] test                                │
     │    Failed in 1m 23s                    │
     └────────────────────────────────────────┘
  
  7. Cliquez sur "test" (le job qui a échoué)
  8. Vous voyez la liste des steps :
  
     ┌────────────────────────────────────────┐
     │ [OK] Set up job            2s            │
     │ [OK] Checkout code         3s            │
     │ [OK] Setup Python 3.11     7s            │
     │ [OK] Install dependencies  45s           │
     │ [X] Run tests             15s <- ERREUR! │
     │ ⊘  Lint with flake8      skipped       │
     └────────────────────────────────────────┘
  
  9. Cliquez sur le step rouge (Run tests)
  10. Vous voyez les logs détaillés avec l'erreur !


# === LES 10 ERREURS LES PLUS COURANTES ===


# ─── ERREUR #1 : requirements.txt introuvable ───

SYMPTÔME dans les logs :
  ERROR: Could not open requirements file: 
  [Errno 2] No such file or directory: 'requirements.txt'

CE QUE ÇA VEUT DIRE :
  Le fichier requirements.txt n'existe pas dans votre projet

POURQUOI ÇA ARRIVE :
  - Vous avez oublié de créer le fichier
  - Le fichier est mal nommé (Requirements.txt, requirement.txt...)
  - Le fichier est dans un sous-dossier

COMMENT CORRIGER :
  
  Solution 1 : Créer le fichier
    # Créez requirements.txt à la racine de votre projet
    touch requirements.txt
    
    # Ajoutez vos dépendances
    echo "pytest==7.4.3" >> requirements.txt
    
    # Commitez et poussez
    git add requirements.txt
    git commit -m "Add requirements.txt"
    git push
  
  Solution 2 : Générer automatiquement
    # Si vous avez un environnement virtuel activé
    pip freeze > requirements.txt
    git add requirements.txt
    git commit -m "Add requirements.txt"
    git push
  
  Solution 3 : Si le fichier est dans un sous-dossier
    # Modifiez le workflow pour spécifier le chemin
    - name: Install dependencies
      run: |
        pip install -r subfolder/requirements.txt


# ─── ERREUR #2 : Checkout oublié ───

SYMPTÔME dans les logs :
  fatal: not a git repository (or any of the parent directories): .git
  OU
  pytest: error: file or directory not found: tests/

CE QUE ÇA VEUT DIRE :
  Votre code n'a pas été téléchargé sur la machine virtuelle

POURQUOI ÇA ARRIVE :
  Vous avez oublié le step actions/checkout@v4

COMMENT CORRIGER :
  
  Ajoutez cette ligne EN PREMIER dans vos steps :
  
  steps:
    - name: Checkout code
      uses: actions/checkout@v4    # <- Ajoutez ça !
    
    - name: Setup Python
      uses: actions/setup-python@v5
      ...

  [ATTENTION] actions/checkout DOIT TOUJOURS être le premier step !


# ─── ERREUR #3 : Python pas installé ───

SYMPTÔME dans les logs :
  python: command not found
  OU
  pip: command not found

CE QUE ÇA VEUT DIRE :
  Python n'est pas installé ou pas configuré

POURQUOI ÇA ARRIVE :
  - Vous avez oublié le step actions/setup-python
  - Vous avez une faute de frappe dans le nom de l'action

COMMENT CORRIGER :
  
  Ajoutez ce step APRÈS checkout :
  
  steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python          # <- Ajoutez ça !
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'
        cache: 'pip'
    
    - name: Install dependencies
      run: pip install -r requirements.txt


# ─── ERREUR #4 : pytest pas installé ───

SYMPTÔME dans les logs :
  pytest: command not found
  OU
  ModuleNotFoundError: No module named 'pytest'

CE QUE ÇA VEUT DIRE :
  pytest n'est pas installé

POURQUOI ÇA ARRIVE :
  - pytest n'est pas dans requirements.txt
  - requirements.txt n'a pas été installé

COMMENT CORRIGER :
  
  Solution 1 : Ajouter pytest dans requirements.txt
    # requirements.txt
    pytest==7.4.3          # <- Ajoutez cette ligne
    flask==3.0.0
    requests>=2.28.0
    
    # Puis commitez
    git add requirements.txt
    git commit -m "Add pytest to requirements"
    git push
  
  Solution 2 : Installer pytest dans le workflow
    - name: Install dependencies
      run: |
        pip install -r requirements.txt
        pip install pytest          # <- Ajoutez cette ligne


# ─── ERREUR #5 : Tests qui échouent ───

SYMPTÔME dans les logs :
  FAILED tests/test_app.py::test_addition - assert 1 + 1 == 3
  ======================== 1 failed in 0.12s ========================

CE QUE ÇA VEUT DIRE :
  Un de vos tests a détecté un problème dans votre code

POURQUOI ÇA ARRIVE :
  - Votre code a un bug
  - Le test lui-même est incorrect
  - L'environnement est différent (variables, fichiers...)

COMMENT CORRIGER :
  
  Étape 1 : Identifier quel test échoue
    Regardez les logs, vous verrez :
    FAILED tests/test_app.py::test_addition
            ^                   ^
            Fichier             Fonction
  
  Étape 2 : Comprendre l'erreur
    assert 1 + 1 == 3
           ^       ^
           Obtenu  Attendu
    
    Le test attend 3, mais obtient 2
  
  Étape 3 : Corriger le code ou le test
    
    Si c'est un bug dans le code :
      # app.py
      def add(a, b):
          return a + b + 1    # <- BUG ICI !
      
      # Corrigez en :
      def add(a, b):
          return a + b        # [OK] Corrigé
    
    Si c'est un bug dans le test :
      # tests/test_app.py
      def test_addition():
          assert add(1, 1) == 3    # <- TEST INCORRECT !
      
      # Corrigez en :
      def test_addition():
          assert add(1, 1) == 2    # [OK] Corrigé
  
  Étape 4 : Tester localement
    python -m pytest tests/test_app.py
    
    Si ça passe : commitez et poussez
    git add .
    git commit -m "Fix test"
    git push


# ─── ERREUR #6 : Erreur de syntaxe YAML ───

SYMPTÔME dans GitHub :
  Vous ne voyez PAS le workflow dans l'onglet Actions
  OU
  Message : "Invalid workflow file"

CE QUE ÇA VEUT DIRE :
  Le fichier .yml a une erreur de syntaxe

POURQUOI ÇA ARRIVE :
  - Indentation incorrecte (utilisé tabs au lieu d'espaces)
  - Deux-points manquants
  - Guillemets mal fermés
  - Tirets mal placés

COMMENT CORRIGER :
  
  Étape 1 : Vérifier la syntaxe YAML
    Allez sur : https://www.yamllint.com/
    Copiez-collez votre fichier .yml
    Le site vous montre les erreurs !
  
  Étape 2 : Erreurs courantes
    
    [X] MAUVAIS :
      on:push                    # Manque espace après ":"
      
    [OK] BON :
      on: push
    
    [X] MAUVAIS :
      steps:
        - name: Test
            run: pytest            # Mauvaise indentation (4 espaces)
      
    [OK] BON :
      steps:
        - name: Test
          run: pytest              # Bonne indentation (2 espaces)
    
    [X] MAUVAIS :
      python-version: 3.11         # Pas de guillemets
      
    [OK] BON :
      python-version: '3.11'       # Avec guillemets
  
  Étape 3 : Règles d'indentation YAML
    - Utilisez TOUJOURS 2 espaces (pas 4, pas de tabs)
    - Tous les éléments du même niveau = même indentation
    - Les tirets "-" pour les listes
    - Les ":" pour les paires clé-valeur


# ─── ERREUR #7 : Permission denied ───

SYMPTÔME dans les logs :
  bash: ./deploy.sh: Permission denied

CE QUE ÇA VEUT DIRE :
  Un script n'a pas les permissions d'exécution

POURQUOI ÇA ARRIVE :
  Les fichiers téléchargés depuis Git n'ont pas le flag +x

COMMENT CORRIGER :
  
  Solution 1 : Ajouter chmod dans le workflow
    - name: Deploy
      run: |
        chmod +x deploy.sh       # <- Rendre exécutable
        ./deploy.sh
  
  Solution 2 : Utiliser bash/python directement
    - name: Deploy
      run: bash deploy.sh        # Pas besoin de chmod
    
    OU
    
    - name: Run script
      run: python script.py      # Pas besoin de chmod


# ─── ERREUR #8 : Module not found ───

SYMPTÔME dans les logs :
  ModuleNotFoundError: No module named 'mon_module'

CE QUE ÇA VEUT DIRE :
  Python ne trouve pas votre module local

POURQUOI ÇA ARRIVE :
  - Le module n'est pas dans le PYTHONPATH
  - Le package n'est pas installé en mode editable

COMMENT CORRIGER :
  
  Solution 1 : Installer le package en mode editable
    - name: Install package
      run: |
        pip install -r requirements.txt
        pip install -e .           # <- Installe votre package
  
  Solution 2 : Ajouter au PYTHONPATH
    - name: Run tests
      env:
        PYTHONPATH: ${{ github.workspace }}/src
      run: pytest
  
  Solution 3 : Créer setup.py
    # setup.py
    from setuptools import setup, find_packages
    
    setup(
        name="mon_projet",
        packages=find_packages(),
    )
    
    # Puis dans le workflow
    - run: pip install -e .


# ─── ERREUR #9 : Timeout dépassé ───

SYMPTÔME dans les logs :
  The job running on runner ... has exceeded the maximum 
  execution time of 360 minutes.

CE QUE ÇA VEUT DIRE :
  Le job a pris trop de temps (plus de 6 heures par défaut)

POURQUOI ÇA ARRIVE :
  - Un test est bloqué (boucle infinie)
  - Un processus attend indéfiniment
  - Téléchargement très long

COMMENT CORRIGER :
  
  Solution 1 : Ajouter un timeout plus court
    jobs:
      test:
        runs-on: ubuntu-latest
        timeout-minutes: 10      # <- Maximum 10 minutes
  
  Solution 2 : Débugger le test qui bloque
    # Lancez localement avec timeout
    pytest --timeout=60 tests/
    
    # Identifiez quel test bloque
  
  Solution 3 : Timeout par step
    - name: Long running task
      timeout-minutes: 5
      run: ./long-script.sh


# ─── ERREUR #10 : Cache ne fonctionne pas ───

SYMPTÔME dans les logs :
  Installation prend toujours 2+ minutes malgré cache: 'pip'

CE QUE ÇA VEUT DIRE :
  Le cache n'est pas réutilisé

POURQUOI ÇA ARRIVE :
  - La clé de cache change à chaque run
  - requirements.txt change souvent
  - Cache expiré (7 jours)

COMMENT CORRIGER :
  
  Vérification 1 : Cache activé ?
    - uses: actions/setup-python@v5
      with:
        python-version: '3.11'
        cache: 'pip'              # <- Doit être là !
  
  Vérification 2 : requirements.txt stable ?
    # Si vous modifiez souvent requirements.txt,
    # le cache se recréé à chaque fois
    
    # Solution : Séparez les dépendances
    # requirements.txt (stable)
    flask==3.0.0
    requests==2.28.0
    
    # requirements-dev.txt (change souvent)
    pytest==7.4.3
    black==23.0.0
  
  Vérification 3 : Voir les logs de cache
    Dans les logs, cherchez :
    "Cache restored from key: ..."
    OU
    "Cache not found for input keys: ..."


═══════════════════════════════════════════════════════════════════
 PARTIE 7 : FONCTIONNALITÉS AVANCÉES EXPLIQUÉES SIMPLEMENT
═══════════════════════════════════════════════════════════════════


# === TESTER PLUSIEURS VERSIONS DE PYTHON (MATRICE) ===

PROBLÈME :
  Vous voulez être sûr que votre code marche avec :
  - Python 3.9
  - Python 3.10
  - Python 3.11
  - Python 3.12

SOLUTION NAÏVE (ne faites pas ça !) :
  Créer 4 workflows identiques, un pour chaque version
  -> Beaucoup de duplication ! [!]

SOLUTION INTELLIGENTE : LA MATRICE !

# Workflow avec matrice
jobs:
  test:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        python-version: ['3.9', '3.10', '3.11', '3.12']
        # ^ Liste des versions à tester
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          # ^ Utilise la version de la matrice
      
      - run: pip install -r requirements.txt
      - run: pytest

EXPLICATION DÉTAILLÉE :

  strategy:
    = "Stratégie d'exécution"
    = Comment organiser les tests
  
  matrix:
    = "Matrice de combinaisons"
    = GitHub va créer plusieurs jobs automatiquement
  
  python-version: ['3.9', '3.10', '3.11', '3.12']
    = Liste des valeurs à tester
    = Attention aux guillemets pour 3.10 ! (sinon vu comme 3.1)
  
  ${{ matrix.python-version }}
    = Variable qui prend chaque valeur de la liste
    = C'est comme une boucle for

CE QUI SE PASSE :
  GitHub crée automatiquement 4 jobs :
  
  Job 1 : test (3.9)
    ├─ Python 3.9 installé
    └─ Tests exécutés
  
  Job 2 : test (3.10)
    ├─ Python 3.10 installé
    └─ Tests exécutés
  
  Job 3 : test (3.11)
    ├─ Python 3.11 installé
    └─ Tests exécutés
  
  Job 4 : test (3.12)
    ├─ Python 3.12 installé
    └─ Tests exécutés
  
  Les 4 jobs s'exécutent EN PARALLÈLE !

DANS L'INTERFACE GITHUB :
  Vous verrez :
  [OK] test (3.9)   - Success
  [OK] test (3.10)  - Success
  [X] test (3.11)  - Failed      <- Ah ! Un problème avec 3.11
  [OK] test (3.12)  - Success


# === MATRICE MULTI-DIMENSIONS ===

PROBLÈME :
  Tester plusieurs Python + plusieurs OS
  - Python 3.10, 3.11, 3.12
  - Ubuntu, Windows, macOS

SOLUTION :

strategy:
  matrix:
    python-version: ['3.10', '3.11', '3.12']
    os: [ubuntu-latest, windows-latest, macos-latest]

EXPLICATION :
  GitHub va créer TOUTES les combinaisons :
  
  3 versions Python × 3 OS = 9 jobs !
  
  1. Python 3.10 sur Ubuntu
  2. Python 3.10 sur Windows
  3. Python 3.10 sur macOS
  4. Python 3.11 sur Ubuntu
  5. Python 3.11 sur Windows
  6. Python 3.11 sur macOS
  7. Python 3.12 sur Ubuntu
  8. Python 3.12 sur Windows
  9. Python 3.12 sur macOS

WORKFLOW COMPLET :

jobs:
  test:
    runs-on: ${{ matrix.os }}
    # ^ Variable, prend chaque OS de la matrice
    
    strategy:
      matrix:
        python-version: ['3.10', '3.11', '3.12']
        os: [ubuntu-latest, windows-latest, macos-latest]
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
      
      - run: pip install -r requirements.txt
      - run: pytest

[ATTENTION] ATTENTION AU COÛT :
  - Ubuntu : 1x (gratuit de base)
  - Windows : 2x (2 fois plus cher)
  - macOS : 10x (10 fois plus cher !)
  
  9 jobs × temps moyen = beaucoup de minutes consommées !
  
  Pour repo privé :
    9 jobs × 2 min × multiplicateurs = 
    3×2×1 + 3×2×2 + 3×2×10 = 6 + 12 + 60 = 78 minutes par run


# === EXCLURE CERTAINES COMBINAISONS ===

PROBLÈME :
  Vous voulez tester partout, SAUF Python 3.9 sur macOS

SOLUTION :

strategy:
  matrix:
    python-version: ['3.9', '3.10', '3.11']
    os: [ubuntu-latest, macos-latest]
    exclude:
      - python-version: '3.9'
        os: macos-latest

EXPLICATION :
  Sans exclude : 3 × 2 = 6 combinaisons
  Avec exclude : 6 - 1 = 5 combinaisons
  
  Combinaisons créées :
  [OK] Python 3.9 sur Ubuntu
  [X] Python 3.9 sur macOS        <- Exclu !
  [OK] Python 3.10 sur Ubuntu
  [OK] Python 3.10 sur macOS
  [OK] Python 3.11 sur Ubuntu
  [OK] Python 3.11 sur macOS


# === JOBS QUI DÉPENDENT D'AUTRES JOBS (needs) ===

PROBLÈME :
  Vous voulez :
  1. D'abord exécuter les tests
  2. Puis déployer seulement SI les tests passent

SOLUTION NAÏVE (ne marche pas !) :
  Mettre tout dans le même job
  -> Pas de parallélisation possible

SOLUTION INTELLIGENTE : needs !

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: pytest
  
  deploy:
    needs: test              # <- Attend que "test" soit fini
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: ./deploy.sh

EXPLICATION DÉTAILLÉE :

  needs: test
    = "Ce job a besoin que le job 'test' soit terminé"
    = Si 'test' échoue, 'deploy' ne s'exécute PAS
  
  TIMELINE :
    14:00:00 -> test démarre
    14:01:30 -> test finit [OK]
    14:01:30 -> deploy démarre      <- Attend test
    14:02:00 -> deploy finit [OK]
  
  SI TEST ÉCHOUE :
    14:00:00 -> test démarre
    14:01:30 -> test finit [X]
    14:01:30 -> deploy ne démarre PAS   <- Sauté !

NEEDS MULTIPLES :

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - run: pytest
  
  lint:
    runs-on: ubuntu-latest
    steps:
      - run: flake8 .
  
  deploy:
    needs: [test, lint]      # <- Attend test ET lint
    runs-on: ubuntu-latest
    steps:
      - run: ./deploy.sh

EXPLICATION :
  needs: [test, lint]
    = Attend que test ET lint soient terminés
    = Si un seul échoue, deploy ne s'exécute pas
  
  TIMELINE :
    14:00:00 -> test démarre   ┐
    14:00:00 -> lint démarre   ├─ En parallèle
    14:01:00 -> lint finit [OK]  │
    14:01:30 -> test finit [OK]  ┘
    14:01:30 -> deploy démarre    <- Attend les 2
    14:02:00 -> deploy finit [OK]# Fichier: python_cheats/cheatsheets/github_action.txt
# Cheatsheet GitHub Actions - Guide Complet


[OK] INTRODUCTION - QU'EST-CE QUE GITHUB ACTIONS ?


# === C'EST QUOI ? ===
# GitHub Actions est un système d'automatisation intégré directement dans GitHub
# Pensez-y comme un "robot" qui exécute des tâches automatiquement pour vous

# POURQUOI L'UTILISER ?
# 1. Tester automatiquement votre code à chaque push
# 2. Déployer automatiquement votre application
# 3. Automatiser des tâches répétitives
# 4. Assurer la qualité du code avant merge
# 5. Générer des rapports, documentation, releases...

# EXEMPLE CONCRET:
# Vous poussez du code -> GitHub Actions:
#   1. Télécharge votre code
#   2. Installe Python et dépendances
#   3. Exécute vos tests
#   4. Vérifie le style de code
#   5. Vous informe si tout est OK ou si erreurs

# === CONCEPTS DE BASE ===

# WORKFLOW
# Un fichier YAML qui définit ce qui doit être fait automatiquement
# Exemple: .github/workflows/test.yml

# ÉVÉNEMENT (Event)
# Ce qui déclenche le workflow
# Exemples: push de code, création de PR, schedule (cron)

# JOB
# Un ensemble d'étapes qui s'exécutent sur une même machine
# Exemples: job "test", job "deploy", job "build"

# STEP
# Une action individuelle dans un job
# Exemples: installer Python, exécuter pytest, envoyer notification

# RUNNER
# La machine (serveur) qui exécute votre workflow
# GitHub fournit des machines gratuitement (Linux, Windows, macOS)

# ACTION
# Un bloc de code réutilisable (comme une fonction)
# Exemples: actions/checkout (télécharger code), actions/setup-python (installer Python)


# === ANATOMIE D'UN WORKFLOW ===

# Fichier: .github/workflows/mon-workflow.yml
name: Mon Premier Workflow        # Nom visible dans GitHub
on: push                          # Quand l'exécuter (ici: à chaque push)
jobs:                             # Liste des jobs à exécuter
  test:                           # Nom du job
    runs-on: ubuntu-latest        # Machine à utiliser
    steps:                        # Liste des étapes
      - name: Dire bonjour        # Nom de l'étape
        run: echo "Bonjour!"      # Commande à exécuter


# === OÙ VOIR LES RÉSULTATS ? ===
# Sur GitHub -> Onglet "Actions" de votre repository
# Vous verrez:
# - Liste des workflows exécutés
# - Statut ([OK] succès, [X] échec, [JAUNE] en cours)
# - Logs détaillés de chaque step
# - Temps d'exécution
# - Artifacts (fichiers générés)


# === TARIFICATION ===

# REPOS PUBLICS: GRATUIT ET ILLIMITÉ ! [BRAVO]

# REPOS PRIVÉS (minutes gratuites/mois):
# - Free: 2000 minutes, 500MB storage
# - Pro: 3000 minutes, 1GB storage  
# - Team: 3000 minutes, 2GB storage
# - Enterprise: 50000 minutes, 50GB storage

# MULTIPLICATEURS (comptage des minutes):
# - Linux: 1x (10 min = 10 min comptées)
# - Windows: 2x (10 min = 20 min comptées)
# - macOS: 10x (10 min = 100 min comptées) [ATTENTION] Cher!
# Astuce: Utilisez Linux sauf si besoin spécifique


# === COMMENT COMMENCER ? ===

# ÉTAPE 1: Créer le dossier
# Dans votre repo: créer .github/workflows/

# ÉTAPE 2: Créer un fichier YAML
# Exemple: .github/workflows/test.yml

# ÉTAPE 3: Écrire votre workflow (voir exemples ci-dessous)

# ÉTAPE 4: Commit et push
git add .github/workflows/test.yml
git commit -m "Add CI workflow"
git push

# ÉTAPE 5: Voir le résultat
# Aller sur GitHub -> onglet Actions
# Votre workflow s'exécute automatiquement!


# === PREMIER WORKFLOW SIMPLE ===

# Fichier: .github/workflows/hello.yml
name: Hello World                 # Nom du workflow

on: push                          # Se déclenche à chaque push

jobs:                             # On définit les jobs
  greet:                          # Nom du job: "greet"
    runs-on: ubuntu-latest        # Machine Linux
    
    steps:                        # Liste des étapes
      - name: Say hello           # Étape 1: Dire bonjour
        run: echo "Hello, World!"
      
      - name: Show date           # Étape 2: Afficher la date
        run: date
      
      - name: List files          # Étape 3: Lister fichiers
        run: ls -la

# Résultat: Ces 3 commandes s'exécutent automatiquement à chaque push!


[OK] STRUCTURE DE BASE - COMPRENDRE LA SYNTAXE


# === FORMAT YAML ===
# GitHub Actions utilise YAML (Yet Another Markup Language)
# C'est comme du JSON mais plus lisible pour les humains

# RÈGLES IMPORTANTES:
# 1. L'indentation compte! Utiliser 2 espaces (pas de tabs!)
# 2. Les tirets (-) indiquent une liste
# 3. Les deux-points (:) séparent clé et valeur
# 4. Les # sont des commentaires

# Exemple de syntaxe YAML:
name: Mon Workflow              # name: valeur
on: push                        # Une seule ligne

jobs:                           # Début d'une section
  build:                        # Sous-section (indentée)
    runs-on: ubuntu-latest      # Propriété (encore plus indentée)
    steps:                      # Liste d'étapes
      - name: Step 1            # Élément de liste (commence par -)
        run: echo "Hello"
      - name: Step 2            # Deuxième élément
        run: echo "World"


# === WORKFLOW MINIMAL EXPLIQUÉ ===

name: Mon Workflow
# ^ Le nom affiché dans l'interface GitHub
# Optionnel mais recommandé pour s'y retrouver

on: push
# ^ QUAND exécuter ce workflow
# Ici: à chaque fois qu'on fait un "git push"

jobs:
# ^ Section qui contient tous les jobs (tâches)

  mon-job:
  # ^ ID du job (vous le nommez comme vous voulez)
  # Utilisez des tirets, pas d'espaces
  
    runs-on: ubuntu-latest
    # ^ Sur quelle machine exécuter (Linux/Windows/macOS)
    # ubuntu-latest = Linux (le plus courant)
    
    steps:
    # ^ Liste des étapes à exécuter dans l'ordre
    
      - name: Étape 1
        # ^ Nom descriptif de l'étape (optionnel mais utile)
        
        run: echo "Hello World"
        # ^ La commande shell à exécuter
        # C'est comme si vous tapiez ça dans un terminal


# === EXEMPLE COMPLET ANNOTÉ ===

name: Test Python Application
# Ce workflow teste une application Python

on: 
  push:
    # Se déclenche sur les pushs...
    branches:
      - main
      - develop
    # ...mais seulement sur ces branches
  pull_request:
    # Et aussi sur les Pull Requests
    branches:
      - main
    # ...vers la branche main

jobs:
  test:
    # Job nommé "test"
    
    runs-on: ubuntu-latest
    # Utilise une machine Linux (gratuit et rapide)
    
    steps:
      # ÉTAPE 1: Télécharger le code
      - name: Checkout code
        uses: actions/checkout@v4
        # 'uses' utilise une action pré-faite
        # checkout@v4 télécharge votre code du repo
        
      # ÉTAPE 2: Installer Python
      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
        # 'with' passe des paramètres à l'action
        # Ici: on veut Python 3.11
        
      # ÉTAPE 3: Installer les dépendances
      - name: Install dependencies
        run: |
          pip install -r requirements.txt
        # 'run' exécute une commande shell
        # | permet d'écrire plusieurs lignes
        
      # ÉTAPE 4: Exécuter les tests
      - name: Run tests
        run: pytest
        # Exécute pytest (comme dans votre terminal)


# === COMPRENDRE LES SECTIONS ===

# NAME (optionnel)
name: Mon Super Workflow
# Affiché dans GitHub > Actions
# Si absent, GitHub utilise le nom du fichier

# ON (obligatoire)
on: push
# Définit l'événement déclencheur
# Peut être simple (push) ou complexe (voir section suivante)

# JOBS (obligatoire)
jobs:
  job1:
    # Un job = une séquence d'étapes
    # Plusieurs jobs = exécution en parallèle
  job2:
    # Ce job s'exécute EN MÊME TEMPS que job1

# RUNS-ON (obligatoire dans chaque job)
runs-on: ubuntu-latest
# La "machine virtuelle" qui exécute le job
# Options: ubuntu-latest, windows-latest, macos-latest

# STEPS (obligatoire dans chaque job)
steps:
  - name: Ma première étape
    run: echo "Hello"
  - name: Ma deuxième étape
    run: echo "World"
# Liste ordonnée d'actions à effectuer
# Exécutées l'une après l'autre (séquentiellement)


# === DEUX FAÇONS D'EXÉCUTER DU CODE ===

# MÉTHODE 1: run (commande shell)
- name: Exécuter une commande
  run: echo "Hello"
  # Comme taper dans un terminal

# MÉTHODE 2: uses (action pré-faite)
- name: Utiliser une action
  uses: actions/checkout@v4
  # Utilise du code déjà écrit par quelqu'un d'autre
  # checkout = télécharger le code du repo


# === VOTRE PREMIER WORKFLOW FONCTIONNEL ===

# Fichier: .github/workflows/first.yml
name: My First Workflow

on: push
# Se déclenche à chaque push

jobs:
  hello:
    runs-on: ubuntu-latest
    
    steps:
      - name: Say hello
        run: echo "Hello from GitHub Actions!"
      
      - name: Show system info
        run: |
          echo "Runner: $RUNNER_OS"
          echo "User: $(whoami)"
          echo "Directory: $(pwd)"
      
      - name: Create a file
        run: echo "Test" > test.txt
      
      - name: Show file content
        run: cat test.txt

# Que fait ce workflow?
# 1. Dit bonjour
# 2. Affiche des infos système
# 3. Crée un fichier test.txt
# 4. Affiche son contenu

# Pour le tester:
# 1. Créez le fichier .github/workflows/first.yml
# 2. Copiez le code ci-dessus
# 3. git add, commit, push
# 4. Allez sur GitHub > Actions
# 5. Regardez le workflow s'exécuter! [BRAVO]


[OK] ÉVÉNEMENTS DÉCLENCHEURS (ON) - QUAND EXÉCUTER ?


# === CONCEPT ===
# L'événement (event) définit QUAND votre workflow s'exécute
# C'est le "déclencheur" automatique

# ÉVÉNEMENTS COURANTS:
# - push: Quand on pousse du code
# - pull_request: Quand on crée/modifie une PR
# - schedule: À intervalle régulier (comme cron)
# - workflow_dispatch: Déclenchement manuel


# === PUSH - À CHAQUE PUSH DE CODE ===

# Simple: sur tous les pushs
on: push

# Sur certaines branches uniquement
on:
  push:
    branches:
      - main          # Seulement sur main
      - develop       # Et sur develop

# Explication:
# Ce workflow s'exécute UNIQUEMENT quand vous faites:
# git push origin main
# ou
# git push origin develop
# Mais PAS sur les autres branches

# Avec pattern de branches
on:
  push:
    branches:
      - main
      - 'releases/**'    # Toutes les branches releases/*
      - 'feature/*'      # Toutes les branches feature/*

# Sur certains fichiers uniquement
on:
  push:
    paths:
      - '**.py'          # Seulement si fichiers Python modifiés
      - 'src/**'         # Ou fichiers dans src/

# Explication:
# Le workflow s'exécute SEULEMENT si vous modifiez:
# - Un fichier .py
# - OU un fichier dans le dossier src/

# Ignorer certains fichiers
on:
  push:
    paths-ignore:
      - '**.md'          # Ignore les fichiers Markdown
      - 'docs/**'        # Ignore le dossier docs/

# Cas d'usage:
# "Je ne veux PAS exécuter les tests si je modifie juste la doc"


# === PULL REQUEST - QUAND ON OUVRE/MODIFIE UNE PR ===

# Simple: sur toutes les PR
on: pull_request

# Expliqué:
on:
  pull_request:
    types:
      - opened          # PR créée
      - synchronize     # Nouveaux commits ajoutés
      - reopened        # PR réouverte
    branches:
      - main            # Seulement vers main

# Cas d'usage typique:
# "Exécuter les tests automatiquement sur chaque PR vers main"

# Exemple pratique:
name: PR Checks

on:
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run tests
        run: pytest

# Résultat: À chaque PR vers main, les tests s'exécutent
# GitHub affiche [OK] ou [X] sur la PR


# === SCHEDULE - EXÉCUTION PÉRIODIQUE (CRON) ===

# Tous les jours à minuit UTC
on:
  schedule:
    - cron: '0 0 * * *'

# Explication du format cron:
# '0 0 * * *'
#  │ │ │ │ │
#  │ │ │ │ └─── Jour de la semaine (0-6, 0=Dimanche)
#  │ │ │ └───── Mois (1-12)
#  │ │ └─────── Jour du mois (1-31)
#  │ └───────── Heure (0-23)
#  └─────────── Minute (0-59)

# Exemples pratiques:

# Envoyer notification Slack seulement si échec:
- name: Notify Slack on failure
  if: failure()
  run: |
    curl -X POST ${{ secrets.SLACK_WEBHOOK }} \
      -d '{"text": "Build failed!"}'

# Deployer seulement si tests OK et branche main:
- name: Deploy
  if: success() && github.ref == 'refs/heads/main'
  run: ./deploy.sh


# === OUTPUTS - PARTAGER DES VALEURS ENTRE STEPS ===

# Step qui génère une valeur:
- name: Generate version
  id: version                 # <- ID pour référencer ce step
  run: echo "number=1.2.3" >> $GITHUB_OUTPUT

# Step qui utilise cette valeur:
- name: Use version
  run: echo "Version: ${{ steps.version.outputs.number }}"

# Explication:
# 1. Premier step génère "number=1.2.3"
# 2. Deuxième step lit cette valeur
# 3. Affiche "Version: 1.2.3"


# === TIMEOUT SUR UN STEP ===

- name: Long running task
  timeout-minutes: 10       # Max 10 minutes
  run: ./long-script.sh

# Si dépassé: Step annulé et workflow échoue
# Utile pour éviter les blocages


[OK] ACTIONS OFFICIELLES - LES PLUS UTILISÉES


# === POURQUOI DES ACTIONS ? ===
# Au lieu d'écrire 20 lignes de bash à chaque fois,
# on réutilise du code déjà écrit et testé!

# Les actions viennent de:
# 1. GitHub (actions/*)
# 2. Éditeurs (comme aws-actions/*)
# 3. Communauté


# === ACTIONS/CHECKOUT - TÉLÉCHARGER VOTRE CODE ===

# Version simple (99% des cas):
- uses: actions/checkout@v4

# Ce que ça fait:
# 1. Clone votre repository
# 2. Checkout la branche/commit de l'événement
# 3. Votre code est dans /home/runner/work/repo/repo

# [ATTENTION] À METTRE EN PREMIER STEP TOUJOURS!
# Sans ça, votre code n'est pas disponible!

# Options avancées:
- uses: actions/checkout@v4
  with:
    fetch-depth: 0            # Télécharge tout l'historique git
    submodules: true          # Avec submodules git
    token: ${{ secrets.GITHUB_TOKEN }}  # Token d'accès
    ref: develop              # Branch spécifique


# === ACTIONS/SETUP-PYTHON - INSTALLER PYTHON ===

# Version simple:
- uses: actions/setup-python@v5
  with:
    python-version: '3.11'

# Ce que ça fait:
# 1. Installe Python 3.11
# 2. Configure pip
# 3. Ajoute python/pip au PATH
# 4. Vous pouvez faire: python, pip, pytest, etc.

# Avec cache (RECOMMANDÉ):
- uses: actions/setup-python@v5
  with:
    python-version: '3.11'
    cache: 'pip'              # <- Cache automatique pour pip

# Avantage du cache:
# Sans cache: Install dependencies = 2 minutes
# Avec cache: Install dependencies = 10 secondes!

# Cache fonctionne avec:
# - pip (requirements.txt)
# - poetry (poetry.lock)
# - pipenv (Pipfile.lock)

# Exemple complet:
- uses: actions/setup-python@v5
  with:
    python-version: '3.11'
    cache: 'pip'
    cache-dependency-path: '**/requirements*.txt'

- name: Install dependencies
  run: pip install -r requirements.txt
  # <- Cette étape sera très rapide grâce au cache!


# === ACTIONS/CACHE - CACHER DES FICHIERS ===

# Pourquoi? Réutiliser des fichiers entre runs
# Exemple: Dépendances pip, node_modules, etc.

- uses: actions/cache@v4
  with:
    path: ~/.cache/pip              # Quoi cacher
    key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
    # ^ Clé unique basée sur OS + hash du requirements.txt
    restore-keys: |
      ${{ runner.os }}-pip-
    # ^ Fallback si la clé exacte n'existe pas

# Comment ça marche:
# 1er run: 
#   - Clé pas trouvée -> Pas de cache
#   - Installe tout normalement
#   - Sauvegarde le cache avec la clé
# 2e run:
#   - Clé trouvée -> Restaure le cache
#   - Installation très rapide!

# Cache multiple paths:
- uses: actions/cache@v4
  with:
    path: |
      ~/.cache/pip
      ~/.cache/pypoetry
      .venv
    key: ${{ runner.os }}-python-${{ hashFiles('**/poetry.lock') }}


# === ACTIONS/UPLOAD-ARTIFACT - SAUVEGARDER DES FICHIERS ===

# Pourquoi? Conserver des fichiers générés
# Exemples: Builds, rapports de tests, logs

- name: Build application
  run: npm run build

- name: Upload build artifacts
  uses: actions/upload-artifact@v4
  with:
    name: dist                      # Nom de l'artifact
    path: dist/                     # Fichiers à sauvegarder

# Ce qui se passe:
# 1. Fichiers uploadés vers GitHub
# 2. Disponibles dans l'interface Actions
# 3. Téléchargeables pendant 90 jours (par défaut)

# Upload multiple paths:
- uses: actions/upload-artifact@v4
  with:
    name: test-results
    path: |
      test-results/
      coverage/
      logs/*.log
    retention-days: 30              # Garde 30 jours


# === ACTIONS/DOWNLOAD-ARTIFACT - RÉCUPÉRER DES FICHIERS ===

# Utiliser dans un job différent:
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist/
  
  deploy:
    needs: build                    # Attend le job build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: dist                # Télécharge l'artifact
          path: dist/
      - name: Deploy
        run: ./deploy.sh dist/

# Flux:
# 1. Job build: Compile et upload
# 2. Job deploy: Download et déploie


# === ACTIONS/SETUP-NODE - INSTALLER NODE.JS ===

- uses: actions/setup-node@v4
  with:
    node-version: '18'
    cache: 'npm'                    # Cache node_modules

# Autres outils:
- uses: actions/setup-java@v4
  with:
    distribution: 'temurin'
    java-version: '17'

- uses: actions/setup-go@v5
  with:
    go-version: '1.21'

- uses: ruby/setup-ruby@v1
  with:
    ruby-version: '3.2'


# === EXEMPLE WORKFLOW COMPLET ANNOTÉ ===

name: Complete Python Workflow

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      # 1. Télécharger le code (TOUJOURS EN PREMIER!)
      - name: Checkout repository
        uses: actions/checkout@v4
      
      # 2. Installer Python avec cache
      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'
      
      # 3. Installer dépendances (rapide grâce au cache)
      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install pytest pytest-cov
      
      # 4. Exécuter les tests
      - name: Run tests
        run: pytest --cov=src --cov-report=xml
      
      # 5. Sauvegarder le rapport de couverture
      - name: Upload coverage report
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report
          path: coverage.xml
      
      # 6. Upload vers Codecov (service externe)
      - name: Upload to Codecov
        uses: codecov/codecov-action@v4
        with:
          file: ./coverage.xml
          fail_ci_if_error: true


[OK] VARIABLES & SECRETS - GÉRER LES DONNÉES


# === CONCEPT ===
# Variables = Données normales (API URL, noms...)
# Secrets = Données sensibles (passwords, tokens...)

# [ATTENTION] RÈGLE D'OR:
# Ne JAMAIS mettre de secrets en dur dans le code!


# === VARIABLES D'ENVIRONNEMENT ===

# Au niveau du workflow (globales):
env:
  NODE_ENV: production
  API_URL: https://api.example.com

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - run: echo $NODE_ENV
      # Affiche: production

# Au niveau d'un job:
jobs:
  test:
    runs-on: ubuntu-latest
    env:
      DATABASE_URL: postgresql://localhost:5432/test
    steps:
      - run: echo $DATABASE_URL

# Au niveau d'un step:
steps:
  - name: Build
    run: npm run build
    env:
      NODE_OPTIONS: --max-old-space-size=4096


# === VARIABLES GITHUB AUTOMATIQUES ===

# GitHub crée automatiquement des variables:
- run: echo "Repository: ${{ github.repository }}"
  # Affiche: username/repo-name

- run: echo "Branch: ${{ github.ref }}"
  # Affiche: refs/heads/main

- run: echo "Commit: ${{ github.sha }}"
  # Affiche: abc123... (SHA du commit)

- run: echo "Actor: ${{ github.actor }}"
  # Affiche: username (qui a déclenché)

# Autres variables utiles:
${{ github.event_name }}          # push, pull_request, etc.
${{ github.run_id }}              # ID unique du run
${{ github.run_number }}          # Numéro du run (1, 2, 3...)
${{ runner.os }}                  # Linux, Windows, macOS
${{ runner.temp }}                # Dossier temporaire

# Variables d'environnement (style shell):
$GITHUB_WORKSPACE                 # /home/runner/work/repo/repo
$GITHUB_REPOSITORY                # owner/repo
$GITHUB_SHA                       # Commit SHA
$GITHUB_REF                       # refs/heads/branch
$GITHUB_ACTOR                     # username
$RUNNER_OS                        # Linux, Windows, macOS


# === SECRETS - DONNÉES SENSIBLES ===

# Comment ajouter un secret:
# 1. GitHub -> Settings -> Secrets and variables -> Actions
# 2. Click "New repository secret"
# 3. Name: API_KEY
# 4. Value: votre_clé_secrète_123
# 5. Add secret

# Utiliser dans un workflow:
- name: Deploy
  run: ./deploy.sh
  env:
    API_KEY: ${{ secrets.API_KEY }}

# [ATTENTION] SÉCURITÉ:
# - Secrets sont automatiquement masqués dans les logs
# - Si vous faites: echo $API_KEY
# - GitHub affiche: echo ***

# Ne JAMAIS faire:
- run: echo "My secret is ${{ secrets.API_KEY }}"
# [X] Même si masqué, c'est dangereux!

# Faire plutôt:
- run: ./script.sh
  env:
    API_KEY: ${{ secrets.API_KEY }}
# [OK] Le script utilise $API_KEY sans l'afficher


# === SECRETS COURANTS ===

# Token GitHub (auto-fourni, pas besoin de le créer):
env:
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Permet d'interagir avec l'API GitHub

# Token PyPI (pour publier des packages):
- name: Publish to PyPI
  env:
    TWINE_USERNAME: __token__
    TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
  run: twine upload dist/*

# AWS Credentials:
env:
  AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
  AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

# Docker Hub:
- name: Login to Docker Hub
  uses: docker/login-action@v3
  with:
    username: ${{ secrets.DOCKERHUB_USERNAME }}
    password: ${{ secrets.DOCKERHUB_TOKEN }}


# === VARIABLES DE REPOSITORY ===

# Pour données non-sensibles mais configurables:
# Settings -> Secrets and variables -> Actions -> Variables

# Créer variable: API_URL = https://api.example.com

# Utiliser:
- run: echo "API: ${{ vars.API_URL }}"

# Différence Variables vs Secrets:
# Variables: Visibles dans l'interface, pour config
# Secrets: Masqués, pour données sensibles


# === DÉFINIR DES VARIABLES DYNAMIQUEMENT ===

# Créer une variable pour les steps suivants:
- name: Set environment variable
  run: echo "VERSION=1.2.3" >> $GITHUB_ENV

- name: Use variable
  run: echo "Version is $VERSION"
  # Affiche: Version is 1.2.3

# Exemple pratique - Extraire version depuis fichier:
- name: Get version
  run: |
    VERSION=$(python -c "import setup; print(setup.__version__)")
    echo "VERSION=$VERSION" >> $GITHUB_ENV

- name: Build with version
  run: docker build -t myapp:$VERSION .


# === VARIABLES MULTILIGNES ===

- name: Set multiline variable
  run: |
    echo 'CONFIG<<EOF' >> $GITHUB_ENV
    echo '{"key": "value"}' >> $GITHUB_ENV
    echo 'EOF' >> $GITHUB_ENV

- name: Use multiline variable
  run: echo "$CONFIG"


# === EXEMPLE COMPLET AVEC SECRETS ===

name: Deploy to Production

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Build Docker image
        run: docker build -t myapp:latest .
      
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
      
      - name: Push image
        run: docker push myapp:latest
      
      - name: Deploy to server
        env:
          SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
          SERVER_HOST: ${{ vars.SERVER_HOST }}
        run: |
          echo "$SSH_KEY" > key.pem
          chmod 600 key.pem
          ssh -i key.pem user@$SERVER_HOST "docker pull myapp:latest && docker restart myapp"


[OK] WORKFLOWS PYTHON - EXEMPLES COMPLETS POUR DÉBUTANTS


# === WORKFLOW 1: TESTS SIMPLES ===

# Fichier: .github/workflows/test.yml
name: Tests

on:
  push:                             # À chaque push
  pull_request:                     # Et chaque PR

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      # Télécharger votre code
      - name: Checkout code
        uses: actions/checkout@v4
      
      # Installer Python
      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'              # Cache pour aller vite
      
      # Installer vos dépendances
      - name: Install dependencies
        run: |
          pip install -r requirements.txt
      
      # Exécuter vos tests
      - name: Run tests
        run: pytest

# Ce workflow:
# 1. S'exécute à chaque push/PR
# 2. Installe Python 3.11
# 3. Installe vos dépendances
# 4. Lance pytest
# 5. Vous montre [OK] ou [X]


# === WORKFLOW 2: TESTS + LINTING ===

name: CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'
      
      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install flake8 black pytest
      
      # Vérifier le style de code
      - name: Lint with flake8
        run: flake8 .
      
      # Vérifier le formatage
      - name: Check formatting with black
        run: black --check .
      
      # Lancer les tests
      - name: Run tests
        run: pytest

# Ce workflow vérifie:
# 1. Style de code (flake8)
# 2. Formatage (black)
# 3. Tests (pytest)
# = Code quality checks complets!


# === WORKFLOW 3: TESTER PLUSIEURS VERSIONS PYTHON ===

name: Test Multiple Versions

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ['3.9', '3.10', '3.11', '3.12']
        # ^ Teste avec 4 versions de Python
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          cache: 'pip'
      
      - name: Install dependencies
        run: pip install -r requirements.txt
      
      - name: Run tests
        run: pytest

# Résultat: 4 jobs en parallèle
# - Test with Python 3.9 [OK]
# - Test with Python 3.10 [OK]
# - Test with Python 3.11 [OK]
# - Test with Python 3.12 [OK]


# === WORKFLOW 4: PROJET DJANGO AVEC POSTGRESQL ===

name: Django Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    # Démarrer PostgreSQL automatiquement
    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: testdb
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'
      
      - name: Install dependencies
        run: |
          pip install -r requirements.txt
      
      # Lancer migrations Django
      - name: Run migrations
        env:
          DATABASE_URL: postgresql://postgres:postgres@localhost:5432/testdb
        run: python manage.py migrate
      
      # Lancer tests Django
      - name: Run tests
        env:
          DATABASE_URL: postgresql://postgres:postgres@localhost:5432/testdb
        run: python manage.py test

# Ce workflow:
# 1. Démarre une vraie base PostgreSQL
# 2. Lance les migrations Django
# 3. Exécute les tests avec la DB
# = Tests comme en production!


# === WORKFLOW 5: PUBLIER SUR PYPI ===

name: Publish to PyPI

on:
  release:
    types: [published]              # Quand vous créez une release GitHub

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      
      # Construire le package
      - name: Build package
        run: |
          pip install build
          python -m build
      
      # Publier sur PyPI
      - name: Publish to PyPI
        env:
          TWINE_USERNAME: __token__
          TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
        run: |
          pip install twine
          twine upload dist/*

# Comment utiliser:
# 1. Créez PYPI_API_TOKEN dans les secrets
# 2. Sur GitHub, créez une Release (tag v1.0.0)
# 3. Le workflow publie automatiquement sur PyPI!


# === WORKFLOW 6: DÉPLOIEMENT SIMPLE ===

name: Deploy

on:
  push:
    branches: [main]                # Seulement sur main

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Deploy via SSH
        env:
          SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
          SERVER: ${{ vars.SERVER_HOST }}
        run: |
          # Configurer SSH
          mkdir -p ~/.ssh
          echo "$SSH_KEY" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          
          # Déployer
          ssh -o StrictHostKeyChecking=no user@$SERVER << 'EOF'
            cd /var/www/myapp
            git pull
            pip install -r requirements.txt
            sudo systemctl restart myapp
          EOF

# Prérequis:
# 1. Créer SSH_PRIVATE_KEY secret (votre clé SSH privée)
# 2. Créer SERVER_HOST variable (IP de votre serveur)
# 3. Push sur main = déploiement automatique!


[OK] COMPRENDRE LES ERREURS - DEBUGGING POUR DÉBUTANTS


# === OÙ VOIR LES LOGS ? ===

# 1. Allez sur GitHub
# 2. Cliquez sur l'onglet "Actions"
# 3. Cliquez sur un workflow run (ex: "Tests #42")
# 4. Cliquez sur un job (ex: "test")
# 5. Cliquez sur un step pour voir ses logs

# Exemple de structure:
# Tests #42 (workflow run)
#   └─ test (job)
#       ├─ Checkout code
#       ├─ Setup Python
#       ├─ Install dependencies
#       └─ Run tests <- Cliquez ici pour voir les logs


# === COMPRENDRE LES STATUTS ===

# [OK] VERT (Success)
# Tout s'est bien passé!

# [X] ROUGE (Failure)
# Une erreur s'est produite
# -> Cliquez sur le step rouge pour voir l'erreur

# [JAUNE] JAUNE (In progress)
# En cours d'exécution, attendez...

# [BLANC] GRIS (Cancelled)
# Vous ou GitHub avez annulé le workflow

# ⊘ GRIS (Skipped)
# Step sauté (condition if: non satisfaite)


# === ERREURS COURANTES ET SOLUTIONS ===

# ERREUR 1: "No such file or directory: requirements.txt"
- name: Install dependencies
  run: pip install -r requirements.txt
# [X] ERREUR: requirements.txt not found

# CAUSE: Vous avez oublié actions/checkout@v4!
# SOLUTION:
- uses: actions/checkout@v4        # <- Ajoutez ça EN PREMIER!
- run: pip install -r requirements.txt


# ERREUR 2: "python: command not found"
- run: python script.py
# [X] ERREUR: python: command not found

# CAUSE: Python pas installé
# SOLUTION:
- uses: actions/setup-python@v5    # <- Installez Python!
  with:
    python-version: '3.11'
- run: python script.py


# ERREUR 3: "pytest: command not found"
- run: pytest
# [X] ERREUR: pytest: command not found

# CAUSE: pytest pas installé
# SOLUTION:
- run: pip install pytest          # <- Installez pytest!
- run: pytest


# ERREUR 4: "Permission denied"
- run: ./deploy.sh
# [X] ERREUR: Permission denied

# CAUSE: Script pas exécutable
# SOLUTION 1:
- run: chmod +x deploy.sh          # Rendre exécutable
- run: ./deploy.sh

# SOLUTION 2:
- run: bash deploy.sh              # Exécuter avec bash


# ERREUR 5: Tests échouent
- run: pytest
# [X] ERREUR: 5 failed, 10 passed

# CAUSE: Vos tests ont trouvé des bugs!
# SOLUTION:
# 1. Regardez les logs détaillés
# 2. Corrigez les bugs dans votre code
# 3. Commit et push
# 4. Le workflow se relance automatiquement


# ERREUR 6: "Invalid workflow file"
# [X] ERREUR: .github/workflows/test.yml is not valid

# CAUSES COURANTES:
# - Indentation incorrecte (utilisez 2 espaces, pas tabs!)
# - Faute de frappe dans un mot-clé
# - Mauvaise syntaxe YAML

# VÉRIFICATION:
# Copiez votre YAML dans https://www.yamllint.com/
# Le site vous montrera les erreurs de syntaxe


# ERREUR 7: "Resource not accessible by integration"
- run: gh issue create --title "Bug"
# [X] ERREUR: Resource not accessible by integration

# CAUSE: Permissions insuffisantes
# SOLUTION:
permissions:
  issues: write                     # <- Ajoutez les permissions!
  contents: read

jobs:
  create-issue:
    runs-on: ubuntu-latest
    steps:
      - run: gh issue create --title "Bug"
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}


# === ACTIVER LES LOGS DE DEBUG ===

# Pour voir encore PLUS de détails:
# 1. Settings -> Secrets and variables -> Actions -> Secrets
# 2. New repository secret
# 3. Name: ACTIONS_RUNNER_DEBUG
# 4. Value: true
# 5. New repository secret
# 6. Name: ACTIONS_STEP_DEBUG  
# 7. Value: true

# Ensuite, relancez votre workflow
# Vous verrez BEAUCOUP plus d'informations!


# === DEBUGGER EN AJOUTANT DES ECHO ===

# Ajoutez des echo pour comprendre ce qui se passe:
- name: Debug info
  run: |
    echo "Current directory: $(pwd)"
    echo "Files: $(ls -la)"
    echo "Python version: $(python --version)"
    echo "Which python: $(which python)"
    echo "Environment variables:"
    env

# Résultat: Vous voyez exactement l'état du système


# === EXEMPLE COMPLET DE DEBUGGING ===

name: Debug Workflow

on: push

jobs:
  debug:
    runs-on: ubuntu-latest
    
    steps:
      - name: Show context
        run: |
          echo "Event: ${{ github.event_name }}"
          echo "Branch: ${{ github.ref }}"
          echo "Commit: ${{ github.sha }}"
          echo "Actor: ${{ github.actor }}"
      
      - uses: actions/checkout@v4
      
      - name: Show files
        run: |
          echo "Working directory:"
          pwd
          echo "Files:"
          ls -la
      
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      
      - name: Python info
        run: |
          python --version
          pip --version
          which python
      
      - name: Install dependencies
        run: |
          echo "Installing dependencies..."
          pip install -r requirements.txt
          echo "Installed packages:"
          pip list
      
      - name: Run tests with verbose output
        run: pytest -v


[OK] BONNES PRATIQUES ESSENTIELLES


# === 1. TOUJOURS COMMENCER PAR CHECKOUT ===

# [X] MAUVAIS:
steps:
  - run: pytest                     # Votre code n'est pas là!

# [OK] BON:
steps:
  - uses: actions/checkout@v4       # D'abord télécharger le code
  - run: pytest                     # Maintenant ça marche


# === 2. UTILISER LE CACHE ===

# [X] SANS CACHE: Installation = 2 minutes à chaque fois
steps:
  - uses: actions/setup-python@v5
    with:
      python-version: '3.11'
  - run: pip install -r requirements.txt

# [OK] AVEC CACHE: Installation = 10 secondes après la 1ère fois
steps:
  - uses: actions/setup-python@v5
    with:
      python-version: '3.11'
      cache: 'pip'                  # <- Ajoutez juste ça!
  - run: pip install -r requirements.txt


# === 3. NOMMER VOS STEPS ===

# [X] MAUVAIS: Difficile de savoir ce qui échoue
steps:
  - run: pip install pytest
  - run: pytest
  - run: flake8 .

# [OK] BON: On comprend immédiatement
steps:
  - name: Install pytest
    run: pip install pytest
  
  - name: Run tests
    run: pytest
  
  - name: Check code style
    run: flake8 .


# === 4. UTILISER BRANCHES POUR DÉCLENCHEURS ===

# [X] MAUVAIS: S'exécute sur TOUTES les branches
on: push

# [OK] BON: Seulement sur les branches importantes
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]


# === 5. IGNORER LES FICHIERS INUTILES ===

# [X] Tests s'exécutent même si vous modifiez juste README.md
on: push

# [OK] BON: Tests seulement si code modifié
on:
  push:
    paths-ignore:
      - '**.md'                     # Ignore Markdown
      - 'docs/**'                   # Ignore docs


# === 6. UTILISER DES TIMEOUTS ===

# [X] MAUVAIS: Peut bloquer pendant des heures
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - run: pytest

# [OK] BON: Timeout pour éviter blocages
jobs:
  test:
    runs-on: ubuntu-latest
    timeout-minutes: 10             # Max 10 minutes
    steps:
      - run: pytest


# === 7. NE JAMAIS LOGGER DES SECRETS ===

# [X] DANGEREUX:
- run: echo "API Key: ${{ secrets.API_KEY }}"

# [OK] BON:
- run: ./deploy.sh
  env:
    API_KEY: ${{ secrets.API_KEY }}
# Le script utilise $API_KEY sans l'afficher


# === 8. SÉPARER LES JOBS ===

# [X] MAUVAIS: Tout dans un job
jobs:
  everything:
    runs-on: ubuntu-latest
    steps:
      - run: flake8 .
      - run: pytest
      - run: mypy .
      - run: ./deploy.sh

# [OK] BON: Jobs séparés (exécution parallèle)
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - run: flake8 .
  
  test:
    runs-on: ubuntu-latest
    steps:
      - run: pytest
  
  typecheck:
    runs-on: ubuntu-latest
    steps:
      - run: mypy .
  
  deploy:
    needs: [lint, test, typecheck]
    runs-on: ubuntu-latest
    steps:
      - run: ./deploy.sh


# === 9. UTILISER CONDITIONS POUR DÉPLOIEMENT ===

# [X] MAUVAIS: Déploie depuis n'importe quelle branche
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - run: ./deploy.sh

# [OK] BON: Déploie seulement depuis main
jobs:
  deploy:
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - run: ./deploy.sh


# === 10. VERSIONNER VOS ACTIONS ===

# [X] RISQUÉ:
- uses: actions/checkout@main       # Version changeante

# [OK] BON:
- uses: actions/checkout@v4         # Version stable


[OK] PROJET COMPLET DE A À Z - GUIDE PRATIQUE


# === SCÉNARIO: Application Flask à déployer ===

# Structure du projet:
# myapp/
# ├── app.py
# ├── requirements.txt
# ├── tests/
# │   └── test_app.py
# └── .github/
#     └── workflows/
#         └── ci-cd.yml


# === ÉTAPE 1: CRÉER LE PROJET ===

# app.py
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello():
    return "Hello World!"

if __name__ == '__main__':
    app.run()

# requirements.txt
flask==3.0.0
pytest==7.4.3

# tests/test_app.py
from app import app

def test_hello():
    client = app.test_client()
    response = client.get('/')
    assert response.status_code == 200
    assert b"Hello World!" in response.data


# === ÉTAPE 2: CRÉER LE WORKFLOW ===

# .github/workflows/ci-cd.yml
name: Flask CI/CD

# Quand exécuter?
on:
  push:
    branches: [main, develop]       # Sur ces branches
  pull_request:
    branches: [main]                # Et PRs vers main

# Les jobs à exécuter
jobs:
  # JOB 1: Tests
  test:
    runs-on: ubuntu-latest
    
    steps:
      # 1. Télécharger le code
      - name: Checkout code
        uses: actions/checkout@v4
      
      # 2. Installer Python
      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'
      
      # 3. Installer les dépendances
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
      
      # 4. Lancer les tests
      - name: Run tests
        run: pytest -v
  
  # JOB 2: Linting (en parallèle avec test)
  lint:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'
      
      - name: Install flake8
        run: pip install flake8
      
      - name: Lint code
        run: flake8 app.py tests/
  
  # JOB 3: Déploiement (seulement après test + lint OK)
  deploy:
    needs: [test, lint]             # Attend ces 2 jobs
    if: github.ref == 'refs/heads/main'  # Seulement sur main
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Deploy to server
        run: |
          echo "Deploying to production..."
          # Votre script de déploiement ici


# === ÉTAPE 3: CONFIGURER LES SECRETS ===

# 1. Allez sur GitHub -> Settings -> Secrets and variables -> Actions
# 2. Créez ces secrets:
#    - SERVER_HOST: votre-serveur.com
#    - SSH_PRIVATE_KEY: (votre clé SSH)
#    - DEPLOY_USER: ubuntu


# === ÉTAPE 4: AMÉLIORER LE DÉPLOIEMENT ===

# Mise à jour du job deploy:
deploy:
  needs: [test, lint]
  if: github.ref == 'refs/heads/main'
  runs-on: ubuntu-latest
  
  steps:
    - uses: actions/checkout@v4
    
    - name: Deploy via SSH
      env:
        SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
        SERVER: ${{ secrets.SERVER_HOST }}
        USER: ${{ secrets.DEPLOY_USER }}
      run: |
        # Configurer SSH
        mkdir -p ~/.ssh
        echo "$SSH_KEY" > ~/.ssh/id_rsa
        chmod 600 ~/.ssh/id_rsa
        
        # Se connecter et déployer
        ssh -o StrictHostKeyChecking=no $USER@$SERVER << 'EOF'
          cd /var/www/myapp
          git pull origin main
          pip install -r requirements.txt
          sudo systemctl restart myapp
        EOF


# === ÉTAPE 5: TESTER LE WORKFLOW ===

# 1. Créez tous les fichiers ci-dessus
# 2. Commitez:
git add .
git commit -m "Add CI/CD workflow"
git push origin main

# 3. Allez sur GitHub -> Actions
# 4. Vous verrez votre workflow s'exécuter!

# Résultat attendu:
# [OK] test - Success
# [OK] lint - Success  
# [OK] deploy - Success (seulement si sur main)


# === ÉTAPE 6: CRÉER UNE PULL REQUEST ===

# 1. Créez une nouvelle branche:
git checkout -b feature/new-endpoint
git push origin feature/new-endpoint

# 2. Sur GitHub, créez une PR vers main

# 3. Le workflow s'exécute automatiquement:
# [OK] test - Success
# [OK] lint - Success
# ⊘ deploy - Skipped (pas sur main)

# 4. Si tout est vert, vous pouvez merger!


[OK] EXEMPLES PAR TYPES DE PROJETS


# === PROJET 1: API FASTAPI ===

name: FastAPI CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    # Base de données pour les tests
    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: testdb
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
        ports:
          - 5432:5432
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'
      
      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install pytest httpx
      
      - name: Run tests
        env:
          DATABASE_URL: postgresql://postgres:postgres@localhost:5432/testdb
        run: pytest


# === PROJET 2: SCRAPER WEB ===

name: Run Scraper

on:
  schedule:
    - cron: '0 */6 * * *'           # Toutes les 6 heures
  workflow_dispatch:                # + déclenchement manuel

jobs:
  scrape:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'
      
      - name: Install dependencies
        run: |
          pip install scrapy pandas
      
      - name: Run scraper
        run: |
          python scraper.py
      
      - name: Upload results
        uses: actions/upload-artifact@v4
        with:
          name: scrape-results-${{ github.run_number }}
          path: results.csv


# === PROJET 3: JUPYTER NOTEBOOKS ===

name: Test Notebooks

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'
      
      - name: Install dependencies
        run: |
          pip install jupyter nbconvert pytest nbval
      
      - name: Test notebooks
        run: |
          pytest --nbval-lax notebooks/
      
      - name: Convert to HTML
        run: |
          jupyter nbconvert --to html notebooks/*.ipynb
      
      - name: Upload HTML
        uses: actions/upload-artifact@v4
        with:
          name: notebooks-html
          path: notebooks/*.html


# === PROJET 4: PACKAGE PYTHON À PUBLIER ===

name: Publish Package

on:
  release:
    types: [published]

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      
      - name: Install build tools
        run: |
          pip install build twine
      
      - name: Build package
        run: |
          python -m build
      
      - name: Check package
        run: |
          twine check dist/*
      
      - name: Publish to PyPI
        env:
          TWINE_USERNAME: __token__
          TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
        run: |
          twine upload dist/*


# === PROJET 5: APPLICATION AVEC DOCKER ===

name: Docker Build and Push

on:
  push:
    branches: [main]
    tags: ['v*']

jobs:
  docker:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
      
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            myuser/myapp:latest
            myuser/myapp:${{ github.sha }}


[OK] RÉSOLUTION DE PROBLÈMES COURANTS


# === PROBLÈME 1: Workflow ne se déclenche pas ===

# Vérifications:
# 1. Fichier dans .github/workflows/ ? [OK]
# 2. Extension .yml ou .yaml ? [OK]
# 3. Syntaxe YAML valide ? (testez sur yamllint.com)
# 4. Événement configuré correctement ?

# Exemple d'erreur silencieuse:
on:
  push
    branches: [main]                # [X] Manque ':' après push!

# Correction:
on:
  push:                             # [OK] Avec ':'
    branches: [main]


# === PROBLÈME 2: Tests passent localement mais échouent sur GitHub ===

# Causes courantes:
# 1. Dépendances manquantes dans requirements.txt
# 2. Variables d'environnement différentes
# 3. Base de données pas disponible
# 4. Fichiers non commités

# Solution 1: Vérifier requirements.txt
pip freeze > requirements.txt       # Générer depuis votre environnement

# Solution 2: Ajouter services si besoin de DB
services:
  postgres:
    image: postgres:15

# Solution 3: Vérifier les fichiers
git status                          # Fichiers non trackés?


# === PROBLÈME 3: Workflow trop lent ===

# Sans cache: 3-5 minutes
# Avec cache: 30 secondes!

# [X] LENT:
- uses: actions/setup-python@v5
  with:
    python-version: '3.11'
- run: pip install -r requirements.txt

# [OK] RAPIDE:
- uses: actions/setup-python@v5
  with:
    python-version: '3.11'
    cache: 'pip'                    # <- Ajoutez ça!
- run: pip install -r requirements.txt


# === PROBLÈME 4: "Module not found" dans les tests ===

# Erreur typique:
# ModuleNotFoundError: No module named 'mypackage'

# Cause: Package local pas installé

# Solution 1: Installer en mode développement
- name: Install package
  run: pip install -e .             # Mode editable

# Solution 2: Ajouter au PYTHONPATH
- name: Run tests
  env:
    PYTHONPATH: ${{ github.workspace }}/src
  run: pytest


# === PROBLÈME 5: Secrets ne fonctionnent pas ===

# Vérifications:
# 1. Secret créé dans Settings -> Secrets ? [OK]
# 2. Nom EXACT (sensible à la casse) ? [OK]
# 3. Syntaxe correcte dans le workflow ? [OK]

# [X] MAUVAIS:
- run: echo ${{ secret.API_KEY }}   # [X] 'secret' au lieu de 'secrets'

# [OK] BON:
- run: ./script.sh
  env:
    API_KEY: ${{ secrets.API_KEY }} # [OK] 'secrets' (pluriel!)


# === PROBLÈME 6: Dépassement de limite de temps ===

# Erreur: "The job running on runner ... has exceeded the maximum execution time of 360 minutes."

# Solution: Ajouter timeout
jobs:
  test:
    timeout-minutes: 30             # <- Limite à 30 minutes
    runs-on: ubuntu-latest


# === PROBLÈME 7: Conflit de concurrency ===

# Plusieurs workflows qui se lancent en même temps

# Solution: Annuler les précédents
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true


[OK] ASTUCES ET RACCOURCIS


# === ASTUCE 1: Tester rapidement un workflow ===

# Créer un workflow minimal pour tester:
name: Quick Test

on: workflow_dispatch                # Déclenchement manuel

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - run: echo "Testing!"

# Allez sur GitHub -> Actions -> Quick Test -> Run workflow
# = Test instantané!


# === ASTUCE 2: Skip CI avec message de commit ===

git commit -m "Update docs [skip ci]"
# Le workflow ne s'exécutera pas pour ce commit

# Utile pour:
# - Modifications de documentation
# - README updates
# - Corrections de typos


# === ASTUCE 3: Réutiliser des steps ===

# Créez un fichier YAML avec vos steps communs:
# .github/workflows/setup.yml (pas vraiment possible, mais concept)

# Mieux: Utilisez composite actions
# Voir section "Composite Actions" plus haut


# === ASTUCE 4: Variables globales ===

# Définir une fois, utiliser partout:
env:
  PYTHON_VERSION: '3.11'
  NODE_VERSION: '18'

jobs:
  backend:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ env.PYTHON_VERSION }}
  
  frontend:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}


# === ASTUCE 5: Voir les fichiers générés ===

# Ajoutez des steps de debug:
- name: List files
  run: |
    echo "Current directory:"
    ls -la
    echo "Dist directory:"
    ls -la dist/


# === ASTUCE 6: Badge dans README ===

# Ajoutez un badge de statut dans votre README.md:
[![CI](https://github.com/username/repo/actions/workflows/ci.yml/badge.svg)](https://github.com/username/repo/actions/workflows/ci.yml)

# Résultat: Un badge [OK] ou [X] visible sur votre README


# === ASTUCE 7: Re-run failed jobs ===

# Sur GitHub Actions, après un échec:
# 1. Cliquez sur "Re-run jobs"
# 2. Choisissez "Re-run failed jobs"
# = Relance seulement ce qui a échoué (économise du temps)


# === ASTUCE 8: Matrix pour tester rapidement ===

# Développement: teste seulement Python 3.11
# Production: teste toutes les versions

strategy:
  matrix:
    python-version: ${{ github.event_name == 'push' && fromJSON('["3.9", "3.10", "3.11", "3.12"]') || fromJSON('["3.11"]') }}

# Résultat:
# - Push -> 4 versions testées
# - PR -> 1 version testée (plus rapide pour feedback)


# === ASTUCE 9: Commenter automatiquement les PRs ===

- name: Comment PR
  uses: actions/github-script@v7
  if: github.event_name == 'pull_request'
  with:
    script: |
      github.rest.issues.createComment({
        issue_number: context.issue.number,
        owner: context.repo.owner,
        repo: context.repo.repo,
        body: '[OK] Tests passed! Ready to review.'
      })


# === ASTUCE 10: Notification en cas d'échec ===

- name: Notify on failure
  if: failure()
  run: |
    curl -X POST ${{ secrets.SLACK_WEBHOOK }} \
      -H 'Content-Type: application/json' \
      -d '{
        "text": "[X] Build failed on ${{ github.ref }}",
        "url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
      }'


[OK] GUIDE VISUEL - COMPRENDRE LE FLUX


# === FLUX TYPIQUE D'UN WORKFLOW ===

# 1. DÉCLENCHEUR (Event)
#    └─ Vous faites: git push
#    └─ GitHub détecte: "Il y a un push sur main!"

# 2. WORKFLOW DÉMARRE
#    └─ GitHub lit: .github/workflows/ci.yml
#    └─ Vérifie: "Ce workflow écoute push sur main"
#    └─ Lance: Le workflow!

# 3. JOBS S'EXÉCUTENT
#    ├─ Job "test" démarre sur ubuntu-latest
#    ├─ Job "lint" démarre EN PARALLÈLE sur ubuntu-latest
#    └─ Job "deploy" ATTEND que test + lint finissent

# 4. STEPS DANS CHAQUE JOB (séquentiellement)
#    Job "test":
#    ├─ Step 1: Checkout code [OK]
#    ├─ Step 2: Setup Python [OK]
#    ├─ Step 3: Install deps [OK]
#    └─ Step 4: Run tests [OK]

# 5. RÉSULTATS
#    ├─ test: [OK] Success
#    ├─ lint: [OK] Success
#    └─ deploy: [OK] Success (car needs: [test, lint] OK)

# 6. NOTIFICATION
#    └─ GitHub vous notifie: Email, mobile, etc.


# === EXEMPLE VISUEL D'EXÉCUTION ===

# Workflow "CI" lancé à 14:30:00

# 14:30:00  ┌─ Job: test (ubuntu-latest)
#           │  ├─ Checkout code              [2s]  [OK]
#           │  ├─ Setup Python 3.11          [5s]  [OK]
#           │  ├─ Install dependencies       [30s] [OK]
#           │  └─ Run tests                  [45s] [OK]
#           │  Total: 1m 22s
#           
# 14:30:00  ┌─ Job: lint (ubuntu-latest) [EN PARALLÈLE]
#           │  ├─ Checkout code              [2s]  [OK]
#           │  ├─ Setup Python 3.11          [5s]  [OK]
#           │  ├─ Install flake8             [3s]  [OK]
#           │  └─ Run linting                [8s]  [OK]
#           │  Total: 18s
#           
# 14:31:22  └─ Job: deploy (ubuntu-latest) [APRÈS test + lint]
#              ├─ Checkout code              [2s]  [OK]
#              └─ Deploy to server           [15s] [OK]
#              Total: 17s

# Temps total: 1m 39s
# (test et lint en parallèle, deploy ensuite)


[OK] CHECKLIST AVANT DE COMMENCER


# === PRÉREQUIS ===

# [OK] Vous avez un compte GitHub
# [OK] Vous avez un repository (public ou privé)
# [OK] Vous savez utiliser git (add, commit, push)
# [OK] Vous avez un fichier requirements.txt ou similar


# === CHECKLIST PREMIER WORKFLOW ===

# 1. Créer le dossier
mkdir -p .github/workflows

# 2. Créer le fichier workflow
touch .github/workflows/test.yml

# 3. Écrire le workflow minimal:
name: Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'
      - run: pip install -r requirements.txt
      - run: pytest

# 4. Commit et push
git add .github/workflows/test.yml
git commit -m "Add GitHub Actions workflow"
git push

# 5. Vérifier sur GitHub
# GitHub -> Actions -> Voir le workflow s'exécuter!

# 6. Débugger si erreurs
# Cliquez sur le step rouge pour voir l'erreur


# === CHECKLIST WORKFLOW PRODUCTION ===

# Pour un workflow complet et robuste:

# [OK] Checkout en premier
- uses: actions/checkout@v4

# [OK] Cache activé
cache: 'pip'

# [OK] Tests sur plusieurs versions Python
strategy:
  matrix:
    python-version: ['3.9', '3.10', '3.11']

# [OK] Linting et formatage
- run: flake8 .
- run: black --check .

# [OK] Type checking
- run: mypy src/

# [OK] Coverage
- run: pytest --cov=src --cov-report=xml

# [OK] Timeout défini
timeout-minutes: 30

# [OK] Conditions pour déploiement
if: github.ref == 'refs/heads/main'

# [OK] Secrets configurés pour déploiement
env:
  API_KEY: ${{ secrets.API_KEY }}


[OK] LEXIQUE - TERMES IMPORTANTS


# WORKFLOW
# = Un fichier YAML qui définit l'automatisation
# Exemple: .github/workflows/ci.yml

# EVENT (Événement)
# = Ce qui déclenche le workflow
# Exemples: push, pull_request, schedule

# JOB
# = Une tâche à accomplir (groupe de steps)
# Exemples: job "test", job "deploy"
# Jobs s'exécutent EN PARALLÈLE par défaut

# STEP
# = Une action individuelle dans un job
# Exemples: installer Python, lancer tests
# Steps s'exécutent SÉQUENTIELLEMENT

# RUNNER
# = La machine qui exécute votre workflow
# Exemples: ubuntu-latest, windows-latest, macos-latest

# ACTION
# = Code réutilisable (comme une fonction)
# Exemples: actions/checkout, actions/setup-python

# ARTIFACT
# = Fichier généré par un workflow
# Exemples: build, rapport de tests, logs

# CACHE
# = Sauvegarde de fichiers pour accélérer
# Exemples: cache pip, cache npm

# SECRET
# = Donnée sensible cryptée
# Exemples: mots de passe, tokens, clés API

# MATRIX
# = Exécuter le même job avec différentes configs
# Exemple: tester avec Python 3.9, 3.10, 3.11

# CONCURRENCY
# = Gérer l'exécution simultanée de workflows
# Exemple: annuler workflows précédents


[OK] COMMANDES GITHUB CLI UTILES


# === INSTALLER GITHUB CLI ===

# macOS
brew install gh

# Ubuntu/Debian
sudo apt install gh

# Windows
winget install GitHub.cli

# Authentification
gh auth login


# === GÉRER LES WORKFLOWS ===

# Lister les workflows
gh workflow list

# Lancer un workflow manuellement
gh workflow run ci.yml

# Lancer avec inputs
gh workflow run deploy.yml \
  -f environment=production \
  -f version=1.2.3

# Voir l'état des workflows
gh workflow view ci.yml

# Activer/désactiver un workflow
gh workflow enable ci.yml
gh workflow disable ci.yml


# === GÉRER LES RUNS ===

# Lister les runs récents
gh run list

# Lister les runs d'un workflow
gh run list --workflow=ci.yml

# Voir les runs qui ont échoué
gh run list --status=failure

# Voir les détails d'un run
gh run view 123456789

# Voir les logs
gh run view 123456789 --log

# Voir seulement les logs des steps échoués
gh run view 123456789 --log-failed

# Relancer un workflow
gh run rerun 123456789

# Relancer seulement les jobs échoués
gh run rerun 123456789 --failed

# Annuler un run en cours
gh run cancel 123456789

# Télécharger les artifacts
gh run download 123456789

# Télécharger un artifact spécifique
gh run download 123456789 -n artifact-name

# Regarder un workflow en temps réel
gh run watch


# === EXEMPLES PRATIQUES ===

# Lancer tests manuellement
gh workflow run test.yml

# Déployer en production
gh workflow run deploy.yml -f environment=production

# Voir les 10 derniers runs
gh run list --limit 10

# Voir tous les runs en cours
gh run list --status=in_progress

# Relancer le dernier workflow échoué
gh run list --status=failure --limit 1 --json databaseId --jq '.[0].databaseId' | xargs gh run rerun


[OK] QUESTIONS FRÉQUENTES (FAQ)


# Q: Combien ça coûte?
# R: GRATUIT pour les repos publics!
#    Pour repos privés: 2000 min/mois gratuit (Free plan)
#    Linux = 1x, Windows = 2x, macOS = 10x


# Q: Puis-je exécuter des workflows localement?
# R: Oui, avec "act": https://github.com/nektos/act
#    Installation: brew install act
#    Usage: act ou act push


# Q: Comment débugger un workflow?
# R: 1. Regardez les logs sur GitHub -> Actions
#    2. Ajoutez des echo pour voir les valeurs
#    3. Activez ACTIONS_STEP_DEBUG=true dans secrets
#    4. Utilisez act pour tester localement


# Q: Mon workflow ne se déclenche pas, pourquoi?
# R: Vérifiez:
#    - Fichier dans .github/workflows/ ?
#    - Extension .yml ou .yaml ?
#    - Syntaxe YAML valide ? (testez sur yamllint.com)
#    - Branche/événement configuré correctement ?


# Q: Puis-je utiliser plusieurs versions de Python?
# R: Oui! Utilisez une matrice:
#    strategy:
#      matrix:
#        python-version: ['3.9', '3.10', '3.11', '3.12']


# Q: Comment accélérer mes workflows?
# R: 1. Utilisez cache: 'pip' dans setup-python
#    2. Exécutez jobs en parallèle
#    3. Ajoutez concurrency pour annuler vieux runs
#    4. Utilisez paths-ignore pour fichiers inutiles


# Q: Puis-je avoir plusieurs workflows?
# R: Oui! Créez plusieurs fichiers .yml:
#    - .github/workflows/test.yml
#    - .github/workflows/deploy.yml
#    - .github/workflows/cron.yml


# Q: Comment partager des données entre jobs?
# R: Utilisez artifacts:
#    Job 1: upload-artifact
#    Job 2: download-artifact


# Q: Puis-je utiliser Docker?
# R: Oui! De plusieurs façons:
#    - Container pour job: container: image: python:3.11
#    - Services: services: postgres: image: postgres:15
#    - Run Docker: run: docker build -t myapp .


# Q: Comment stocker des secrets?
# R: Settings -> Secrets and variables -> Actions
#    Puis utilisez: ${{ secrets.SECRET_NAME }}


# Q: Puis-je déclencher un workflow manuellement?
# R: Oui! Ajoutez: on: workflow_dispatch
#    Puis: GitHub -> Actions -> Run workflow


# Q: Comment déployer automatiquement?
# R: 1. Ajoutez job "deploy" avec needs: [test]
#    2. Ajoutez if: github.ref == 'refs/heads/main'
#    3. Configurez vos secrets (SSH keys, tokens...)
#    4. Écrivez script de déploiement


# Q: Workflow trop long, que faire?
# R: 1. Vérifiez que cache est activé
#    2. Parallélisez les jobs
#    3. Réduisez les tests sur PR (matrix conditionnelle)
#    4. Utilisez timeout-minutes
#    5. Considérez self-hosted runner


# Q: "Permission denied" sur déploiement?
# R: Vérifiez:
#    - permissions: au niveau workflow
#    - Script exécutable: chmod +x script.sh
#    - SSH key correct dans secrets


# Q: Comment tester avec une base de données?
# R: Utilisez services:
#    services:
#      postgres:
#        image: postgres:15
#        env:
#          POSTGRES_PASSWORD: postgres


# Q: Puis-je avoir des jobs qui dépendent d'autres?
# R: Oui! Utilisez needs:
#    deploy:
#      needs: [test, lint]  # Attend test ET lint


# Q: Comment ignorer certains commits?
# R: Ajoutez [skip ci] dans le message de commit:
#    git commit -m "Update docs [skip ci]"


# Q: Workflow échoue mais tests passent localement?
# R: Causes courantes:
#    - requirements.txt pas à jour
#    - Variables d'environnement différentes
#    - Base de données manquante
#    - Fichiers non commités


# Q: Comment voir les fichiers générés?
# R: 1. Ajoutez step: run: ls -la
#    2. Ou uploadez avec upload-artifact
#    3. Téléchargez depuis GitHub -> Actions


# Q: Puis-je réutiliser des workflows?
# R: Oui! Créez un workflow avec:
#    on: workflow_call
#    Puis appelez-le depuis un autre workflow


# Q: Comment notifier Slack/Discord?
# R: Utilisez actions du marketplace:
#    - uses: 8398a7/action-slack@v3
#    - uses: sarisia/actions-status-discord@v1


# Q: Différence entre 'run' et 'uses'?
# R: - run: Exécute une commande shell
#    - uses: Utilise une action pré-faite


# Q: Que signifie '@v4' dans actions/checkout@v4?
# R: C'est la version de l'action
#    Toujours utiliser une version (pas @main)


# Q: Cache ne fonctionne pas?
# R: Vérifiez:
#    - Key unique et stable
#    - Path correct
#    - restore-keys configuré
#    - Cache pas expiré (7 jours max)


[OK] RESSOURCES POUR ALLER PLUS LOIN


# === DOCUMENTATION OFFICIELLE ===
# https://docs.github.com/en/actions
# La référence complète, avec exemples


# === MARKETPLACE D'ACTIONS ===
# https://github.com/marketplace?type=actions
# Milliers d'actions réutilisables


# === AWESOME GITHUB ACTIONS ===
# https://github.com/sdras/awesome-actions
# Liste communautaire des meilleures actions


# === APPRENDRE PAR L'EXEMPLE ===
# 1. Trouvez un projet similaire au vôtre
# 2. Regardez leur .github/workflows/
# 3. Inspirez-vous de leur configuration


# === COMMUNAUTÉ ===
# GitHub Community Forum:
# https://github.community/c/github-actions/

# Stack Overflow:
# https://stackoverflow.com/questions/tagged/github-actions


# === OUTILS UTILES ===

# act - Tester localement
# https://github.com/nektos/act

# YAML validator
# https://www.yamllint.com/

# GitHub CLI
# https://cli.github.com/

# VS Code Extension
# GitHub Actions (par GitHub)


# === BLOGS ET TUTORIELS ===

# GitHub Blog
# https://github.blog/tag/github-actions/

# Real Python
# https://realpython.com/

# Dev.to
# https://dev.to/t/githubactions


[OK] TEMPLATE DE DÉMARRAGE RAPIDE


# === COPIEZ-COLLEZ CE TEMPLATE ===

# Fichier: .github/workflows/ci.yml

name: CI

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

jobs:
  test:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'
      
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
      
      - name: Run linting
        run: |
          pip install flake8
          flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
      
      - name: Run tests
        run: |
          pip install pytest
          pytest -v

# Ensuite:
# 1. Adaptez python-version à votre projet
# 2. Ajoutez vos dépendances dans requirements.txt
# 3. Ajoutez vos tests
# 4. Commit et push
# 5. Regardez sur GitHub -> Actions !


# === FIN DE LA CHEATSHEET - VOUS ÊTES PRÊT! ===

# Prochaines étapes recommandées:
# 1. Créez votre premier workflow avec le template ci-dessus
# 2. Testez-le sur GitHub
# 3. Ajoutez progressivement plus de fonctionnalités
# 4. Consultez la doc officielle pour cas spécifiques
# 5. Rejoignez la communauté GitHub Actions

# Bonne automatisation! [RAPIDE]:
on:
  schedule:
    - cron: '0 9 * * 1'      # Tous les lundis à 9h UTC
    - cron: '*/15 * * * *'   # Toutes les 15 minutes
    - cron: '0 0 * * 0'      # Tous les dimanches à minuit
    - cron: '30 14 * * *'    # Tous les jours à 14h30 UTC

# [ATTENTION] ATTENTION: 
# - L'heure est en UTC, pas votre heure locale!
# - Conversion: UTC = GMT (Paris = UTC+1 ou UTC+2)
# - 9h à Paris = 8h ou 7h UTC selon saison

# Cas d'usage:
# - Sauvegardes régulières
# - Nettoyage de données
# - Rapports quotidiens/hebdomadaires
# - Tests de nuit


# === WORKFLOW_DISPATCH - DÉCLENCHEMENT MANUEL ===

# Simple: bouton "Run workflow" dans GitHub
on: workflow_dispatch

# Explication:
# Vous allez sur GitHub > Actions > Votre workflow
# Et vous voyez un bouton "Run workflow"
# Clic = ça lance le workflow manuellement

# Avec paramètres (inputs):
on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Environnement à déployer'
        required: true
        type: choice
        options:
          - staging
          - production
      
      version:
        description: 'Numéro de version'
        required: false
        type: string
        default: 'latest'

# Explication détaillée:
# - description: Texte affiché dans l'interface
# - required: Si obligatoire ou optionnel
# - type: Type de donnée (string, boolean, choice)
# - options: Liste de choix (pour type: choice)
# - default: Valeur par défaut

# Comment l'utiliser dans le workflow:
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to ${{ inputs.environment }}
        run: |
          echo "Deploying version ${{ inputs.version }}"
          echo "To environment ${{ inputs.environment }}"

# Interface dans GitHub:
# Vous voyez un formulaire avec:
# - Un menu déroulant (staging/production)
# - Un champ texte (version)
# Vous remplissez et cliquez "Run workflow"


# === MULTIPLES ÉVÉNEMENTS ===

# Combiner plusieurs déclencheurs
on:
  push:
    branches: [main]      # Push sur main
  pull_request:
    branches: [main]      # PR vers main
  schedule:
    - cron: '0 0 * * *'   # Quotidien
  workflow_dispatch:      # Manuel

# Cas d'usage:
# - Tests sur push ET PR
# - Tests nocturnes en plus
# - Possibilité de lancer manuellement


# === EXEMPLES PRATIQUES ===

# Exemple 1: CI Classique
name: CI

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

# Explication: Tests automatiques sur:
# - Chaque push vers main ou develop
# - Chaque PR vers main

# Exemple 2: Déploiement Production
name: Deploy

on:
  push:
    branches: [main]
    paths-ignore:
      - 'docs/**'
      - '**.md'

# Explication: Déploie uniquement si:
# - Push sur main
# - ET fichiers de code modifiés (pas juste la doc)

# Exemple 3: Tests Nightly
name: Nightly Tests

on:
  schedule:
    - cron: '0 2 * * *'   # 2h du matin UTC
  workflow_dispatch:      # + manuel si besoin

# Explication: Tests complets chaque nuit
# Avec possibilité de lancer manuellement


[OK] RUNNERS - LES MACHINES QUI EXÉCUTENT VOTRE CODE


# === CONCEPT ===
# Un runner = une machine virtuelle (VM) qui exécute votre workflow
# C'est comme avoir un ordinateur distant qui fait le travail pour vous

# GitHub fournit des runners GRATUITEMENT:
# - Linux (Ubuntu)
# - Windows
# - macOS

# Chaque runner a Python, Node.js, Docker, Git... déjà installés!


# === RUNNERS HÉBERGÉS PAR GITHUB ===

# Ubuntu (Linux) - LE PLUS UTILISÉ
runs-on: ubuntu-latest
# Pourquoi? Gratuit, rapide, bien supporté
# Équivalent à: Ubuntu 22.04 actuellement

# Versions spécifiques
runs-on: ubuntu-22.04    # Ubuntu 22.04
runs-on: ubuntu-20.04    # Ubuntu 20.04

# Windows
runs-on: windows-latest
# Utilisez si: Tests Windows, .NET, PowerShell
# [ATTENTION] Compte 2x plus de minutes!

# macOS
runs-on: macos-latest
# Utilisez si: Apps iOS/macOS, tests Safari
# [ATTENTION] Compte 10x plus de minutes! Très cher!

# macOS avec différentes puces
runs-on: macos-13        # Intel
runs-on: macos-14        # Apple Silicon (M1)


# === CHOIX DU RUNNER - GUIDE DÉCISION ===

# UTILISEZ UBUNTU si:
# [OK] Projet Python/Node.js/Java standard
# [OK] Besoin de Docker
# [OK] Serveur web/API
# [OK] 99% des cas! C'est le default recommandé

# UTILISEZ WINDOWS si:
# [OK] Projet .NET/C#
# [OK] Tests d'apps Windows
# [OK] PowerShell spécifique

# UTILISEZ MACOS si:
# [OK] Apps iOS/macOS
# [OK] Tests Safari
# [OK] Sinon évitez (cher!)


# === EXEMPLE PRATIQUE - PROJET PYTHON ===

jobs:
  test:
    runs-on: ubuntu-latest
    # ^ Ubuntu suffit pour 99% des projets Python
    
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - run: pip install -r requirements.txt
      - run: pytest


# === TESTER SUR PLUSIEURS OS (MATRICE) ===

# Si vous voulez être sûr que ça marche partout:
jobs:
  test:
    runs-on: ${{ matrix.os }}
    # ^ Variable dynamique depuis la matrice
    
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        # ^ Liste des OS à tester
    
    steps:
      - uses: actions/checkout@v4
      - name: Run tests
        run: pytest

# Ce que ça fait:
# GitHub crée 3 jobs en parallèle:
# 1. test sur ubuntu-latest
# 2. test sur windows-latest  
# 3. test sur macos-latest

# Résultat: Vous êtes sûr que ça marche sur Linux/Windows/Mac!
# [ATTENTION] Attention au coût si repo privé (Windows 2x, macOS 10x)


# === CE QUI EST PRÉ-INSTALLÉ ===

# Sur tous les runners:
# - Git
# - curl, wget
# - zip, unzip
# - Docker (sauf macOS)
# - Node.js (plusieurs versions)
# - Python (plusieurs versions)
# - Ruby, Go, Java...

# Voir liste complète:
# https://github.com/actions/runner-images

# Exemples d'utilisation:
- name: Check versions
  run: |
    python --version
    node --version
    git --version
    docker --version


# === SELF-HOSTED RUNNERS (AVANCÉ) ===

# Si vous avez votre propre serveur:
runs-on: self-hosted

# Pourquoi?
# - Accès à ressources internes
# - GPU pour ML
# - Pas de limite de temps
# - Gratuit (vous payez votre serveur)

# Configuration:
# Voir section "Self-hosted runners" plus bas


[OK] JOBS - ORGANISER VOTRE WORKFLOW


# === CONCEPT ===
# Un job = une séquence d'étapes qui s'exécutent ensemble
# Plusieurs jobs = exécution EN PARALLÈLE par défaut

# Pensez à un job comme une "tâche" à accomplir
# Exemples: job "test", job "build", job "deploy"


# === JOB SIMPLE ===

jobs:
  mon-job:                    # Nom du job (votre choix)
    runs-on: ubuntu-latest    # Machine à utiliser
    steps:                    # Étapes à exécuter
      - name: Step 1
        run: echo "Hello"
      - name: Step 2
        run: echo "World"

# Ce job:
# 1. Démarre une machine Ubuntu
# 2. Exécute Step 1
# 3. Exécute Step 2
# 4. Se termine


# === PLUSIEURS JOBS EN PARALLÈLE ===

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Build app
        run: echo "Building..."
  
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Run tests
        run: echo "Testing..."
  
  lint:
    runs-on: ubuntu-latest
    steps:
      - name: Check style
        run: echo "Linting..."

# Ce que GitHub fait:
# Lance 3 machines EN MÊME TEMPS
# - Une pour build
# - Une pour test
# - Une pour lint
# = Plus rapide qu'en séquence!


# === JOBS DÉPENDANTS (needs) ===

# Souvent, vous voulez un ORDRE:
# 1. D'abord build
# 2. Puis tests
# 3. Enfin deploy

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: echo "Building..."
  
  test:
    needs: build              # <- Attend que build soit fini
    runs-on: ubuntu-latest
    steps:
      - run: echo "Testing..."
  
  deploy:
    needs: [build, test]      # <- Attend build ET test
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploying..."

# Ordre d'exécution:
# 1. build s'exécute
# 2. test attend que build finisse, puis s'exécute
# 3. deploy attend que build ET test finissent, puis s'exécute


# === EXEMPLE RÉEL - PROJET PYTHON ===

name: Python CI/CD

on: push

jobs:
  # JOB 1: Vérifier le style
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - run: pip install flake8
      - run: flake8 .
  
  # JOB 2: Exécuter les tests
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - run: pip install -r requirements.txt
      - run: pytest
  
  # JOB 3: Déployer (seulement si lint + test OK)
  deploy:
    needs: [lint, test]       # Attend les 2 jobs
    if: github.ref == 'refs/heads/main'  # Seulement sur main
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploying to production..."

# Comportement:
# - lint et test s'exécutent EN PARALLÈLE
# - deploy attend que les 2 soient OK
# - deploy s'exécute SEULEMENT sur branche main


# === CONDITIONS (if) ===

# Exécuter un job conditionnellement:
jobs:
  deploy:
    if: github.ref == 'refs/heads/main'
    # ^ S'exécute SEULEMENT si on est sur main
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploying..."

# Autres exemples de conditions:
if: github.event_name == 'push'              # Seulement sur push
if: github.event_name == 'pull_request'      # Seulement sur PR
if: contains(github.ref, 'release')          # Si branche contient "release"
if: startsWith(github.ref, 'refs/tags/')     # Si c'est un tag


# === TIMEOUT ===

# Définir un temps maximum:
jobs:
  test:
    runs-on: ubuntu-latest
    timeout-minutes: 10       # <- Max 10 minutes
    # Si dépassé: job annulé automatiquement
    steps:
      - run: pytest

# Pourquoi?
# - Éviter qu'un job bloqué consomme des minutes
# - Détecter les tests qui "hangent"
# - Default: 360 minutes (6 heures)


# === MATRICE - TESTER PLUSIEURS VERSIONS ===

# Concept: Tester avec Python 3.9, 3.10, 3.11, 3.12
# Sans matrice: Copier-coller 4 fois le job [TIRED_FACE]
# Avec matrice: Un seul job, GitHub fait 4 copies! [BRAVO]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ['3.9', '3.10', '3.11', '3.12']
        # ^ GitHub crée 4 jobs automatiquement
    
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          # ^ Utilise la version de la matrice
      - run: pip install -r requirements.txt
      - run: pytest

# Résultat dans GitHub:
# [OK] test (3.9)
# [OK] test (3.10)
# [OK] test (3.11)
# [OK] test (3.12)
# = 4 jobs exécutés en parallèle!


# === MATRICE MULTI-DIMENSIONS ===

# Tester plusieurs Python + plusieurs OS:
strategy:
  matrix:
    python-version: ['3.10', '3.11', '3.12']
    os: [ubuntu-latest, windows-latest]

# Combinaisons créées automatiquement:
# 1. Python 3.10 sur Ubuntu
# 2. Python 3.10 sur Windows
# 3. Python 3.11 sur Ubuntu
# 4. Python 3.11 sur Windows
# 5. Python 3.12 sur Ubuntu
# 6. Python 3.12 sur Windows
# = 6 jobs au total (3 × 2)


# === SERVICES (BASES DE DONNÉES) ===

# Si vos tests ont besoin d'une base de données:
jobs:
  test:
    runs-on: ubuntu-latest
    
    services:
      postgres:
        image: postgres:15        # Image Docker à utiliser
        env:
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: testdb
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432             # Expose le port
    
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - run: pip install -r requirements.txt
      - run: pytest
        env:
          DATABASE_URL: postgresql://postgres:postgres@localhost:5432/testdb

# Ce que ça fait:
# 1. GitHub démarre un container PostgreSQL
# 2. Attend qu'il soit prêt (health checks)
# 3. Votre code peut s'y connecter sur localhost:5432
# 4. À la fin: container automatiquement supprimé

# Autres services courants:
services:
  redis:
    image: redis:7
    ports:
      - 6379:6379
  
  mysql:
    image: mysql:8
    env:
      MYSQL_ROOT_PASSWORD: root
    ports:
      - 3306:3306


[OK] STEPS - LES ACTIONS À EXÉCUTER


# === CONCEPT ===
# Un step = une action individuelle dans un job
# Steps s'exécutent DANS L'ORDRE, l'un après l'autre

# Deux types de steps:
# 1. run: Exécuter une commande shell
# 2. uses: Utiliser une action pré-faite


# === RUN - EXÉCUTER DES COMMANDES ===

# Commande simple:
- name: Say hello
  run: echo "Hello World"

# Équivalent à taper dans votre terminal:
# $ echo "Hello World"

# Plusieurs commandes (avec |):
- name: Install and test
  run: |
    pip install pytest
    pytest tests/
    echo "Tests done!"

# Le | permet d'écrire plusieurs lignes
# Chaque ligne = une commande

# Avec répertoire spécifique:
- name: Install frontend
  working-directory: ./frontend
  run: npm install

# Équivalent à:
# $ cd frontend
# $ npm install


# === USES - UTILISER DES ACTIONS ===

# Concept: Réutiliser du code déjà écrit
# Au lieu de réécrire 20 lignes, on utilise une action!

# Exemple 1: Télécharger votre code
- name: Checkout repository
  uses: actions/checkout@v4

# Pourquoi "checkout"?
# GitHub ne télécharge PAS votre code automatiquement!
# Cette action le fait pour vous
# [ATTENTION] À mettre en PREMIER dans presque tous les workflows

# Exemple 2: Installer Python
- name: Setup Python
  uses: actions/setup-python@v5
  with:
    python-version: '3.11'

# Ce que ça fait:
# - Installe Python 3.11
# - Configure pip
# - Ajoute python au PATH

# Exemple 3: Cache (accélérer les installations)
- name: Cache pip packages
  uses: actions/cache@v4
  with:
    path: ~/.cache/pip
    key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}

# Ce que ça fait:
# - Sauvegarde ~/.cache/pip après premier run
# - Réutilise ce cache si requirements.txt n'a pas changé
# - = Installation beaucoup plus rapide!


# === STRUCTURE D'UN STEP ===

- name: Nom descriptif du step
  # ^ Optionnel mais FORTEMENT recommandé
  # Affiché dans les logs GitHub
  
  run: echo "Hello"
  # ^ OU uses: actions/...
  # (run et uses sont mutuellement exclusifs)
  
  env:
    MY_VAR: value
    # ^ Variables d'environnement pour ce step
  
  working-directory: ./src
  # ^ Répertoire d'exécution
  
  continue-on-error: true
  # ^ Continue même si ce step échoue
  
  timeout-minutes: 5
  # ^ Timeout max pour ce step


# === EXEMPLE COMPLET - WORKFLOW PYTHON ===

name: Python CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      # STEP 1: Télécharger le code
      - name: Checkout code
        uses: actions/checkout@v4
        # [ATTENTION] TOUJOURS en premier!
      
      # STEP 2: Installer Python
      - name: Setup Python 3.11
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'        # Active le cache pip automatiquement
      
      # STEP 3: Installer les dépendances
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
          pip install -r requirements-dev.txt
      
      # STEP 4: Linting
      - name: Lint with flake8
        run: |
          flake8 src/ tests/
      
      # STEP 5: Type checking
      - name: Type check with mypy
        run: mypy src/
      
      # STEP 6: Run tests
      - name: Test with pytest
        run: |
          pytest --cov=src --cov-report=xml
      
      # STEP 7: Upload coverage
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v4
        with:
          file: ./coverage.xml

# Ordre d'exécution:
# 1. Télécharge votre code
# 2. Installe Python
# 3. Installe vos dépendances
# 4. Vérifie le style (flake8)
# 5. Vérifie les types (mypy)
# 6. Exécute les tests
# 7. Envoie le rapport de couverture


# === CONDITIONS SUR LES STEPS ===

# Exécuter un step conditionnellement:
- name: Deploy to production
  if: github.ref == 'refs/heads/main'
  run: ./deploy.sh

# Seulement si step précédent a réussi (défaut):
- name: Notify success
  if: success()
  run: echo "Everything OK!"

# Seulement si step précédent a échoué:
- name: Notify failure
  if: failure()
  run: echo "Something failed!"

# Toujours exécuter (même si échec):
- name: Cleanup
  if: always()
  run: rm -rf temp/

# Exemples pratiques

on:
  push:
    branches:
      - main                          # Seulement branche main
      - 'releases/**'                 # Pattern de branches
    branches-ignore:
      - dev                           # Exclure branche
    paths:
      - '**.py'                       # Seulement fichiers Python
      - 'src/**'                      # Dossier spécifique
    paths-ignore:
      - 'docs/**'                     # Ignorer dossier
    tags:
      - v*                            # Tous les tags v*
      - v1.*.*                        # Tags v1.x.x


# === Pull Request ===
on: pull_request

on:
  pull_request:
    types:
      - opened                        # PR ouverte
      - synchronize                   # Nouveaux commits
      - reopened                      # PR réouverte
      - closed                        # PR fermée
      - assigned                      # Assignation
      - labeled                       # Label ajouté
      - review_requested              # Review demandée
    branches:
      - main
      - develop


# === Schedule (Cron) ===
on:
  schedule:
    - cron: '0 0 * * *'               # Tous les jours à minuit UTC
    - cron: '*/15 * * * *'            # Toutes les 15 minutes
    - cron: '0 9 * * 1'               # Lundis à 9h UTC

# Format cron: minute heure jour mois jour_semaine
# * * * * *
# │ │ │ │ │
# │ │ │ │ └─── jour semaine (0-6, 0=dimanche)
# │ │ │ └───── mois (1-12)
# │ │ └─────── jour mois (1-31)
# │ └───────── heure (0-23)
# └─────────── minute (0-59)


# === Workflow Dispatch (Manuel) ===
on:
  workflow_dispatch:                  # Déclenchement manuel
    inputs:
      environment:
        description: 'Environment to deploy'
        required: true
        default: 'staging'
        type: choice
        options:
          - staging
          - production
      version:
        description: 'Version number'
        required: false
        type: string
      debug:
        description: 'Enable debug mode'
        required: false
        type: boolean
        default: false


# === Issues ===
on:
  issues:
    types:
      - opened
      - closed
      - labeled
      - assigned


# === Release ===
on:
  release:
    types:
      - published
      - created
      - edited


# === Repository Dispatch (API) ===
on:
  repository_dispatch:
    types:
      - webhook                       # Type personnalisé


# === Workflow Call (Réutilisable) ===
on:
  workflow_call:
    inputs:
      config-path:
        required: true
        type: string
    secrets:
      token:
        required: true


# === Multiples événements ===
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 0 * * 0'               # Dimanche minuit
  workflow_dispatch:


# === Événements moins courants ===
on:
  fork:                               # Repo forké
  watch:                              # Repo starred
  create:                             # Branche/tag créé
  delete:                             # Branche/tag supprimé
  gollum:                             # Wiki modifié
  issue_comment:                      # Commentaire issue
  pull_request_review:                # Review PR
  deployment:                         # Déploiement
  status:                             # Status check
  check_run:                          # Check run
  check_suite:                        # Check suite
  page_build:                         # GitHub Pages build


[OK] RUNNERS


# === Runners hébergés par GitHub ===

runs-on: ubuntu-latest                # Ubuntu (recommandé)
runs-on: ubuntu-22.04                 # Version spécifique
runs-on: ubuntu-20.04

runs-on: windows-latest               # Windows
runs-on: windows-2022
runs-on: windows-2019

runs-on: macos-latest                 # macOS
runs-on: macos-13                     # macOS 13 (Intel)
runs-on: macos-14                     # macOS 14 (ARM64 M1)
runs-on: macos-12

# Images disponibles:
# https://github.com/actions/runner-images


# === Matrice de runners ===
jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]


# === Self-hosted runners ===
runs-on: self-hosted

runs-on: [self-hosted, linux, x64]   # Avec labels
runs-on: [self-hosted, ARM64]


# === Runner groups (Enterprise) ===
runs-on:
  group: production-runners


[OK] JOBS


# === Job simple ===
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: echo "Building..."


# === Jobs multiples ===
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: npm run build
  
  test:
    runs-on: ubuntu-latest
    steps:
      - run: npm test


# === Dépendances entre jobs ===
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: npm run build
  
  test:
    needs: build                      # Attend build
    runs-on: ubuntu-latest
    steps:
      - run: npm test
  
  deploy:
    needs: [build, test]              # Attend plusieurs jobs
    runs-on: ubuntu-latest
    steps:
      - run: npm run deploy


# === Conditions ===
jobs:
  deploy:
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - run: deploy.sh


# === Timeout ===
jobs:
  build:
    runs-on: ubuntu-latest
    timeout-minutes: 30               # Timeout du job (défaut: 360)
    steps:
      - run: build.sh


# === Stratégie de matrice ===
jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: [3.8, 3.9, '3.10', 3.11, 3.12]
        os: [ubuntu-latest, windows-latest]
    steps:
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}


# === Matrice avec include/exclude ===
strategy:
  matrix:
    os: [ubuntu-latest, windows-latest, macos-latest]
    node: [14, 16, 18]
    exclude:
      - os: macos-latest              # Exclure combinaison
        node: 14
    include:
      - os: ubuntu-latest             # Ajouter combinaison
        node: 19
        experimental: true


# === Fail-fast ===
strategy:
  fail-fast: false                    # Continue si un job échoue
  matrix:
    version: [1, 2, 3]


# === Max parallel ===
strategy:
  max-parallel: 2                     # Max 2 jobs en parallèle
  matrix:
    version: [1, 2, 3, 4, 5]


# === Environnement du job ===
jobs:
  deploy:
    runs-on: ubuntu-latest
    environment:
      name: production                # Environnement
      url: https://example.com        # URL de déploiement


# === Conteneur ===
jobs:
  test:
    runs-on: ubuntu-latest
    container:
      image: node:18
      env:
        NODE_ENV: test
      ports:
        - 80
      volumes:
        - my_volume:/volume_mount
      options: --cpus 1


# === Services (databases, etc.) ===
jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_PASSWORD: postgres
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432
      
      redis:
        image: redis:7
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 6379:6379


# === Outputs ===
jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.version.outputs.value }}
    steps:
      - id: version
        run: echo "value=1.0.0" >> $GITHUB_OUTPUT
  
  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploying version ${{ needs.build.outputs.version }}"


[OK] STEPS


# === Run command ===
steps:
  - name: Simple command
    run: echo "Hello World"

  - name: Multiple commands
    run: |
      echo "Line 1"
      echo "Line 2"
      npm install

  - name: Working directory
    run: npm install
    working-directory: ./frontend


# === Shell ===
steps:
  - name: Bash (default Linux/macOS)
    run: echo $HOME
    shell: bash

  - name: PowerShell (default Windows)
    run: Write-Host $env:HOME
    shell: pwsh

  - name: Python
    run: |
      import os
      print(os.environ['HOME'])
    shell: python

  - name: Node
    run: console.log(process.env.HOME)
    shell: node {0}


# === Uses (actions) ===
steps:
  - name: Checkout code
    uses: actions/checkout@v4

  - name: Setup Python
    uses: actions/setup-python@v5
    with:
      python-version: '3.11'

  - name: Action spécifique version
    uses: actions/setup-node@v4.0.1

  - name: Action depuis commit
    uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab

  - name: Action local
    uses: ./.github/actions/my-action

  - name: Action Docker
    uses: docker://alpine:3.8


# === Conditions (if) ===
steps:
  - name: Run on main only
    if: github.ref == 'refs/heads/main'
    run: deploy.sh

  - name: Run on success
    if: success()
    run: echo "Previous steps succeeded"

  - name: Run on failure
    if: failure()
    run: echo "Previous step failed"

  - name: Always run
    if: always()
    run: echo "Runs even if previous steps failed"

  - name: Cancelled
    if: cancelled()
    run: echo "Workflow was cancelled"

  - name: Complex condition
    if: |
      github.event_name == 'push' &&
      github.ref == 'refs/heads/main' &&
      !contains(github.event.head_commit.message, '[skip ci]')
    run: deploy.sh


# === Continue on error ===
steps:
  - name: Step that might fail
    continue-on-error: true
    run: exit 1

  - name: This will still run
    run: echo "Previous step failed but we continue"


# === Timeout ===
steps:
  - name: Step with timeout
    timeout-minutes: 5
    run: long-running-script.sh


# === ID et outputs ===
steps:
  - name: Generate version
    id: version
    run: echo "number=1.2.3" >> $GITHUB_OUTPUT

  - name: Use output
    run: echo "Version is ${{ steps.version.outputs.number }}"


# === Environnement variables ===
steps:
  - name: Set env for step
    run: echo "Hello $NAME"
    env:
      NAME: World

  - name: Set env for workflow
    run: echo "MY_VAR=value" >> $GITHUB_ENV

  - name: Use env set previously
    run: echo $MY_VAR


[OK] ACTIONS OFFICIELLES COURANTES


# === Checkout ===
- uses: actions/checkout@v4                      # Version récente

- uses: actions/checkout@v4
  with:
    fetch-depth: 0                               # Tout l'historique
    submodules: true                             # Avec submodules
    token: ${{ secrets.GITHUB_TOKEN }}           # Token personnalisé
    ref: develop                                 # Branche spécifique
    path: custom-directory                       # Dossier personnalisé


# === Setup Python ===
- uses: actions/setup-python@v5
  with:
    python-version: '3.11'

- uses: actions/setup-python@v5
  with:
    python-version: '3.11'
    cache: 'pip'                                 # Cache pip packages

- uses: actions/setup-python@v5
  with:
    python-version: '3.11'
    cache: 'poetry'                              # Cache poetry

# Matrice Python
strategy:
  matrix:
    python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
steps:
  - uses: actions/setup-python@v5
    with:
      python-version: ${{ matrix.python-version }}


# === Setup Node.js ===
- uses: actions/setup-node@v4
  with:
    node-version: '18'

- uses: actions/setup-node@v4
  with:
    node-version: '18'
    cache: 'npm'                                 # Cache npm

- uses: actions/setup-node@v4
  with:
    node-version-file: '.nvmrc'                  # Version depuis fichier


# === Cache ===
- uses: actions/cache@v4
  with:
    path: ~/.cache/pip
    key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
    restore-keys: |
      ${{ runner.os }}-pip-

# Cache multiple paths
- uses: actions/cache@v4
  with:
    path: |
      ~/.cache/pip
      ~/.local/share/virtualenvs
    key: ${{ runner.os }}-python-${{ hashFiles('**/Pipfile.lock') }}


# === Upload Artifact ===
- uses: actions/upload-artifact@v4
  with:
    name: my-artifact
    path: dist/

- uses: actions/upload-artifact@v4
  with:
    name: test-results
    path: |
      test-results/
      coverage/
    retention-days: 30                           # Conservation (défaut: 90)


# === Download Artifact ===
- uses: actions/download-artifact@v4
  with:
    name: my-artifact
    path: dist/

- uses: actions/download-artifact@v4             # Tous les artifacts


# === Setup autres langages ===

# Java
- uses: actions/setup-java@v4
  with:
    distribution: 'temurin'
    java-version: '17'
    cache: 'maven'

# Go
- uses: actions/setup-go@v5
  with:
    go-version: '1.21'
    cache: true

# Ruby
- uses: ruby/setup-ruby@v1
  with:
    ruby-version: '3.2'
    bundler-cache: true

# .NET
- uses: actions/setup-dotnet@v4
  with:
    dotnet-version: '8.0.x'


# === GitHub CLI ===
- name: Create issue
  run: gh issue create --title "Bug" --body "Description"
  env:
    GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}


# === GitHub Script ===
- uses: actions/github-script@v7
  with:
    script: |
      const issue = await github.rest.issues.create({
        owner: context.repo.owner,
        repo: context.repo.repo,
        title: 'New issue',
        body: 'Created from workflow'
      })
      console.log(`Created issue #${issue.data.number}`)


[OK] VARIABLES & SECRETS


# === Variables d'environnement ===

# Niveau workflow
env:
  NODE_ENV: production
  API_URL: https://api.example.com

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: echo $NODE_ENV

# Niveau job
jobs:
  build:
    runs-on: ubuntu-latest
    env:
      BUILD_NUMBER: 123
    steps:
      - run: echo $BUILD_NUMBER

# Niveau step
steps:
  - name: Build
    run: npm run build
    env:
      NODE_OPTIONS: --max-old-space-size=4096


# === Variables GitHub (automatiques) ===

# Contexte github
- run: echo "${{ github.repository }}"           # owner/repo
- run: echo "${{ github.ref }}"                  # refs/heads/main
- run: echo "${{ github.sha }}"                  # commit SHA
- run: echo "${{ github.actor }}"                # Utilisateur déclencheur
- run: echo "${{ github.event_name }}"           # push, pull_request, etc.
- run: echo "${{ github.run_id }}"               # ID du workflow run
- run: echo "${{ github.run_number }}"           # Numéro du run

# Contexte runner
- run: echo "${{ runner.os }}"                   # Linux, Windows, macOS
- run: echo "${{ runner.arch }}"                 # X64, ARM64
- run: echo "${{ runner.temp }}"                 # Dossier temporaire

# Contexte job
- run: echo "${{ job.status }}"                  # success, failure, cancelled

# Variables d'environnement par défaut
- run: echo "$GITHUB_WORKSPACE"                  # /home/runner/work/repo/repo
- run: echo "$GITHUB_REPOSITORY"                 # owner/repo
- run: echo "$GITHUB_SHA"                        # commit SHA
- run: echo "$GITHUB_REF"                        # refs/heads/main
- run: echo "$GITHUB_REF_NAME"                   # main
- run: echo "$GITHUB_ACTOR"                      # username
- run: echo "$RUNNER_OS"                         # Linux, Windows, macOS


# === Variables de repository ===

# Définir dans Settings > Secrets and variables > Actions > Variables
# Utilisation:
- run: echo "${{ vars.MY_VARIABLE }}"


# === Secrets ===

# Définir dans Settings > Secrets and variables > Actions > Secrets
# Utilisation:
- run: echo "${{ secrets.MY_SECRET }}"

# Ne jamais logger les secrets directement!
# Ils sont automatiquement masqués dans les logs

# Exemple API key
- name: Deploy
  run: deploy.sh
  env:
    API_KEY: ${{ secrets.API_KEY }}

# Exemple tokens
env:
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}      # Token auto-généré
  NPM_TOKEN: ${{ secrets.NPM_TOKEN }}            # Token personnalisé


# === Variables d'environnement dynamiques ===

steps:
  - name: Set variable
    run: echo "TIMESTAMP=$(date +%s)" >> $GITHUB_ENV

  - name: Use variable
    run: echo "Timestamp: $TIMESTAMP"


# === Multiline variables ===
steps:
  - name: Set multiline variable
    run: |
      echo 'JSON_DATA<<EOF' >> $GITHUB_ENV
      echo '{"key": "value", "foo": "bar"}' >> $GITHUB_ENV
      echo 'EOF' >> $GITHUB_ENV

  - name: Use multiline variable
    run: echo "$JSON_DATA"


[OK] EXPRESSIONS & FONCTIONS


# === Syntaxe ===
${{ expression }}


# === Opérateurs ===
${{ 1 + 2 }}                                     # Addition
${{ 'hello' + ' world' }}                        # Concaténation
${{ github.ref == 'refs/heads/main' }}           # Égalité
${{ github.ref != 'refs/heads/main' }}           # Différent
${{ 5 > 3 }}                                     # Comparaison
${{ true && false }}                             # AND logique
${{ true || false }}                             # OR logique
${{ !false }}                                    # NOT logique


# === Fonctions ===

# contains - Vérifier présence
${{ contains('Hello World', 'Hello') }}          # true
${{ contains(github.ref, 'feature/') }}          # true si branche feature/*

# startsWith - Commence par
${{ startsWith(github.ref, 'refs/heads/feature/') }}

# endsWith - Finit par
${{ endsWith(github.ref, '/dev') }}

# format - Formatter string
${{ format('Hello {0} {1}', 'World', '!') }}

# join - Joindre array
${{ join(matrix.os, ', ') }}

# toJSON - Convertir en JSON
${{ toJSON(github) }}

# fromJSON - Parser JSON
${{ fromJSON('{"key": "value"}').key }}

# hashFiles - Hash de fichiers
${{ hashFiles('**/package-lock.json') }}
${{ hashFiles('**/*.py') }}


# === Fonctions de statut ===

# success - Étapes précédentes réussies
if: success()

# failure - Une étape a échoué
if: failure()

# always - Toujours exécuter
if: always()

# cancelled - Workflow annulé
if: cancelled()


# === Exemples complexes ===

# Exécuter seulement sur push main sans [skip ci]
if: |
  github.event_name == 'push' &&
  github.ref == 'refs/heads/main' &&
  !contains(github.event.head_commit.message, '[skip ci]')

# Exécuter sur PR de branches feature vers develop
if: |
  github.event_name == 'pull_request' &&
  github.base_ref == 'develop' &&
  startsWith(github.head_ref, 'feature/')

# Nom de job dynamique
name: Test-${{ matrix.python-version }}-${{ matrix.os }}

# Version dynamique depuis tag
- run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV

# Déterminer environnement
- name: Set environment
  run: |
    if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
      echo "ENVIRONMENT=production" >> $GITHUB_ENV
    else
      echo "ENVIRONMENT=staging" >> $GITHUB_ENV
    fi


[OK] WORKFLOWS PYTHON COMPLETS


# === Test Python Simple ===
name: Python Tests

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'
        cache: 'pip'
    
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
        pip install -r requirements-dev.txt
    
    - name: Run tests
      run: pytest
    
    - name: Run linter
      run: flake8 .
    
    - name: Run type checker
      run: mypy .


# === Test Matrice Multi-Python ===
name: Python Tests Matrix

on: [push, pull_request]

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python ${{ matrix.python-version }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}
        cache: 'pip'
    
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
        pip install pytest pytest-cov
    
    - name: Run tests with coverage
      run: pytest --cov=./ --cov-report=xml
    
    - name: Upload coverage
      uses: codecov/codecov-action@v4
      with:
        file: ./coverage.xml
        flags: ${{ matrix.os }}-py${{ matrix.python-version }}


# === Django CI/CD ===
name: Django CI/CD

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

env:
  PYTHON_VERSION: '3.11'
  DJANGO_SETTINGS_MODULE: project.settings.test

jobs:
  test:
    runs-on: ubuntu-latest
    
    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: testdb
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432
      
      redis:
        image: redis:7
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 10s
        ports:
          - 6379:6379
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: ${{ env.PYTHON_VERSION }}
        cache: 'pip'
    
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
    
    - name: Run migrations
      env:
        DATABASE_URL: postgresql://postgres:postgres@localhost:5432/testdb
      run: python manage.py migrate
    
    - name: Run tests
      env:
        DATABASE_URL: postgresql://postgres:postgres@localhost:5432/testdb
        REDIS_URL: redis://localhost:6379
      run: |
        python manage.py test
        coverage run --source='.' manage.py test
        coverage report
        coverage xml
    
    - name: Upload coverage
      uses: codecov/codecov-action@v4
      with:
        file: ./coverage.xml

  deploy:
    needs: test
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Deploy to production
      env:
        DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
      run: |
        echo "Deploying to production..."
        ./deploy.sh


# === Poetry Workflow ===
name: Poetry CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'
    
    - name: Install Poetry
      uses: snok/install-poetry@v1
      with:
        version: 1.7.1
        virtualenvs-create: true
        virtualenvs-in-project: true
    
    - name: Load cached venv
      id: cached-poetry-dependencies
      uses: actions/cache@v4
      with:
        path: .venv
        key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}
    
    - name: Install dependencies
      if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
      run: poetry install --no-interaction --no-root
    
    - name: Install project
      run: poetry install --no-interaction
    
    - name: Run tests
      run: poetry run pytest
    
    - name: Run linting
      run: |
        poetry run black . --check
        poetry run isort . --check-only
        poetry run flake8 .


# === Publish PyPI ===
name: Publish to PyPI

on:
  release:
    types: [published]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'
    
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install build twine
    
    - name: Build package
      run: python -m build
    
    - name: Publish to PyPI
      env:
        TWINE_USERNAME: __token__
        TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
      run: twine upload dist/*


# === Pre-commit Hooks ===
name: Pre-commit

on: [push, pull_request]

jobs:
  pre-commit:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'
    
    - name: Run pre-commit
      uses: pre-commit/action@v3.0.0


[OK] WORKFLOWS AVANCÉS


# === Monorepo avec paths ===
name: Monorepo CI

on:
  push:
    paths:
      - 'backend/**'
      - 'frontend/**'
      - '.github/workflows/**'

jobs:
  backend:
    if: contains(github.event.head_commit.message, 'backend') || contains(github.event.changed_files, 'backend/')
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Test backend
      working-directory: ./backend
      run: |
        pip install -r requirements.txt
        pytest

  frontend:
    if: contains(github.event.head_commit.message, 'frontend') || contains(github.event.changed_files, 'frontend/')
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Test frontend
      working-directory: ./frontend
      run: |
        npm install
        npm test


# === Workflow réutilisable ===

# .github/workflows/reusable-test.yml
name: Reusable Test Workflow

on:
  workflow_call:
    inputs:
      python-version:
        required: true
        type: string
      working-directory:
        required: false
        type: string
        default: '.'
    secrets:
      token:
        required: true

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: ${{ inputs.python-version }}
    
    - name: Run tests
      working-directory: ${{ inputs.working-directory }}
      run: pytest

# Utilisation
# .github/workflows/main.yml
name: Main CI

on: [push]

jobs:
  call-test:
    uses: ./.github/workflows/reusable-test.yml
    with:
      python-version: '3.11'
      working-directory: './src'
    secrets:
      token: ${{ secrets.GITHUB_TOKEN }}


# === Déploiement conditionnel ===
name: Deploy

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

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Determine environment
      id: env
      run: |
        if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
          echo "name=production" >> $GITHUB_OUTPUT
          echo "url=https://prod.example.com" >> $GITHUB_OUTPUT
        elif [[ "${{ github.ref }}" == "refs/heads/staging" ]]; then
          echo "name=staging" >> $GITHUB_OUTPUT
          echo "url=https://staging.example.com" >> $GITHUB_OUTPUT
        else
          echo "name=development" >> $GITHUB_OUTPUT
          echo "url=https://dev.example.com" >> $GITHUB_OUTPUT
        fi
    
    - name: Deploy to ${{ steps.env.outputs.name }}
      environment:
        name: ${{ steps.env.outputs.name }}
        url: ${{ steps.env.outputs.url }}
      run: |
        echo "Deploying to ${{ steps.env.outputs.name }}"
        ./deploy.sh ${{ steps.env.outputs.name }}


# === Release automatique ===
name: Auto Release

on:
  push:
    branches: [main]

jobs:
  release:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0
    
    - name: Get version from tag
      id: version
      run: |
        VERSION=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
        echo "current=$VERSION" >> $GITHUB_OUTPUT
        
        # Incrémenter version
        MAJOR=$(echo $VERSION | cut -d. -f1 | sed 's/v//')
        MINOR=$(echo $VERSION | cut -d. -f2)
        PATCH=$(echo $VERSION | cut -d. -f3)
        PATCH=$((PATCH + 1))
        NEW_VERSION="v$MAJOR.$MINOR.$PATCH"
        echo "new=$NEW_VERSION" >> $GITHUB_OUTPUT
    
    - name: Generate changelog
      id: changelog
      run: |
        CHANGELOG=$(git log ${{ steps.version.outputs.current }}..HEAD --pretty=format:"- %s" | head -20)
        echo "content<<EOF" >> $GITHUB_OUTPUT
        echo "$CHANGELOG" >> $GITHUB_OUTPUT
        echo "EOF" >> $GITHUB_OUTPUT
    
    - name: Create Release
      uses: actions/create-release@v1
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        tag_name: ${{ steps.version.outputs.new }}
        release_name: Release ${{ steps.version.outputs.new }}
        body: |
          ## Changes
          ${{ steps.changelog.outputs.content }}
        draft: false
        prerelease: false


# === Docker Build & Push ===
name: Docker Build

on:
  push:
    branches: [main]
    tags: ['v*']

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Docker Buildx
      uses: docker/setup-buildx-action@v3
    
    - name: Login to GitHub Container Registry
      uses: docker/login-action@v3
      with:
        registry: ${{ env.REGISTRY }}
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}
    
    - name: Extract metadata
      id: meta
      uses: docker/metadata-action@v5
      with:
        images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
        tags: |
          type=ref,event=branch
          type=semver,pattern={{version}}
          type=semver,pattern={{major}}.{{minor}}
          type=sha
    
    - name: Build and push
      uses: docker/build-push-action@v5
      with:
        context: .
        push: true
        tags: ${{ steps.meta.outputs.tags }}
        labels: ${{ steps.meta.outputs.labels }}
        cache-from: type=gha
        cache-to: type=gha,mode=max


# === Notification Slack ===
name: Slack Notification

on:
  push:
    branches: [main]

jobs:
  notify:
    runs-on: ubuntu-latest
    
    steps:
    - name: Notify Slack
      uses: 8398a7/action-slack@v3
      with:
        status: ${{ job.status }}
        text: 'Deployment completed!'
        webhook_url: ${{ secrets.SLACK_WEBHOOK }}
      if: always()


# === Dependency Review (PR) ===
name: Dependency Review

on: [pull_request]

jobs:
  dependency-review:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Dependency Review
      uses: actions/dependency-review-action@v4


# === Security Scanning ===
name: Security Scan

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 0 * * 0'  # Hebdomadaire

jobs:
  security:
    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: '.'
        format: 'sarif'
        output: 'trivy-results.sarif'
    
    - name: Upload Trivy results to GitHub Security
      uses: github/codeql-action/upload-sarif@v3
      with:
        sarif_file: 'trivy-results.sarif'


# === CodeQL Analysis ===
name: CodeQL

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 0 * * 1'

jobs:
  analyze:
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
      security-events: write
    
    strategy:
      matrix:
        language: ['python', 'javascript']
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Initialize CodeQL
      uses: github/codeql-action/init@v3
      with:
        languages: ${{ matrix.language }}
    
    - name: Autobuild
      uses: github/codeql-action/autobuild@v3
    
    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v3


[OK] COMPOSITE ACTIONS


# Créer action réutilisable
# .github/actions/setup-python-env/action.yml

name: 'Setup Python Environment'
description: 'Setup Python with caching and dependencies'

inputs:
  python-version:
    description: 'Python version'
    required: true
    default: '3.11'
  cache-dependency-path:
    description: 'Path to requirements file'
    required: false
    default: '**/requirements*.txt'

runs:
  using: 'composite'
  steps:
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: ${{ inputs.python-version }}
        cache: 'pip'
        cache-dependency-path: ${{ inputs.cache-dependency-path }}
    
    - name: Install dependencies
      shell: bash
      run: |
        python -m pip install --upgrade pip
        if [ -f requirements.txt ]; then
          pip install -r requirements.txt
        fi

# Utilisation
steps:
  - uses: actions/checkout@v4
  - uses: ./.github/actions/setup-python-env
    with:
      python-version: '3.11'


# === Docker Action ===
# .github/actions/my-docker-action/action.yml

name: 'My Docker Action'
description: 'Run custom Docker action'

inputs:
  who-to-greet:
    description: 'Who to greet'
    required: true

outputs:
  time:
    description: 'The time we greeted you'

runs:
  using: 'docker'
  image: 'Dockerfile'
  args:
    - ${{ inputs.who-to-greet }}


# === JavaScript Action ===
# .github/actions/my-js-action/action.yml

name: 'My JavaScript Action'
description: 'Run custom JS action'

inputs:
  milliseconds:
    description: 'Time to wait'
    required: true
    default: '1000'

runs:
  using: 'node20'
  main: 'dist/index.js'


[OK] GITHUB PAGES DEPLOYMENT


# === Deploy Static Site ===
name: Deploy to GitHub Pages

on:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: "pages"
  cancel-in-progress: false

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Node
      uses: actions/setup-node@v4
      with:
        node-version: '18'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Build
      run: npm run build
    
    - name: Upload artifact
      uses: actions/upload-pages-artifact@v3
      with:
        path: './dist'
  
  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    needs: build
    steps:
    - name: Deploy to GitHub Pages
      id: deployment
      uses: actions/deploy-pages@v4


# === MkDocs Documentation ===
name: Deploy Documentation

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'
    
    - name: Install dependencies
      run: |
        pip install mkdocs mkdocs-material
    
    - name: Deploy docs
      run: mkdocs gh-deploy --force


# === Sphinx Documentation ===
name: Build Sphinx Docs

on: [push]

jobs:
  docs:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'
    
    - name: Install dependencies
      run: |
        pip install sphinx sphinx_rtd_theme
    
    - name: Build docs
      run: |
        cd docs
        make html
    
    - name: Deploy to GitHub Pages
      uses: peaceiris/actions-gh-pages@v3
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_dir: ./docs/_build/html


[OK] MATRICES AVANCÉES


# === Matrice avec exclusions ===
strategy:
  matrix:
    os: [ubuntu-latest, windows-latest, macos-latest]
    python: ['3.8', '3.9', '3.10', '3.11', '3.12']
    exclude:
      # Python 3.8 pas supporté sur macOS 14 (ARM)
      - os: macos-latest
        python: '3.8'
      # Python 3.12 pas testé sur Windows
      - os: windows-latest
        python: '3.12'


# === Matrice avec inclusions ===
strategy:
  matrix:
    os: [ubuntu-latest]
    python: ['3.11']
    include:
      # Tests spéciaux sur Python 3.12
      - os: ubuntu-latest
        python: '3.12'
        experimental: true
      # Tests Windows seulement sur Python stable
      - os: windows-latest
        python: '3.11'
      # Tests macOS ARM64
      - os: macos-14
        python: '3.11'
        arch: ARM64


# === Matrice dynamique ===
jobs:
  setup:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}
    steps:
    - id: set-matrix
      run: |
        if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
          echo "matrix={\"python\":[\"3.9\",\"3.10\",\"3.11\",\"3.12\"]}" >> $GITHUB_OUTPUT
        else
          echo "matrix={\"python\":[\"3.11\"]}" >> $GITHUB_OUTPUT
        fi
  
  test:
    needs: setup
    runs-on: ubuntu-latest
    strategy:
      matrix: ${{ fromJSON(needs.setup.outputs.matrix) }}
    steps:
    - uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python }}


[OK] CONCURRENCY & CANCELLATION


# === Annuler workflows en cours ===
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

# Annule workflows précédents sur même branche
# Utile pour économiser minutes CI


# === Concurrency par PR ===
concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true


# === Pas d'annulation sur main ===
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}


[OK] PERMISSIONS


# === Permissions par défaut (restrictives) ===
permissions: {}

# === Lecture seule ===
permissions: read-all

# === Permissions spécifiques ===
permissions:
  contents: read          # Lire code
  pull-requests: write    # Commenter PR
  issues: write           # Créer/modifier issues
  packages: write         # Publier packages
  deployments: write      # Créer déploiements
  statuses: write         # Créer status checks


# === Permissions au niveau job ===
jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
    - run: echo "Building..."


# === Token GITHUB_TOKEN ===
# Token automatique avec permissions limitées
# Expire à la fin du workflow
# Utilisation:
env:
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


[OK] ENVIRONMENTS


# === Définir environnement ===
jobs:
  deploy:
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://example.com
    steps:
    - name: Deploy
      run: ./deploy.sh


# === Environnement avec approval ===
# Configuration dans Settings > Environments
# - Required reviewers
# - Wait timer
# - Deployment branches

environment:
  name: production
  url: ${{ steps.deploy.outputs.url }}


# === Variables d'environnement ===
# Définies dans Settings > Environments > production > Variables
jobs:
  deploy:
    environment: production
    steps:
    - run: echo "${{ vars.API_URL }}"
    - run: echo "${{ secrets.API_KEY }}"


[OK] DEBUGGING


# === Enable debug logging ===
# Settings > Secrets > New repository secret
# Name: ACTIONS_RUNNER_DEBUG
# Value: true

# Name: ACTIONS_STEP_DEBUG
# Value: true


# === Debug dans workflow ===
steps:
  - name: Debug info
    run: |
      echo "Event name: ${{ github.event_name }}"
      echo "Ref: ${{ github.ref }}"
      echo "SHA: ${{ github.sha }}"
      echo "Actor: ${{ github.actor }}"
      echo "Runner OS: ${{ runner.os }}"
      echo "Working directory: $PWD"
      env


# === Dump contexts ===
- name: Dump GitHub context
  run: echo '${{ toJSON(github) }}'

- name: Dump job context
  run: echo '${{ toJSON(job) }}'

- name: Dump steps context
  run: echo '${{ toJSON(steps) }}'

- name: Dump runner context
  run: echo '${{ toJSON(runner) }}'

- name: Dump env context
  run: echo '${{ toJSON(env) }}'


# === SSH Debug ===
- name: Setup tmate session
  uses: mxschmitt/action-tmate@v3
  if: failure()
  # Crée session SSH pour débugger


[OK] BEST PRACTICES


# === 1. Pin versions des actions ===
# [OK] BON
uses: actions/checkout@v4

# [OK] MEILLEUR (commit SHA)
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab

# [X] MAUVAIS
uses: actions/checkout@main


# === 2. Utiliser cache ===
- uses: actions/cache@v4
  with:
    path: ~/.cache/pip
    key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
    restore-keys: |
      ${{ runner.os }}-pip-


# === 3. Fail-fast désactivé pour matrices ===
strategy:
  fail-fast: false      # Continue même si un test échoue
  matrix:
    python: ['3.9', '3.10', '3.11']


# === 4. Timeouts ===
jobs:
  build:
    timeout-minutes: 30
    steps:
    - name: Long task
      timeout-minutes: 10
      run: ./long-script.sh


# === 5. Ne pas logger secrets ===
# [X] MAUVAIS
- run: echo "Token: ${{ secrets.API_TOKEN }}"

# [OK] BON
- run: |
    echo "::add-mask::${{ secrets.API_TOKEN }}"
    ./script.sh
  env:
    API_TOKEN: ${{ secrets.API_TOKEN }}


# === 6. Concurrency pour économiser minutes ===
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true


# === 7. Conditions pour skip ===
if: "!contains(github.event.head_commit.message, '[skip ci]')"


# === 8. Artifacts pour partager entre jobs ===
jobs:
  build:
    steps:
    - run: npm run build
    - uses: actions/upload-artifact@v4
      with:
        name: dist
        path: dist/
  
  deploy:
    needs: build
    steps:
    - uses: actions/download-artifact@v4
      with:
        name: dist


# === 9. Séparer workflows ===
# .github/workflows/ci.yml - Tests
# .github/workflows/deploy.yml - Déploiement
# .github/workflows/cron.yml - Tâches périodiques


# === 10. Utiliser dependabot ===
# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"


[OK] ACTIONS MARKETPLACE POPULAIRES


# === Code Quality ===

# Super-Linter
- uses: github/super-linter@v5
  env:
    DEFAULT_BRANCH: main
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

# SonarCloud
- uses: SonarSource/sonarcloud-github-action@master
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}


# === Coverage ===

# Codecov
- uses: codecov/codecov-action@v4
  with:
    token: ${{ secrets.CODECOV_TOKEN }}
    files: ./coverage.xml

# Coveralls
- uses: coverallsapp/github-action@master
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}


# === Release ===

# Semantic Release
- uses: cycjimmy/semantic-release-action@v4
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

# Release Drafter
- uses: release-drafter/release-drafter@v5
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


# === Notifications ===

# Slack
- uses: 8398a7/action-slack@v3
  with:
    status: ${{ job.status }}
    webhook_url: ${{ secrets.SLACK_WEBHOOK }}

# Discord
- uses: sarisia/actions-status-discord@v1
  with:
    webhook: ${{ secrets.DISCORD_WEBHOOK }}

# Telegram
- uses: appleboy/telegram-action@master
  with:
    to: ${{ secrets.TELEGRAM_TO }}
    token: ${{ secrets.TELEGRAM_TOKEN }}
    message: Deployment completed!


# === Deployment ===

# AWS
- uses: aws-actions/configure-aws-credentials@v4
  with:
    aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    aws-region: us-east-1

# Heroku
- uses: akhileshns/heroku-deploy@v3.13.15
  with:
    heroku_api_key: ${{ secrets.HEROKU_API_KEY }}
    heroku_app_name: "my-app"
    heroku_email: "email@example.com"

# Vercel
- uses: amondnet/vercel-action@v25
  with:
    vercel-token: ${{ secrets.VERCEL_TOKEN }}
    vercel-org-id: ${{ secrets.ORG_ID }}
    vercel-project-id: ${{ secrets.PROJECT_ID }}


# === Utilities ===

# Label PR
- uses: actions/labeler@v5
  with:
    repo-token: ${{ secrets.GITHUB_TOKEN }}

# Stale Bot
- uses: actions/stale@v9
  with:
    stale-issue-message: 'This issue is stale'
    days-before-stale: 30
    days-before-close: 7

# Auto-merge Dependabot
- uses: ahmadnassri/action-dependabot-auto-merge@v2
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}


[OK] TROUBLESHOOTING


# === Erreur: Permission denied ===
# Solution: Ajouter permissions
permissions:
  contents: write
  pull-requests: write


# === Erreur: Resource not accessible by integration ===
# Solution: Vérifier permissions GITHUB_TOKEN
permissions:
  contents: read
  packages: write


# === Workflow ne se déclenche pas ===
# Vérifier:
# 1. Fichier dans .github/workflows/
# 2. Extension .yml ou .yaml
# 3. Syntaxe YAML valide
# 4. Événement configuré correctement
# 5. Branches/paths correspondent


# === Cache ne fonctionne pas ===
# Vérifier:
# 1. Key unique et stable
# 2. Path correct
# 3. Restore-keys configuré
# 4. Cache pas expiré (7 jours)


# === Secrets non disponibles ===
# Vérifier:
# 1. Secret défini dans Settings > Secrets
# 2. Scope correct (repo/org/environment)
# 3. Syntaxe: ${{ secrets.SECRET_NAME }}


# === Workflow trop lent ===
# Optimisations:
# 1. Activer cache (pip, npm, etc.)
# 2. Parallel jobs
# 3. Matrice fail-fast: false
# 4. Concurrency cancel-in-progress
# 5. Réduire checkout fetch-depth


# === Limite de minutes dépassée ===
# Solutions:
# 1. Optimiser workflows
# 2. Self-hosted runners
# 3. Upgrade plan GitHub
# 4. Split workflows


# === Artifact trop gros ===
# Limite: 10GB par workflow
# Solutions:
# 1. Compresser artifacts
# 2. Réduire retention-days
# 3. Nettoyer artifacts inutiles


[OK] SELF-HOSTED RUNNERS


# === Installation ===

# Linux
mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64-2.311.0.tar.gz \
  -L https://github.com/actions/runner/releases/download/v2.311.0/actions-runner-linux-x64-2.311.0.tar.gz
tar xzf ./actions-runner-linux-x64-2.311.0.tar.gz

# Configuration
./config.sh --url https://github.com/OWNER/REPO --token TOKEN

# Lancer
./run.sh

# Service (Linux)
sudo ./svc.sh install
sudo ./svc.sh start


# === Labels ===
./config.sh --url URL --token TOKEN --labels prod,gpu,linux


# === Utilisation ===
runs-on: self-hosted

runs-on: [self-hosted, linux, x64, gpu]


# === Runner Groups (Enterprise) ===
runs-on:
  group: production-runners
  labels: [linux, x64]


# === Docker dans runner ===
jobs:
  build:
    runs-on: self-hosted
    container:
      image: python:3.11
    steps:
    - run: python --version


[OK] SECURITY


# === Dependabot ===
# .github/dependabot.yml
version: 2
updates:
  # GitHub Actions
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"
  
  # Python
  - package-ecosystem: "pip"
    directory: "/"
    schedule:
      interval: "daily"


# === Secret scanning ===
# Automatique pour repos publics
# Settings > Code security and analysis


# === Code scanning (CodeQL) ===
name: "CodeQL"

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 0 * * 1'

jobs:
  analyze:
    runs-on: ubuntu-latest
    permissions:
      security-events: write
    steps:
    - uses: actions/checkout@v4
    - uses: github/codeql-action/init@v3
      with:
        languages: python
    - uses: github/codeql-action/analyze@v3


# === OIDC (OpenID Connect) ===
# Authentification sans secrets
permissions:
  id-token: write
  contents: read

steps:
- uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: arn:aws:iam::123456789012:role/my-role
    aws-region: us-east-1


[OK] RESSOURCES


# Documentation officielle
https://docs.github.com/en/actions

# Actions Marketplace
https://github.com/marketplace?type=actions

# Awesome Actions
https://github.com/sdras/awesome-actions

# Status GitHub Actions
https://www.githubstatus.com/

# Community Forum
https://github.community/c/github-actions/

# Pricing
https://docs.github.com/en/billing/managing-billing-for-github-actions


[OK] EXEMPLES COMPLETS PAR STACK


# === FastAPI ===
name: FastAPI CI/CD

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: testdb
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'
        cache: 'pip'
    
    - name: Install dependencies
      run: |
        pip install -r requirements.txt
        pip install pytest pytest-cov httpx
    
    - name: Run migrations
      env:
        DATABASE_URL: postgresql://postgres:postgres@localhost:5432/testdb
      run: alembic upgrade head
    
    - name: Run tests
      env:
        DATABASE_URL: postgresql://postgres:postgres@localhost:5432/testdb
      run: pytest --cov=app --cov-report=xml
    
    - name: Upload coverage
      uses: codecov/codecov-action@v4
      with:
        file: ./coverage.xml
  
  deploy:
    needs: test
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Deploy to production
      run: |
        echo "Deploying FastAPI app..."


# === Flask ===
name: Flask CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    services:
      redis:
        image: redis:7
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 10s
        ports:
          - 6379:6379
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'
        cache: 'pip'
    
    - name: Install dependencies
      run: |
        pip install -r requirements.txt
        pip install pytest pytest-flask pytest-cov
    
    - name: Run tests
      env:
        FLASK_ENV: testing
        REDIS_URL: redis://localhost:6379
      run: |
        pytest --cov=app --cov-report=xml tests/
    
    - name: Lint with flake8
      run: flake8 app/ tests/
    
    - name: Type check with mypy
      run: mypy app/


# === Celery + Redis ===
name: Celery Tasks

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    services:
      redis:
        image: redis:7
        ports:
          - 6379:6379
      
      rabbitmq:
        image: rabbitmq:3-management
        ports:
          - 5672:5672
          - 15672:15672
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'
        cache: 'pip'
    
    - name: Install dependencies
      run: |
        pip install -r requirements.txt
        pip install pytest pytest-celery
    
    - name: Run Celery tests
      env:
        CELERY_BROKER_URL: redis://localhost:6379/0
        CELERY_RESULT_BACKEND: redis://localhost:6379/0
      run: pytest tests/test_celery.py


# === Scrapy ===
name: Scrapy Spider

on:
  push:
    branches: [main]
  schedule:
    - cron: '0 */6 * * *'  # Toutes les 6 heures

jobs:
  scrape:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'
    
    - name: Install dependencies
      run: |
        pip install scrapy scrapyd-client
    
    - name: Run spider
      run: |
        scrapy crawl myspider -o output.json
    
    - name: Upload results
      uses: actions/upload-artifact@v4
      with:
        name: scrape-results
        path: output.json


# === Jupyter Notebooks ===
name: Jupyter Notebooks

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'
        cache: 'pip'
    
    - name: Install dependencies
      run: |
        pip install jupyter nbconvert pytest nbval
    
    - name: Test notebooks
      run: pytest --nbval-lax notebooks/
    
    - name: Convert notebooks to HTML
      run: |
        jupyter nbconvert --to html notebooks/*.ipynb
    
    - name: Upload HTML
      uses: actions/upload-artifact@v4
      with:
        name: notebooks-html
        path: notebooks/*.html


# === Data Science Pipeline ===
name: ML Pipeline

on:
  push:
    branches: [main]
  schedule:
    - cron: '0 0 * * *'  # Daily

jobs:
  train:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'
        cache: 'pip'
    
    - name: Install dependencies
      run: |
        pip install -r requirements.txt
        pip install mlflow scikit-learn pandas numpy
    
    - name: Download data
      run: python scripts/download_data.py
    
    - name: Train model
      run: python scripts/train_model.py
      env:
        MLFLOW_TRACKING_URI: ${{ secrets.MLFLOW_URI }}
    
    - name: Evaluate model
      run: python scripts/evaluate_model.py
    
    - name: Upload model
      uses: actions/upload-artifact@v4
      with:
        name: trained-model
        path: models/


# === AWS Lambda Deployment ===
name: Deploy Lambda

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'
    
    - name: Install dependencies
      run: |
        pip install -r requirements.txt -t package/
        cp lambda_function.py package/
    
    - name: Create deployment package
      run: |
        cd package
        zip -r ../lambda.zip .
    
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v4
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: us-east-1
    
    - name: Deploy to Lambda
      run: |
        aws lambda update-function-code \
          --function-name my-function \
          --zip-file fileb://lambda.zip


# === Google Cloud Run ===
name: Deploy to Cloud Run

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Authenticate to Google Cloud
      uses: google-github-actions/auth@v2
      with:
        credentials_json: ${{ secrets.GCP_SA_KEY }}
    
    - name: Set up Cloud SDK
      uses: google-github-actions/setup-gcloud@v2
    
    - name: Build and push Docker image
      run: |
        gcloud builds submit \
          --tag gcr.io/${{ secrets.GCP_PROJECT }}/myapp
    
    - name: Deploy to Cloud Run
      run: |
        gcloud run deploy myapp \
          --image gcr.io/${{ secrets.GCP_PROJECT }}/myapp \
          --platform managed \
          --region us-central1 \
          --allow-unauthenticated


# === Azure Functions ===
name: Deploy to Azure Functions

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'
    
    - name: Install dependencies
      run: |
        pip install -r requirements.txt --target=".python_packages/lib/site-packages"
    
    - name: Deploy to Azure Functions
      uses: Azure/functions-action@v1
      with:
        app-name: my-function-app
        package: .
        publish-profile: ${{ secrets.AZURE_FUNCTIONAPP_PUBLISH_PROFILE }}


# === Terraform ===
name: Terraform

on:
  push:
    branches: [main]
  pull_request:

jobs:
  terraform:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Terraform
      uses: hashicorp/setup-terraform@v3
      with:
        terraform_version: 1.6.0
    
    - name: Terraform Init
      run: terraform init
      env:
        AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
        AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    
    - name: Terraform Format
      run: terraform fmt -check
    
    - name: Terraform Validate
      run: terraform validate
    
    - name: Terraform Plan
      run: terraform plan -out=tfplan
      env:
        AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
        AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    
    - name: Terraform Apply
      if: github.ref == 'refs/heads/main' && github.event_name == 'push'
      run: terraform apply -auto-approve tfplan
      env:
        AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
        AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}


[OK] WORKFLOWS AVEC APPROBATION


# === Déploiement avec approbation manuelle ===
name: Deploy with Approval

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Build
      run: npm run build
    - uses: actions/upload-artifact@v4
      with:
        name: build
        path: dist/
  
  request-approval:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: production-approval
    steps:
    - name: Waiting for approval
      run: echo "Deployment approved!"
  
  deploy:
    needs: request-approval
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://example.com
    steps:
    - uses: actions/download-artifact@v4
      with:
        name: build
    - name: Deploy
      run: ./deploy.sh


# === Workflow avec review multiple ===
# Configuration dans Settings > Environments > production
# Required reviewers: 2 reviewers minimum


[OK] CACHING AVANCÉ


# === Cache avec fallback ===
- name: Cache Python dependencies
  uses: actions/cache@v4
  with:
    path: |
      ~/.cache/pip
      ~/.local/share/virtualenvs
    key: ${{ runner.os }}-python-${{ hashFiles('**/Pipfile.lock') }}
    restore-keys: |
      ${{ runner.os }}-python-${{ hashFiles('**/Pipfile.lock') }}
      ${{ runner.os }}-python-
      ${{ runner.os }}-


# === Cache Docker layers ===
- name: Set up Docker Buildx
  uses: docker/setup-buildx-action@v3

- name: Cache Docker layers
  uses: actions/cache@v4
  with:
    path: /tmp/.buildx-cache
    key: ${{ runner.os }}-buildx-${{ github.sha }}
    restore-keys: |
      ${{ runner.os }}-buildx-

- name: Build
  uses: docker/build-push-action@v5
  with:
    cache-from: type=local,src=/tmp/.buildx-cache
    cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max

- name: Move cache
  run: |
    rm -rf /tmp/.buildx-cache
    mv /tmp/.buildx-cache-new /tmp/.buildx-cache


# === Cache npm/yarn/pnpm ===
- uses: actions/setup-node@v4
  with:
    node-version: '18'
    cache: 'npm'
    cache-dependency-path: '**/package-lock.json'


# === Cache Poetry ===
- name: Cache Poetry dependencies
  uses: actions/cache@v4
  with:
    path: ~/.cache/pypoetry
    key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}


# === Cache Gradle ===
- name: Cache Gradle packages
  uses: actions/cache@v4
  with:
    path: |
      ~/.gradle/caches
      ~/.gradle/wrapper
    key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}


[OK] STRATÉGIES DE DÉPLOIEMENT


# === Blue-Green Deployment ===
name: Blue-Green Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Deploy to green environment
      run: ./deploy.sh green
    
    - name: Run smoke tests
      run: ./smoke-tests.sh green
    
    - name: Switch traffic to green
      run: ./switch-traffic.sh green
    
    - name: Monitor for 5 minutes
      run: sleep 300
    
    - name: Rollback if needed
      if: failure()
      run: ./switch-traffic.sh blue


# === Canary Deployment ===
name: Canary Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Deploy canary (10%)
      run: ./deploy-canary.sh --traffic=10
    
    - name: Wait and monitor
      run: sleep 600
    
    - name: Increase to 50%
      run: ./deploy-canary.sh --traffic=50
    
    - name: Wait and monitor
      run: sleep 600
    
    - name: Full deployment
      run: ./deploy-canary.sh --traffic=100


# === Rolling Deployment ===
name: Rolling Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        server: [server1, server2, server3, server4]
      max-parallel: 1  # Un serveur à la fois
    steps:
    - uses: actions/checkout@v4
    
    - name: Deploy to ${{ matrix.server }}
      run: ./deploy.sh ${{ matrix.server }}
    
    - name: Health check
      run: ./health-check.sh ${{ matrix.server }}
    
    - name: Wait before next
      run: sleep 60


[OK] MULTI-ENVIRONMENT


# === Stratégie par environnement ===
name: Multi-Environment Deploy

on:
  push:
    branches: [dev, staging, main]

jobs:
  determine-env:
    runs-on: ubuntu-latest
    outputs:
      environment: ${{ steps.set-env.outputs.environment }}
    steps:
    - id: set-env
      run: |
        if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
          echo "environment=production" >> $GITHUB_OUTPUT
        elif [[ "${{ github.ref }}" == "refs/heads/staging" ]]; then
          echo "environment=staging" >> $GITHUB_OUTPUT
        else
          echo "environment=development" >> $GITHUB_OUTPUT
        fi
  
  deploy:
    needs: determine-env
    runs-on: ubuntu-latest
    environment:
      name: ${{ needs.determine-env.outputs.environment }}
    steps:
    - uses: actions/checkout@v4
    
    - name: Deploy to ${{ needs.determine-env.outputs.environment }}
      run: |
        echo "Deploying to ${{ needs.determine-env.outputs.environment }}"
        ./deploy.sh ${{ needs.determine-env.outputs.environment }}
      env:
        API_KEY: ${{ secrets.API_KEY }}
        DATABASE_URL: ${{ vars.DATABASE_URL }}


[OK] WORKFLOW DISPATCH AVANCÉ


# === Déploiement manuel avec options ===
name: Manual Deploy

on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Target environment'
        required: true
        type: choice
        options:
          - development
          - staging
          - production
      
      version:
        description: 'Version to deploy (leave empty for latest)'
        required: false
        type: string
      
      run_migrations:
        description: 'Run database migrations'
        required: true
        type: boolean
        default: true
      
      send_notification:
        description: 'Send Slack notification'
        required: false
        type: boolean
        default: true
      
      rollback:
        description: 'Rollback deployment'
        required: false
        type: boolean
        default: false

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}
    
    steps:
    - uses: actions/checkout@v4
      with:
        ref: ${{ inputs.version || github.ref }}
    
    - name: Deploy application
      if: ${{ !inputs.rollback }}
      run: ./deploy.sh ${{ inputs.environment }}
    
    - name: Rollback
      if: ${{ inputs.rollback }}
      run: ./rollback.sh ${{ inputs.environment }}
    
    - name: Run migrations
      if: ${{ inputs.run_migrations && !inputs.rollback }}
      run: ./migrate.sh
    
    - name: Notify Slack
      if: ${{ inputs.send_notification }}
      uses: 8398a7/action-slack@v3
      with:
        status: ${{ job.status }}
        text: |
          Deployment to ${{ inputs.environment }}
          Version: ${{ inputs.version || 'latest' }}
          Migrations: ${{ inputs.run_migrations }}
        webhook_url: ${{ secrets.SLACK_WEBHOOK }}


[OK] CONDITIONAL WORKFLOWS


# === Exécuter selon fichiers modifiés ===
name: Smart CI

on:
  pull_request:
    paths:
      - 'backend/**'
      - 'frontend/**'
      - 'infrastructure/**'

jobs:
  detect-changes:
    runs-on: ubuntu-latest
    outputs:
      backend: ${{ steps.filter.outputs.backend }}
      frontend: ${{ steps.filter.outputs.frontend }}
      infrastructure: ${{ steps.filter.outputs.infrastructure }}
    steps:
    - uses: actions/checkout@v4
    
    - uses: dorny/paths-filter@v2
      id: filter
      with:
        filters: |
          backend:
            - 'backend/**'
          frontend:
            - 'frontend/**'
          infrastructure:
            - 'infrastructure/**'
  
  test-backend:
    needs: detect-changes
    if: needs.detect-changes.outputs.backend == 'true'
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Test backend
      run: cd backend && pytest
  
  test-frontend:
    needs: detect-changes
    if: needs.detect-changes.outputs.frontend == 'true'
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Test frontend
      run: cd frontend && npm test
  
  deploy-infrastructure:
    needs: detect-changes
    if: needs.detect-changes.outputs.infrastructure == 'true'
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Deploy infrastructure
      run: cd infrastructure && terraform apply


# === Skip CI ===
name: CI with Skip

on: [push, pull_request]

jobs:
  test:
    if: "!contains(github.event.head_commit.message, '[skip ci]')"
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - run: npm test


[OK] PERFORMANCE & OPTIMIZATION


# === Workflow parallèle optimisé ===
name: Optimized CI

on: [push, pull_request]

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

jobs:
  setup:
    runs-on: ubuntu-latest
    outputs:
      cache-key: ${{ steps.cache-key.outputs.value }}
    steps:
    - uses: actions/checkout@v4
    
    - id: cache-key
      run: echo "value=${{ hashFiles('**/requirements.txt') }}" >> $GITHUB_OUTPUT
    
    - uses: actions/cache@v4
      id: cache
      with:
        path: ~/.cache/pip
        key: ${{ runner.os }}-pip-${{ steps.cache-key.outputs.value }}
    
    - name: Install dependencies
      if: steps.cache.outputs.cache-hit != 'true'
      run: pip install -r requirements.txt
  
  lint:
    needs: setup
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: actions/cache@v4
      with:
        path: ~/.cache/pip
        key: ${{ runner.os }}-pip-${{ needs.setup.outputs.cache-key }}
    - run: flake8 .
  
  test:
    needs: setup
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ['3.9', '3.10', '3.11']
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}
    - uses: actions/cache@v4
      with:
        path: ~/.cache/pip
        key: ${{ runner.os }}-pip-${{ needs.setup.outputs.cache-key }}
    - run: pytest
  
  security:
    needs: setup
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: actions/cache@v4
      with:
        path: ~/.cache/pip
        key: ${{ runner.os }}-pip-${{ needs.setup.outputs.cache-key }}
    - run: bandit -r .


[OK] GESTION D'ERREURS


# === Retry on failure ===
- name: Flaky test with retry
  uses: nick-fields/retry@v2
  with:
    timeout_minutes: 10
    max_attempts: 3
    retry_on: error
    command: pytest tests/integration


# === Notification sur échec ===
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Run tests
      run: pytest
    
    - name: Notify on failure
      if: failure()
      uses: 8398a7/action-slack@v3
      with:
        status: failure
        text: 'Tests failed!'
        webhook_url: ${{ secrets.SLACK_WEBHOOK }}


# === Continue on error spécifique ===
- name: Optional step
  id: optional
  continue-on-error: true
  run: ./optional-script.sh

- name: Check optional result
  if: steps.optional.outcome == 'failure'
  run: echo "Optional step failed but we continue"


[OK] CUSTOM GITHUB ACTIONS (JavaScript)


# === Action JavaScript personnalisée ===
# .github/actions/hello/action.yml

name: 'Hello Action'
description: 'Say hello'
inputs:
  who-to-greet:
    description: 'Who to greet'
    required: true
    default: 'World'
outputs:
  time:
    description: 'Time of greeting'
runs:
  using: 'node20'
  main: 'index.js'

# .github/actions/hello/index.js
const core = require('@actions/core');
const github = require('@actions/github');

try {
  const nameToGreet = core.getInput('who-to-greet');
  console.log(`Hello ${nameToGreet}!`);
  
  const time = (new Date()).toTimeString();
  core.setOutput('time', time);
  
  const payload = JSON.stringify(github.context.payload, undefined, 2);
  console.log(`The event payload: ${payload}`);
} catch (error) {
  core.setFailed(error.message);
}

# Utilisation
- uses: ./.github/actions/hello
  with:
    who-to-greet: 'Alice'


[OK] PATTERNS AVANCÉS


# === Job dependencies complexes ===
jobs:
  build-backend:
    runs-on: ubuntu-latest
    steps:
    - run: echo "Building backend"
  
  build-frontend:
    runs-on: ubuntu-latest
    steps:
    - run: echo "Building frontend"
  
  test-backend:
    needs: build-backend
    runs-on: ubuntu-latest
    steps:
    - run: echo "Testing backend"
  
  test-frontend:
    needs: build-frontend
    runs-on: ubuntu-latest
    steps:
    - run: echo "Testing frontend"
  
  integration-tests:
    needs: [build-backend, build-frontend]
    runs-on: ubuntu-latest
    steps:
    - run: echo "Integration tests"
  
  deploy:
    needs: [test-backend, test-frontend, integration-tests]
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
    - run: echo "Deploying"


# === Dynamic job creation ===
jobs:
  setup:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}
    steps:
    - uses: actions/checkout@v4
    
    - id: set-matrix
      run: |
        # Lire depuis fichier ou API
        MATRIX=$(cat .github/test-matrix.json)
        echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
  
  test:
    needs: setup
    runs-on: ubuntu-latest
    strategy:
      matrix: ${{ fromJSON(needs.setup.outputs.matrix) }}
    steps:
    - uses: actions/checkout@v4
    - run: echo "Testing with ${{ matrix.version }}"


# === Workflow de workflow ===
name: Orchestrator

on:
  workflow_dispatch:

jobs:
  trigger-tests:
    runs-on: ubuntu-latest
    steps:
    - name: Trigger test workflow
      uses: actions/github-script@v7
      with:
        script: |
          await github.rest.actions.createWorkflowDispatch({
            owner: context.repo.owner,
            repo: context.repo.repo,
            workflow_id: 'test.yml',
            ref: 'main'
          })


[OK] MONITORING & OBSERVABILITY


# === Status badge ===
# README.md
[![CI](https://github.com/username/repo/actions/workflows/ci.yml/badge.svg)](https://github.com/username/repo/actions/workflows/ci.yml)


# === Métriques de workflow ===
name: Metrics

on:
  workflow_run:
    workflows: ["CI"]
    types: [completed]

jobs:
  metrics:
    runs-on: ubuntu-latest
    steps:
    - name: Get workflow metrics
      uses: actions/github-script@v7
      with:
        script: |
          const run = context.payload.workflow_run;
          console.log(`Workflow: ${run.name}`);
          console.log(`Status: ${run.conclusion}`);
          console.log(`Duration: ${run.run_started_at} - ${run.updated_at}`);
          
          // Envoyer à système monitoring
          const duration = new Date(run.updated_at) - new Date(run.run_started_at);
          console.log(`Duration ms: ${duration}`);


# === Custom metrics ===
- name: Record build time
  run: |
    START_TIME=$(date +%s)
    npm run build
    END_TIME=$(date +%s)
    DURATION=$((END_TIME - START_TIME))
    echo "Build took ${DURATION} seconds"
    echo "BUILD_DURATION=$DURATION" >> $GITHUB_ENV

- name: Send metrics to DataDog
  env:
    DD_API_KEY: ${{ secrets.DATADOG_API_KEY }}
  run: |
    curl -X POST "https://api.datadoghq.com/api/v1/series" \
      -H "Content-Type: application/json" \
      -H "DD-API-KEY: ${DD_API_KEY}" \
      -d "{\"series\": [{\"metric\": \"github.build.duration\", \"points\": [[$(date +%s), ${BUILD_DURATION}]]}]}"


[OK] COST OPTIMIZATION


# === Économiser les minutes CI ===

# 1. Cache agressif
- uses: actions/cache@v4
  with:
    path: |
      ~/.cache/pip
      ~/.npm
      ~/.cargo
    key: ${{ runner.os }}-deps-${{ hashFiles('**/*.lock') }}

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

# 3. Conditional execution
on:
  push:
    branches: [main]
    paths-ignore:
      - '**.md'
      - 'docs/**'

# 4. Matrice réduite sur PR
strategy:
  matrix:
    python-version: ${{ github.event_name == 'push' && fromJSON('["3.9", "3.10", "3.11", "3.12"]') || fromJSON('["3.11"]') }}

# 5. Self-hosted runners pour projets intenses

# 6. Timeouts courts
timeout-minutes: 10

# 7. Skip CI commits
if: "!contains(github.event.head_commit.message, '[skip ci]')"


[OK] MIGRATION VERS GITHUB ACTIONS


# === Depuis Travis CI ===

# .travis.yml
language: python
python:
  - "3.9"
  - "3.10"
script:
  - pytest

# Équivalent GitHub Actions
name: CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ['3.9', '3.10']
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}
    - run: pip install -r requirements.txt
    - run: pytest


# === Depuis CircleCI ===

# .circleci/config.yml
version: 2.1
jobs:
  test:
    docker:
      - image: python:3.11
    steps:
      - checkout
      - run: pip install -r requirements.txt
      - run: pytest

# Équivalent GitHub Actions
name: CI

on: [push]

jobs:
  test:
    runs-on: ubuntu-latest
    container:
      image: python:3.11
    steps:
    - uses: actions/checkout@v4
    - run: pip install -r requirements.txt
    - run: pytest


# === Depuis GitLab CI ===

# .gitlab-ci.yml
test:
  image: python:3.11
  script:
    - pip install -r requirements.txt
    - pytest

# Équivalent GitHub Actions
name: CI

on: [push]

jobs:
  test:
    runs-on: ubuntu-latest
    container:
      image: python:3.11
    steps:
    - uses: actions/checkout@v4
    - run: pip install -r requirements.txt
    - run: pytest


[OK] TESTING WORKFLOWS LOCALEMENT


# === act - Run GitHub Actions locally ===

# Installation
# macOS
brew install act

# Linux
curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash

# Windows
choco install act-cli

# Usage
act                           # Exécute on: push
act pull_request              # Exécute on: pull_request
act -j test                   # Exécute job spécifique
act -l                        # Liste les workflows

# Avec secrets
act -s GITHUB_TOKEN=xxx

# Avec variables
act -var API_URL=http://localhost

# Spécifier runner
act -P ubuntu-latest=nektos/act-environments-ubuntu:18.04


[OK] GITHUB CLI INTEGRATION


# === Déclencher workflow ===
gh workflow run ci.yml

gh workflow run deploy.yml \
  -f environment=production \
  -f version=1.2.3

# === Lister workflows ===
gh workflow list

# === Voir runs ===
gh run list

gh run list --workflow=ci.yml

gh run list --status=failure

# === Voir run spécifique ===
gh run view 123456789

gh run view --log

# === Re-run workflow ===
gh run rerun 123456789

gh run rerun --failed

# === Annuler run ===
gh run cancel 123456789

# === Télécharger artifacts ===
gh run download 123456789

gh run download 123456789 -n artifact-name

# === Voir logs ===
gh run view --log

gh run view 123456789 --log-failed


[OK] BRANCH PROTECTION AVEC ACTIONS


# === Configuration recommandée ===
# Settings > Branches > Branch protection rules

# 1. Require status checks to pass before merging
#    [OK] CI / test
#    [OK] CI / lint
#    [OK] CI / security-scan

# 2. Require branches to be up to date before merging

# 3. Require pull request reviews before merging
#    - Required approvals: 1+

# 4. Require conversation resolution before merging

# === Status check workflow ===
name: Status Check

on:
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  status:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Check PR title
      run: |
        if [[ ! "${{ github.event.pull_request.title }}" =~ ^(feat|fix|docs|chore): ]]; then
          echo "PR title must start with feat:, fix:, docs:, or chore:"
          exit 1
        fi
    
    - name: Check PR description
      run: |
        if [ -z "${{ github.event.pull_request.body }}" ]; then
          echo "PR description cannot be empty"
          exit 1
        fi


[OK] LIMITATIONS & QUOTAS


# === Limites par workflow ===
- Max 1000 workflows par repository
- Max 256 jobs par workflow run
- Max 256 jobs en file d'attente ou running
- Max durée workflow: 35 jours
- Max durée job: 6 heures
- Max durée step: 6 heures (recommandé: timeout-minutes)
- API rate limit: 1000 requests/hour

# === Limites artifacts ===
- Max taille: 10 GB par workflow
- Max retention: 90 jours (configurable)
- Max uploads par workflow: Pas de limite stricte

# === Limites cache ===
- Max taille totale: 10 GB par repository
- Expiration: 7 jours (dernier accès)
- Max taille entrée: 10 GB

# === Limites matrices ===
- Max 256 jobs par matrice
- Max 256 combinaisons possibles

# === Minutes gratuites (privé) ===
# Free: 2000 min/mois
# Pro: 3000 min/mois
# Team: 3000 min/mois
# Enterprise: 50000 min/mois

# Multiplicateurs:
# Linux: 1x
# Windows: 2x
# macOS: 10x
# GPU: 50x


[OK] SNIPPETS UTILES


# === Obtenir version depuis package.json ===
- name: Get version
  id: version
  run: echo "value=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT


# === Obtenir version depuis pyproject.toml ===
- name: Get version
  id: version
  run: |
    VERSION=$(python -c "import tomli; print(tomli.load(open('pyproject.toml', 'rb'))['tool']['poetry']['version'])")
    echo "value=$VERSION" >> $GITHUB_OUTPUT


# === Vérifier si tag existe ===
- name: Check if tag exists
  id: tag-check
  run: |
    if git rev-parse "v${{ steps.version.outputs.value }}" >/dev/null 2>&1; then
      echo "exists=true" >> $GITHUB_OUTPUT
    else
      echo "exists=false" >> $GITHUB_OUTPUT
    fi


# === Obtenir PR number depuis commit ===
- name: Get PR number
  id: pr
  run: |
    PR_NUMBER=$(gh pr list --search "${{ github.sha }}" --json number --jq '.[0].number')
    echo "number=$PR_NUMBER" >> $GITHUB_OUTPUT
  env:
    GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}


# === Commenter sur PR ===
- name: Comment on PR
  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: '[OK] Deployment successful!'
      })


# === Créer issue automatiquement ===
- name: Create issue on failure
  if: failure()
  uses: actions/github-script@v7
  with:
    script: |
      await github.rest.issues.create({
        owner: context.repo.owner,
        repo: context.repo.repo,
        title: 'CI Failure: ${{ github.workflow }}',
        body: `Workflow failed: ${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
        labels: ['bug', 'ci']
      })


# === Merger PR automatiquement ===
- name: Auto-merge PR
  if: github.event.pull_request.user.login == 'dependabot[bot]'
  uses: actions/github-script@v7
  with:
    script: |
      await github.rest.pulls.merge({
        owner: context.repo.owner,
        repo: context.repo.repo,
        pull_number: context.issue.number,
        merge_method: 'squash'
      })


# === Extraire branch name ===
- name: Extract branch name
  id: branch
  run: |
    if [[ "${{ github.event_name }}" == "pull_request" ]]; then
      echo "name=${{ github.head_ref }}" >> $GITHUB_OUTPUT
    else
      echo "name=${GITHUB_REF#refs/heads/}" >> $GITHUB_OUTPUT
    fi


# === Générer changelog ===
- name: Generate changelog
  id: changelog
  run: |
    PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
    if [ -z "$PREV_TAG" ]; then
      CHANGELOG=$(git log --pretty=format:"- %s (%an)" HEAD)
    else
      CHANGELOG=$(git log --pretty=format:"- %s (%an)" $PREV_TAG..HEAD)
    fi
    echo "content<<EOF" >> $GITHUB_OUTPUT
    echo "$CHANGELOG" >> $GITHUB_OUTPUT
    echo "EOF" >> $GITHUB_OUTPUT


# === Comparer tailles de build ===
- name: Compare bundle sizes
  uses: actions/github-script@v7
  with:
    script: |
      const fs = require('fs');
      const currentSize = fs.statSync('dist/bundle.js').size;
      
      const comment = `[PACKAGE] Bundle size: ${(currentSize / 1024).toFixed(2)} KB`;
      
      await github.rest.issues.createComment({
        issue_number: context.issue.number,
        owner: context.repo.owner,
        repo: context.repo.repo,
        body: comment
      });


[OK] TEMPLATES & STARTER WORKFLOWS


# === Python Package Template ===
name: Python Package

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  release:
    types: [published]

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}
        cache: 'pip'
    
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -e ".[dev]"
    
    - name: Lint
      run: |
        flake8 src/ tests/
        black --check src/ tests/
        isort --check src/ tests/
    
    - name: Type check
      run: mypy src/
    
    - name: Test
      run: pytest --cov=src --cov-report=xml
    
    - name: Upload coverage
      if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11'
      uses: codecov/codecov-action@v4
      with:
        file: ./coverage.xml
  
  publish:
    needs: test
    if: github.event_name == 'release'
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'
    
    - name: Build package
      run: |
        pip install build
        python -m build
    
    - name: Publish to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1
      with:
        password: ${{ secrets.PYPI_API_TOKEN }}


# === Django Template ===
name: Django CI/CD

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

env:
  PYTHON_VERSION: '3.11'
  NODE_VERSION: '18'

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: ${{ env.PYTHON_VERSION }}
        cache: 'pip'
    
    - name: Install linting tools
      run: |
        pip install flake8 black isort pylint
    
    - name: Run linters
      run: |
        flake8 .
        black --check .
        isort --check .
        pylint **/*.py
  
  test:
    runs-on: ubuntu-latest
    
    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: testdb
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432
      
      redis:
        image: redis:7
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 10s
        ports:
          - 6379:6379
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: ${{ env.PYTHON_VERSION }}
        cache: 'pip'
    
    - name: Install dependencies
      run: |
        pip install -r requirements.txt
        pip install coverage
    
    - name: Run migrations
      env:
        DATABASE_URL: postgresql://postgres:postgres@localhost:5432/testdb
      run: python manage.py migrate
    
    - name: Run tests
      env:
        DATABASE_URL: postgresql://postgres:postgres@localhost:5432/testdb
        REDIS_URL: redis://localhost:6379
      run: |
        coverage run --source='.' manage.py test
        coverage report
        coverage xml
    
    - name: Upload coverage
      uses: codecov/codecov-action@v4
      with:
        file: ./coverage.xml
  
  security:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: ${{ env.PYTHON_VERSION }}
    
    - name: Security check
      run: |
        pip install bandit safety
        bandit -r .
        safety check
  
  deploy:
    needs: [lint, test, security]
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Deploy to production
      env:
        DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
        DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
      run: |
        echo "Deploying to production..."
        ./deploy.sh


[OK] CHECKLISTE WORKFLOW


# === Checklist complète ===

# [OK] Configuration de base
# [WHITE_SQUARE] Nom du workflow descriptif
# [WHITE_SQUARE] Événements déclencheurs appropriés
# [WHITE_SQUARE] Branches/paths configurés correctement
# [WHITE_SQUARE] Concurrency si nécessaire

# [OK] Jobs
# [WHITE_SQUARE] Runner approprié (ubuntu/windows/macos)
# [WHITE_SQUARE] Dépendances entre jobs configurées
# [WHITE_SQUARE] Timeouts définis
# [WHITE_SQUARE] Permissions minimales

# [OK] Steps
# [WHITE_SQUARE] Checkout en premier
# [WHITE_SQUARE] Setup language/tools
# [WHITE_SQUARE] Cache configuré
# [WHITE_SQUARE] Tests exécutés
# [WHITE_SQUARE] Linting/formatting vérifié
# [WHITE_SQUARE] Security scanning

# [OK] Sécurité
# [WHITE_SQUARE] Secrets utilisés pour données sensibles
# [WHITE_SQUARE] Pas de logs de secrets
# [WHITE_SQUARE] Permissions restrictives
# [WHITE_SQUARE] Dependabot activé
# [WHITE_SQUARE] CodeQL configuré

# [OK] Performance
# [WHITE_SQUARE] Cache activé
# [WHITE_SQUARE] Concurrency cancel-in-progress
# [WHITE_SQUARE] Jobs parallèles quand possible
# [WHITE_SQUARE] Paths-ignore pour fichiers inutiles
# [WHITE_SQUARE] Matrices optimisées

# [OK] Déploiement
# [WHITE_SQUARE] Environnements configurés
# [WHITE_SQUARE] Approbations si nécessaire
# [WHITE_SQUARE] Rollback prévu
# [WHITE_SQUARE] Monitoring configuré
# [WHITE_SQUARE] Notifications configurées

# [OK] Documentation
# [WHITE_SQUARE] README avec badges
# [WHITE_SQUARE] Commentaires dans workflow
# [WHITE_SQUARE] Variables documentées
# [WHITE_SQUARE] Secrets documentés


[OK] RESSOURCES & LIENS


# Documentation officielle
https://docs.github.com/en/actions

# Actions Marketplace
https://github.com/marketplace?type=actions

# GitHub Actions Runner
https://github.com/actions/runner

# GitHub Actions Toolkit
https://github.com/actions/toolkit

# Awesome Actions (liste communautaire)
https://github.com/sdras/awesome-actions

# GitHub Actions par GitHub
https://github.com/actions

# Act (run locally)
https://github.com/nektos/act

# Status page
https://www.githubstatus.com/

# Community forum
https://github.community/c/github-actions/

# Blog GitHub Actions
https://github.blog/tag/github-actions/

# Pricing
https://docs.github.com/en/billing/managing-billing-for-github-actions

# Workflow syntax
https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions

# Expressions
https://docs.github.com/en/actions/learn-github-actions/expressions

# Contexts
https://docs.github.com/en/actions/learn-github-actions/contexts

# Variables
https://docs.github.com/en/actions/learn-github-actions/variables

# Encrypted secrets
https://docs.github.com/en/actions/security-guides/encrypted-secrets


# === FIN DE LA CHEATSHEET ===/