CI/CD для PHP проектов: GitHub Actions продвинутый уровень

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: от базового CI/CD к продакшен-уровню

Базовый пайплайн CI/CD, который просто запускает тесты, — это хорошо. Но для серьезного PHP-проекта этого недостаточно. В реальной разработке требуются кэширование зависимостей для ускорения сборок, параллельные задачи, матричные сборки для разных версий PHP, статический анализ и, конечно, автоматический деплой. В этой статье мы разберем продвинутые техники настройки GitHub Actions для PHP-проектов на практических примерах.

Мы предполагаем, что вы уже знакомы с основами YAML-синтаксиса и базовой структурой .github/workflows. Теперь перейдем к оптимизации и расширению возможностей.

1. Ускорение сборок: кэширование Composer и оптимальная стратегия

Каждый запуск workflow скачивает зависимости заново. Это занимает 1-3 минуты, которые можно сократить до 5-10 секунд с помощью кэширования.

Интеллектуальное кэширование vendor

Используем actions/cache с ключом на основе хеша composer.lock. Если файл блокировки не изменился — зависимости восстановятся из кэша.

name: PHP CI
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          tools: composer:v2

      - name: Cache Composer dependencies
        uses: actions/cache@v3
        with:
          path: vendor
          key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
          restore-keys: |
            ${{ runner.os }}-php-

      - name: Install dependencies
        run: composer install --no-progress --prefer-dist

Обратите внимание на restore-keys: если точный ключ не найден, будет использован последний кэш для этой ОС. Это страхует при смене веток.

Матричная сборка для разных версий PHP

Чтобы гарантировать совместимость, тестируйте на нескольких версиях PHP параллельно:

jobs:
  test:
    strategy:
      matrix:
        php-version: ['8.1', '8.2', '8.3']
        dependency-stability: [prefer-lowest, prefer-stable]

    steps:
      - uses: actions/checkout@v4
      - name: Setup PHP ${{ matrix.php-version }}
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php-version }}

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

      - name: Install dependencies
        run: |
          composer update --${{ matrix.dependency-stability }} --prefer-dist --no-interaction

      - name: Run tests
        run: vendor/bin/phpunit

Этот подход выявляет проблемы совместимости ещё до мержа в основную ветку. Вы получите 6 параллельных джоб (3 версии × 2 стратегии стабильности).

2. Продвинутые этапы: статический анализ и качество кода

Юнит-тесты — не единственный инструмент контроля качества. Добавим PHPStan (максимальный уровень анализа) и PHP Code Sniffer.

Линтинг и статический анализ

Создайте отдельную джобу для анализа, которая выполняется параллельно с тестами:

jobs:
  static-analysis:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          coverage: none

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

      - run: composer install --no-interaction --prefer-dist

      - name: PHPStan
        run: vendor/bin/phpstan analyse --level=max src/

      - name: PHPCS
        run: vendor/bin/phpcs --standard=PSR12 src/

Для ускорения можно кэшировать результаты PHPStan с помощью phpstan-cache в отдельном пути кэша.

Отчеты о покрытии кода

Генерация и публикация отчета о покрытии с помощью Codecov или Coveralls:

      - name: Run tests with coverage
        run: vendor/bin/phpunit --coverage-clover coverage.xml

      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v3
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: coverage.xml

3. Автоматический деплой: SSH, Docker и Zero Downtime

После успешного прохождения всех тестов и анализа — деплой. Рассмотрим два популярных сценария.

Сценарий A: Деплой через SSH (традиционный)

Используем appleboy/ssh-action для выполнения команд на сервере:

deploy:
  needs: [test, static-analysis]
  if: github.ref == 'refs/heads/main'
  runs-on: ubuntu-latest

  steps:
    - uses: actions/checkout@v4

    - name: Deploy via SSH
      uses: appleboy/ssh-action@v1.0.3
      with:
        host: ${{ secrets.DEPLOY_HOST }}
        username: ${{ secrets.DEPLOY_USER }}
        key: ${{ secrets.DEPLOY_KEY }}
        script: |
          cd /var/www/project
          git pull origin main
          composer install --no-dev --optimize-autoloader
          php artisan migrate --force
          php artisan optimize
          sudo systemctl reload php8.2-fpm

Важно: используйте needs, чтобы деплой запускался только после успешных тестов. Условие if: github.ref == 'refs/heads/main' защищает от случайного деплоя из feature-веток.

Сценарий B: Docker-образ и Zero Downtime

Для микросервисов или контейнеризированных приложений:

deploy-docker:
  needs: [test]
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4

    - name: Build Docker image
      run: |
        docker build -t myapp/php-app:${{ github.sha }} .
        docker tag myapp/php-app:${{ github.sha }} myapp/php-app:latest

    - name: Push to registry
      run: |
        echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin                

Блоги

Рекомендаций по книгам