Aller au contenu principal
Retour au blog

Architecture Symfony de A à Z — Partie 4 : CI/CD et déploiement

Flavien Métivier13 janvier 20268 min

Dernier volet de notre série Architecture Symfony. Après l'architecture hexagonale, les patterns CQRS et la sécurité, on passe au déploiement. Un code parfait qui ne se déploie pas de manière fiable ne sert à rien. Voyons comment construire un pipeline CI/CD complet pour Symfony avec GitHub Actions.

Les 5 étapes d'un pipeline Symfony robuste

Un pipeline CI/CD Symfony professionnel se décompose en cinq jobs ordonnés : lint, test, sécurité, mutation testing et déploiement. Chaque job agit comme un quality gate : si un seul échoue, le déploiement est bloqué.

  • Lint — PHP-CS-Fixer (style de code) + PHPStan (analyse statique niveau max)
  • Tests — PHPUnit avec couverture de code (unitaires + intégration + fonctionnels)
  • Sécurité — Composer audit (vulnérabilités des dépendances) + vérification des secrets
  • Mutation testing — Infection pour valider la qualité des tests eux-mêmes
  • Déploiement — Build et deploy uniquement si tous les gates sont passés

Le workflow GitHub Actions complet

Voici le fichier .github/workflows/ci.yml que nous utilisons en production. Il est conçu pour un projet Symfony avec PostgreSQL et Redis.

# .github/workflows/ci.yml
name: CI

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

jobs:
    code-quality:
        name: Code Quality (PHP ${{ matrix.php }})
        runs-on: ubuntu-latest
        strategy:
            fail-fast: false
            matrix:
                php: ['8.2', '8.3', '8.4']
        steps:
            - uses: actions/checkout@v4

            - name: Setup PHP
              uses: shivammathur/setup-php@v2
              with:
                  php-version: ${{ matrix.php }}
                  extensions: mbstring, xml, ctype, iconv, intl, pdo_pgsql
                  coverage: xdebug
                  tools: composer:v2

            - name: Cache Composer
              uses: actions/cache@v4
              with:
                  path: vendor
                  key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}

            - name: Install Dependencies
              run: composer install --no-interaction --prefer-dist

            - name: PHP CS Fixer
              run: vendor/bin/php-cs-fixer fix --dry-run --diff

            - name: PHPStan
              run: vendor/bin/phpstan analyse --level=max --memory-limit=1G

    security:
        name: Security Check
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v4
            - name: Setup PHP
              uses: shivammathur/setup-php@v2
              with:
                  php-version: '8.3'
            - name: Composer Audit
              run: composer audit
            - name: Security Checker
              uses: symfonycorp/security-checker-action@v5

    tests:
        name: Tests
        runs-on: ubuntu-latest
        services:
            postgres:
                image: postgres:16-alpine
                env:
                    POSTGRES_DB: test_db
                    POSTGRES_USER: test
                    POSTGRES_PASSWORD: test
                ports:
                    - 5432:5432
                options: >-
                    --health-cmd pg_isready
                    --health-interval 10s
                    --health-timeout 5s
                    --health-retries 5
            redis:
                image: redis:7-alpine
                ports:
                    - 6379:6379
        steps:
            - uses: actions/checkout@v4
            - name: Setup PHP
              uses: shivammathur/setup-php@v2
              with:
                  php-version: '8.3'
                  extensions: mbstring, xml, ctype, iconv, intl, pdo_pgsql, redis
                  coverage: xdebug
            - name: Install Dependencies
              run: composer install --no-interaction
            - name: Run Tests with Coverage
              run: XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-clover=var/coverage/clover.xml
              env:
                  DATABASE_URL: postgresql://test:test@127.0.0.1:5432/test_db
                  REDIS_URL: redis://127.0.0.1:6379
            - name: Upload Coverage
              uses: codecov/codecov-action@v4
              with:
                  files: ./var/coverage/clover.xml
                  fail_ci_if_error: true

    mutation:
        name: Mutation Testing
        runs-on: ubuntu-latest
        needs: tests
        steps:
            - uses: actions/checkout@v4
            - name: Setup PHP
              uses: shivammathur/setup-php@v2
              with:
                  php-version: '8.3'
                  coverage: xdebug
            - name: Install Dependencies
              run: composer install --no-interaction
            - name: Infection
              run: vendor/bin/infection --min-msi=80 --min-covered-msi=90

Les quality gates : vos gardiens automatiques

Un quality gate est une condition bloquante. Si elle n'est pas remplie, le code ne passe pas en production. Voici les gates recommandés pour un projet Symfony professionnel.

  • PHPStan niveau max — Zéro erreur d'analyse statique
  • PHP-CS-Fixer — Zéro violation de style de code
  • Tests — 100 % des tests passent
  • Couverture — Minimum 80 % de code coverage
  • Mutation Score (MSI) — Minimum 80 %, covered MSI minimum 90 %
  • Vulnérabilités — Zéro vulnérabilité connue dans les dépendances

Besoin d'un expert Symfony ?

Réserver un appel

Les services PostgreSQL et Redis dans le pipeline

GitHub Actions permet de lancer des services containers directement dans le job. C'est indispensable pour les tests d'intégration et fonctionnels qui nécessitent une vraie base de données.

Le healthcheck sur chaque service garantit que PostgreSQL et Redis sont prêts avant de lancer les tests. Sans ça, tu auras des échecs intermittents liés au timing de démarrage.

Le Makefile : l'interface unifiée

Le Makefile est le point d'entrée unique pour toutes les commandes qualité, que ce soit en local ou en CI. Voici les targets essentielles.

.PHONY: qa
qa: cs-check analyse test infection ## Toutes les vérifications qualité

.PHONY: cs-check
cs-check: ## Vérifie le code style
	@vendor/bin/php-cs-fixer fix --dry-run --diff

.PHONY: analyse
analyse: ## PHPStan + Psalm
	@vendor/bin/phpstan analyse --level=max --memory-limit=1G

.PHONY: test
test: ## Lance les tests PHPUnit
	@vendor/bin/phpunit

.PHONY: test-coverage
test-coverage: ## Tests avec couverture
	@XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-html=var/coverage

.PHONY: infection
infection: ## Mutation testing
	@vendor/bin/infection --min-msi=80 --min-covered-msi=90

Les métriques cibles

  • PHPStan — Level max, 0 erreur
  • PHP-CS-Fixer — 0 violation
  • Tests — 100 % passent
  • Coverage — >= 80 %
  • MSI (Mutation Score) — >= 80 %
  • Vulnérabilités — 0

Cette série Architecture Symfony touche à sa fin. De l'architecture hexagonale au pipeline CI/CD, tu as maintenant les fondations d'un projet Symfony professionnel. Si tu as un projet existant qui nécessite une mise à niveau, notre offre Bear Upgrade propose des audits et migrations automatisées. Réserve un appel pour en discuter.

Cet article vous a plu ? Partagez-le !

Besoin d'un expert Symfony ?

20 ans d'expérience sur l'écosystème PHP/Symfony.