GitHub Actions Intermédiaire

Maîtriser les Workflows Avancés : Automatisation Intelligente avec GitHub Actions

Dépassez les bases et explorez les patterns professionnels pour construire des pipelines CI/CD robustes et maintenables. Découvrez comment optimiser vos workflows pour la scalabilité en environnement d'entreprise.

Preparetoi.academy 30 min

Architectures de Workflows Réutilisables et Composables

La création de workflows réutilisables est une pratique fondamentale dans les organisations modernes utilisant GitHub Actions. Un workflow réutilisable est essentiellement un modèle paramétrable que plusieurs projets peuvent utiliser sans dupliquer le code. Cette approche suit le principe DRY (Don't Repeat Yourself) et facilite la maintenance centralisée des processus CI/CD.

Les workflows réutilisables permettent de définir une logique commune une seule fois et de la partager across multiple repositories. Vous créez un fichier de workflow dans le dossier .github/workflows/ avec le trigger workflow_call, puis d'autres workflows peuvent l'invoquer via uses. Cette architecture est particulièrement utile pour les organisations avec plusieurs équipes et projets similaires.

Définition : Un workflow réutilisable est un fichier YAML paramétré stocké dans un repository qui peut être appelé par d'autres workflows via l'événement workflow_call, créant ainsi une couche d'abstraction centralisée pour les processus CI/CD.

Explication : Imaginez une organisation avec 50 microservices Node.js. Au lieu de dupliquer les étapes de test et build dans chaque repository, vous créez un workflow réutilisable appelé test-and-build.yml dans un repository central. Tous les microservices l'appellent en passant simplement des paramètres spécifiques. Cela réduit la maintenance et assure la cohérence.

# .github/workflows/reusable-test-build.yml
name: Test and Build Reusable Workflow

on:
  workflow_call:
    inputs:
      node-version:
        required: false
        type: string
        default: '18'
      registry-url:
        required: true
        type: string
      package-manager:
        required: false
        type: string
        default: 'npm'
    secrets:
      npm-token:
        required: true
      docker-username:
        required: true
      docker-password:
        required: true

jobs:
  test-and-build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node-version }}
          registry-url: ${{ inputs.registry-url }}

      - name: Cache dependencies
        uses: actions/cache@v3
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: Install dependencies
        run: ${{ inputs.package-manager }} ci

      - name: Run tests with coverage
        run: ${{ inputs.package-manager }} run test:coverage
        env:
          NODE_ENV: test

      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v3
        with:
          files: ./coverage/coverage-final.json
          flags: unittests

      - name: Build application
        run: ${{ inputs.package-manager }} run build

      - name: Login to Docker Registry
        uses: docker/login-action@v2
        with:
          registry: ${{ inputs.registry-url }}
          username: ${{ secrets.docker-username }}
          password: ${{ secrets.docker-password }}

      - name: Build and push Docker image
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: ${{ inputs.registry-url }}/app:${{ github.sha }}

# .github/workflows/ci.yml (consommateur du workflow réutilisable)
name: CI Pipeline

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

jobs:
  quality-checks:
    uses: ./.github/workflows/reusable-test-build.yml
    with:
      node-version: '18'
      registry-url: 'ghcr.io'
      package-manager: 'npm'
    secrets:
      npm-token: ${{ secrets.NPM_TOKEN }}
      docker-username: ${{ secrets.DOCKER_USERNAME }}
      docker-password: ${{ secrets.DOCKER_PASSWORD }}
Aspect Avantage Cas d'usage
Centralisation Maintenance unique, cohérence garantie Organisations multi-équipes
Paramétrage Flexibilité adaptée à chaque projet Ecosystèmes technologiques variés
Réutilisabilité Économie de temps, réduction de bugs Nombreux repositories similaires
Versioning Contrôle des versions du workflow Évolution progressive des outils
Encapsulation Abstraction des détails techniques Équipes avec niveaux d'expérience différents

Astuce : Versionnez vos workflows réutilisables avec des tags Git. Par exemple, uses: organization/reusable-workflows/.github/workflows/test.yml@v1.0.0 permet une évolution contrôlée sans casser les workflows existants. Maintenez une documentation claire des inputs et secrets requis.

⚠️ Attention : Les workflows réutilisables héritent du contexte du repository appelant, pas du repository contenant le workflow. Les chemins de fichiers (.github/secrets.json) sont relatifs au repository appelant. Testez toujours dans un environment de test avant production. Les secrets ne sont pas automatiquement transmis ; vous devez les passer explicitement pour chaque secret.


Gestion Avancée des Secrets et Variables d'Environnement

La gestion sécurisée des données sensibles est critique dans les pipelines CI/CD. GitHub Actions propose plusieurs mécanismes pour gérer les secrets, variables et configurations sans exposer d'informations confidentielles dans les logs ou le code source. Une stratégie robuste combine les secrets, les variables d'environnement et les configurations externe.

Les secrets sont chiffrés et disponibles uniquement au runtime. Les variables, introduites pour les données non-sensibles, offrent une alternative plus performante et visible. Les variables d'environnement job peuvent être définies à plusieurs niveaux : workflow, job ou step. La hiérarchie et le scoping sont essentiels pour une architecture maintainable.

Définition : La gestion des secrets et variables est un système hiérarchique de stockage et de transmission de données sensibles (credentials, tokens) et non-sensibles (URL, version) dans les workflows GitHub Actions, en respectant les principes de least privilege et de chiffrement.

Explication : Considérez une application déployée en production. Elle nécessite une clé API AWS, un token Docker Hub, une URL de base de données chiffrée, et une version d'application. Les 3 premiers sont des secrets (chiffrés), la version est une variable. GitHub Actions permet de définir ces éléments à différents niveaux : organization (partagé), repository, environment (production/staging différencié), ou step spécifique.

# .github/workflows/advanced-secrets-management.yml
name: Advanced Secrets & Variables Management

on:
  push:
    branches: [main]
  workflow_dispatch:
    inputs:
      environment:
        description: 'Deployment environment'
        required: true
        type: choice
        options:
          - staging
          - production

env:
  # Variables globales du workflow (non-sensibles)
  APP_VERSION: ${{ github.ref_name }}-${{ github.run_number }}
  LOG_LEVEL: info
  REGISTRY: ghcr.io

jobs:
  security-scan:
    runs-on: ubuntu-latest
    environment:
      name: ${{ github.event.inputs.environment || 'staging' }}
      url: https://${{ github.event.inputs.environment || 'staging' }}.example.com
    
    env:
      # Variables spécifiques au job
      CI_COMMIT_SHA: ${{ github.sha }}
      RUNNER_DEBUG: 0
    
    steps:
      - uses: actions/checkout@v4

      - name: Setup security scanner
        env:
          # Variables spécifiques au step
          SCANNER_CONFIG: /tmp/config.yaml
          TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db
        run: |
          echo "Initializing security scan"
          echo "Environment: ${{ vars.ENVIRONMENT_NAME }}"
          echo "Version: $APP_VERSION"

      - name: Scan Docker image with Trivy
        env:
          # Utilisation de secrets avec variable interpolation
          DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
          REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
        run: |
          # Les secrets ne s'affichent JAMAIS dans les logs
          docker login -u $DOCKER_USERNAME -p $REGISTRY_TOKEN $REGISTRY
          
          trivy image \
            --severity CRITICAL,HIGH \
            --format json \
            --output trivy-report.json \
            $REGISTRY/myapp:$APP_VERSION

      - name: Upload security report
        uses: actions/upload-artifact@v3
        if: always()
        with:
          name: security-reports
          path: trivy-report.json

      - name: Notify security team
        if: failure()
        env:
          # Secrets pour notification
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_SECURITY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          # Utiliser des variables masquées pour les URL sensibles
          curl -X POST $SLACK_WEBHOOK \
            -d '{
              "text": "Security scan failed on '${{ github.ref }}'"
            }'

  deploy:
    needs: security-scan
    runs-on: ubuntu-latest
    environment:
      name: ${{ github.event.inputs.environment || 'staging' }}
      url: https://${{ github.event.inputs.environment || 'staging' }}.example.com
    
    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
          aws-region: ${{ vars.AWS_REGION }}
          role-session-name: github-actions-${{ github.run_id }}

      - name: Deploy to CloudFormation
        env:
          STACK_NAME: app-${{ vars.ENVIRONMENT_NAME }}
          ARTIFACT_BUCKET: ${{ vars.S3_ARTIFACT_BUCKET }}
          DOCKER_IMAGE: ${{ env.REGISTRY }}/myapp:${{ env.APP_VERSION }}
        run: |
          aws s3 cp ./dist s3://$ARTIFACT_BUCKET/$APP_VERSION/ --recursive
          
          aws cloudformation deploy \
            --template-file infrastructure/template.yaml \
            --stack-name $STACK_NAME \
            --parameter-overrides \
              ImageUri=$DOCKER_IMAGE \
              EnvironmentName=${{ vars.ENVIRONMENT_NAME }} \
            --capabilities CAPABILITY_IAM

      - name: Health check
        run: |
          for i in {1..30}; do
            if curl -f https://${{ github.event.inputs.environment || 'staging' }}.example.com/health; then
              echo "Deployment successful"
              exit 0
            fi
            echo "Waiting... attempt $i/30"
            sleep 10
          done
          exit 1

# Fichier .github/workflows/secrets-rotation.yml
# Workflow de rotation automatique des secrets
name: Secrets Rotation Audit

on:
  schedule:
    - cron: '0 0 1 * *'  # Premier du mois
  workflow_dispatch:

jobs:
  audit-secrets:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write
    
    steps:
      - uses: actions/checkout@v4

      - name: List organization secrets (audit only)
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          echo "Checking for secrets last updated more than 90 days ago"
          # Audit des secrets via GitHub API
          gh api repos/${{ github.repository }}/actions/secrets \
            --method GET \
            --paginate \
            --jq '.secrets[] | select(.updated_at < (now - 90*86400 | todate)) | {name: .name, updated_at: .updated_at}'
Niveau Scope Visibilité Cas d'usage Risque
Organization Tous les repos Admin seulement Credentials transversaux Accès large, difficile à auditer
Repository Ce repo Collaborateurs Tokens spécifiques au projet Partage accidentel
Environment Production/Staging Équipe déployante Config par environnement Isolation insuffisante
Step Une étape Logs masqués Données ultra-sensibles Exposition en debug
Variables Tous niveaux Public Non-sensible (URL, version) Pas de chiffrement

Astuce : Utilisez les "environment secrets" pour différencier production et staging. GitHub masquera automatiquement les secrets dans les logs (même les valeurs de variables qui contiendraient le secret). Pour les mots de passe complexes, utilisez openssl rand -base64 32 pour générer des secrets forts. Auditez régulièrement qui a accès aux secrets via les "Repository Settings" → "Secrets and variables".

⚠️ Attention : Les secrets ne sont JAMAIS affichés dans les logs, mais les variables interpolées peuvent l'être. JAMAIS d'écho de variables contenant des secrets : echo ${{ secrets.TOKEN }} est dangereux même sans affichage direct. Les secrets restent disponibles même dans les forks pour les pull requests de propriétaires (risque d'exfiltration). Utilisez permissions: pour limiter l'accès du token par défaut. Les secrets ne sont pas transmis automatiquement aux workflows réutilisables ; vous devez les passer explicitement.


Optimisation des Performances et Caching Stratégique

La performance des workflows CI/CD impacte directement la productivité des développeurs. Chaque minute d'attente répétée représente des coûts et une friction. L'optimisation passe par trois piliers : le caching intelligent, le parallelization des jobs, et la sélection judicieuse des runners. GitHub Actions propose des mécanismes de cache sophistiqués pour réduire les temps de build.

Le caching s'applique à plusieurs niveaux : dépendances npm/pip/maven, build artifacts, images Docker, et même résultats d'analyse statique. La clé de cache doit être granulaire pour éviter les faux positifs (utiliser hashFiles() sur les fichiers de dépendances). La stratégie de parallélisation utilise les matrices pour créer automatiquement plusieurs jobs variant sur des paramètres (versions de Node, OS, configurations).

Définition : L'optimisation des performances en GitHub Actions combine le caching distribué (réutilisation d'artifacts entre runs), la parallelization via matrices de jobs, et l'utilisation stratégique de self-hosted runners pour les tâches coûteuses.

Explication : Une application TypeScript avec 500 dépendances npm peut prendre 3 minutes à installer à chaque run. Avec le caching, la première installation prend 3 minutes, les suivantes 30 secondes (si package-lock.json n'a pas changé). Pour une équipe de 20 développeurs faisant 5 commits/jour, cela représente 40 minutes économisées quotidiennement. Les matrices permettent de tester simultanément sur Node 18, 20, 22 et LTS plutôt que séquentiellement.

# .github/workflows/performance-optimized.yml
name: Optimized Performance Pipeline

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

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true  # Annuler les anciens runs

jobs:
  # Job léger pour détecter les changements
  detect-changes:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}
      has-deps-changes: ${{ steps.changes.outputs.dependencies }}
      has-code-changes: ${{ steps.changes.outputs.code }}
    
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Detect file changes
        id: changes
        run: |
          if git diff ${{ github.event.before }} HEAD -- package*.json yarn.lock; then
            echo "dependencies=true" >> $GITHUB_OUTPUT
          else
            echo "dependencies=false" >> $GITHUB_OUTPUT
          fi
          
          if git diff ${{ github.event.before }} HEAD -- 'src/**' 'tests/**'; then
            echo "code=true" >> $GITHUB_OUTPUT
          else
            echo "code=false" >> $GITHUB_OUTPUT
          fi

      - name: Set test matrix
        id: set-matrix
        run: |
          # Réduire la matrice sur PR vs push
          if [[ "${{ github.event_name }}" == "pull_request" ]]; then
            echo 'matrix={"node-version": ["18", "20"]}' >> $GITHUB_OUTPUT
          else
            echo 'matrix={"node-version": ["18", "20", "22"]}' >> $GITHUB_OUTPUT
          fi

  test-matrix:
    needs: detect-changes
    if: needs.detect-changes.outputs.has-code-changes == 'true'
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix: ${{ fromJson(needs.detect-changes.outputs.matrix) }}
        os: [ubuntu-latest, macos-latest]
    
    timeout-minutes: 30
    
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          
      # Cache avec clé granulaire basée sur hashFiles
      - name: Cache npm dependencies
        uses: actions/cache@v3
        id: npm-cache
        with:
          path: ~/.npm
          # Clé unique pour chaque configuration Node + lock file
          key: ${{ runner.os }}-npm-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-npm-${{ matrix.node-version }}-
            ${{ runner.os }}-npm-

      - name: Install dependencies
        if: steps.npm-cache.outputs.cache-hit != 'true'
        run: npm ci --prefer-offline --no-audit
        timeout-minutes: 10

      # Cache des artifacts de build
      - name: Cache build artifacts
        uses: actions/cache@v3
        with:
          path: |
            dist/
            .next/
            build/
          key: ${{ runner.os }}-build-${{ matrix.node-version }}-${{ github.sha }}
          restore-keys: |
            ${{ runner.os }}-build-${{ matrix.node-version }}-

      - name: Run linter with cache
        run: npm run lint -- --cache --cache-location .eslintcache

      - name: Build application
        run: npm run build

      # Cache des résultats de test pour analyse incrémentale
      - name: Cache test results
        uses: actions/cache@v3
        with:
          path: .jest-cache
          key: ${{ runner.os }}-jest-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}

      - name: Run tests in parallel
        run: npm run test -- --maxWorkers=4 --coverage

      - name: Upload coverage (only on latest Node)
        if: matrix.node-version == '20'
        uses: codecov/codecov-action@v3
        with:
          files: ./coverage/lcov.info
          flags: unittests-${{ runner.os }}

  # Job lourd sur runner self-hosted
  performance-tests:
    runs-on: self-hosted  # ou ubuntu-latest-large
    timeout-minutes: 45
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    
    steps:
      - uses: actions/checkout@v4

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

      - name: Cache dependencies on self-hosted
        uses: actions/cache@v3
        with:
          path: ~/.npm
          key: self-hosted-npm-${{ hashFiles('**/package-lock.json') }}
          restore-keys: self-hosted-npm-

      - name: Install dependencies
        run: npm ci --prefer-offline

      - name: Run performance benchmarks
        run: npm run benchmark

      - name: Generate performance report
        run: npm run benchmark:report

      - name: Comment PR with performance impact
        if: github.event_name == 'pull_request'
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const report = fs.readFileSync('perf-report.json', 'utf8');
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: '## Performance Benchmark\n```json\n' + report + '\n```'
            });

  # Job de regroupement
  all-checks:
    runs-on: ubuntu-latest
    needs: [detect-changes, test-matrix]
    if: always()
    
    steps:
      - name: Check test results
        run: |
          if [[ "${{ needs.test-matrix.result }}" == "failure" ]]; then
            echo "Tests failed"
            exit 1
          fi
          echo "All checks passed"
Stratégie Impact Complexité Quand l'utiliser
Caching npm -80% temps install Basse Toujours
Caching build -60% temps build Moyenne Gros projets (>100MB)
Matrices parallèles N×temps (N jobs) Moyenne Tests multi-version
Self-hosted runners -70% pour tâches GPU Haute ML/image processing
Détection changements -90% inutile runs Moyenne Monorepos ou repos volumineux

Astuce : Utilisez concurrency.cancel-in-progress: true pour annuler les anciens runs quand un nouveau push arrive sur la même branche. Les runners GitHub-hosted sont gratuits jusqu'à 3000 minutes/mois pour les organisations privées. Pour les workflows de build longs (>30 min), un self-hosted runner améliore les temps de 50-70%. Mesurez avec workflow.run_duration dans GitHub API pour identifier les goulots.

⚠️ Attention : Le cache est par branch et varie selon le runner OS. Un cache de ubuntu-latest n'est pas utilisable sur macos-latest. Si vous changez de runner type, le cache ne sera pas restauré. Les clés de cache distinguent majeure/mineure : key: v1-cache et restore-keys: v1- retrouvera les caches v1-* mais pas v2-*. Limiter les tailles de cache (max 5GB par repo) ; les anciens caches sont purgés après 7 jours d'inutilisation. Ne cachez jamais les secrets ou credentials.


Stratégies de Déploiement et Gestion des Environnements

Le déploiement en production requiert une orchestration précise, des validations multiples et une gestion des risques. GitHub Actions supporte plusieurs stratégies de déploiement : blue-green, canary, rolling deployments. Chaque stratégie a des compromis en termes de downtime, complexité et vitesse de rollback. La gestion des environnements permet de définir des approbations, des variables spécifiques et des protections par branch.

Les "Deployment Environments" de GitHub Actions offrent un contrôle d'accès granulaire (qui peut déployer en production), des variables spécifiques par environnement, et des exigences de review. Les "Deployment Protection Rules" permettent de bloquer les déploiements selon des conditions (seuil de couverture de tests, passage des checks). L'intégration avec les systèmes de monitoring (DataDog, New Relic) permet de valider les déploiements post-livraison.

Définition : La gestion des environnements est un système d'orchestration de déploiements multi-étapes avec approbations, validations progressives et protections contre les erreurs, intégrant des stratégies comme blue-green ou canary pour minimiser les risques de downtime.

Explication : Une équipe déploie une nouvelle version. Au lieu d'aller directement en production, le workflow suit : build → tests unitaires → déployer en staging → tests d'intégration → approuver manuellement → déployer en production canary (10% du trafic) → monitorer → si OK, 100%. Si une métrique critique (error rate) dépasse un seuil, rollback automatique. Ceci exige une configuration d'environnements, des checks de validation et l'intégration avec la métrologie.

# .github/workflows/advanced-deployment.yml
name: Advanced Multi-Environment Deployment

on:
  push:
    branches: [main, develop]
    tags: ['v*']
  workflow_dispatch:
    inputs:
      environment:
        description: 'Target environment'
        required: true
        type: choice
        options: [staging, production]
      deployment-strategy:
        description: 'Deployment strategy'
        required: true
        type: choice
        options: [rolling, blue-green, canary]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  # Phase 1: Build et validation initiale
  build-and-validate:
    runs-on: ubuntu-latest
    outputs:
      image-tag: ${{ steps.image.outputs.tag }}
      image-digest: ${{ steps.image.outputs.digest }}
    
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

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

      - name: Log in to registry
        uses: docker/login-action@v2
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push Docker image
        id: image
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: |
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Run Trivy security scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
          format: 'sarif'
          output: 'trivy-results.sarif'

      - name: Upload security results
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: 'trivy-results.sarif'

  # Phase 2: Déploiement en staging (automatique)
  deploy-staging:
    needs: build-and-validate
    runs-on: ubuntu-latest
    environment:
      name: staging
      url: https://staging.example.com
    
    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          role-to-assume: ${{ secrets.AWS_STAGING_ROLE }}
          aws-region: ${{ vars.AWS_REGION }}

      - name: Update ECS service for staging
        run: |
          aws ecs update-service \
            --cluster staging-cluster \
            --service app-service \
            --force-new-deployment \
            --region ${{ vars.AWS_REGION }}

      - name: Wait for staging deployment
        run: |
          aws ecs wait services-stable \
            --cluster staging-cluster \
            --services app-service \
            --region ${{ vars.AWS_REGION }}

      - name: Run smoke tests
        env:
          STAGING_URL: https://staging.example.com
        run: |
          npm install -g newman
          newman run tests/postman-collection.json \
            --environment tests/staging-env.json \
            --reporters cli,json \
            --reporter-json-export results.json

  # Phase 3: Tests d'intégration en staging
  integration-tests:
    needs: deploy-staging
    runs-on: ubuntu-latest
    timeout-minutes: 30
    
    steps:
      - uses: actions/checkout@v4

      - name: Setup test environment
        run: |
          npm ci
          npm run test:integration -- --env=staging

      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: integration-test-results
          path: test-results/

  # Phase 4: Approval avant production
  approval-production:
    needs: [build-and-validate, integration-tests]
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://example.com
    
    steps:
      - name: Await approval
        run: echo "Approval received. Proceeding to production deployment."

  # Phase 5: Déploiement en production (stratégie configurable)
  deploy-production:
    needs: [approval-production, build-and-validate]
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://example.com
    
    strategy:
      max-parallel: 1  # Un déploiement à la fois
    
    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          role-to-assume: ${{ secrets.AWS_PRODUCTION_ROLE }}
          aws-region: ${{ vars.AWS_REGION }}
          role-session-name: github-deployment-${{ github.run_id }}

      - name: Determine deployment strategy
        id: strategy
        run: |
          STRATEGY="${{ github.event.inputs.deployment-strategy }}"
          if [[ -z "$STRATEGY" ]]; then
            STRATEGY="rolling"
          fi
          echo "strategy=$STRATEGY" >> $GITHUB_OUTPUT

      - name: Deploy with Blue-Green strategy
        if: steps.strategy.outputs.strategy == 'blue-green'
        env:
          IMAGE_TAG: ${{ needs.build-and-validate.outputs.image-tag }}
        run: |
          # Créer l'environnement "Green" (nouveau
Accédez à des centaines d'examens QCM — Découvrir les offres Premium