Зачем вам CI/CD, если вы не робот?
Представьте: пятница, вечер, вы только что допилили фичу, заливаете код на сервер через FTP (да, такое ещё бывает), и… сайт падает с белым экраном смерти. Знакомо? А если бы у вас был CI/CD pipeline для PHP, всё произошло бы автоматически: тесты отвалились бы ещё на этапе пуша, код не улетел бы на прод, а вы спокойно пошли бы пить чай.
Continuous Integration и Continuous Delivery (или Deployment) — это не просто модные аббревиатуры. Это ваш личный ассистент, который проверяет код, прогоняет тесты, линтит, собирает артефакты и деплоит на сервер. В этой статье я покажу, как настроить CI/CD pipeline для PHP с нуля, используя GitHub Actions. Без абстракций — только реальный код и рабочие примеры.
Что такое CI/CD pipeline и почему PHP-разработчику это нужно
CI/CD pipeline — это последовательность автоматических шагов, которые выполняются каждый раз, когда вы пушите код в репозиторий. Для PHP это особенно актуально, потому что:
- PHP — динамический язык, и многие ошибки (опечатки, неверные типы) вылезают только в рантайме.
- Деплой часто делают «на коленке» — через SCP, rsync или даже FTP (крик души).
- Команды растут, и ручные проверки становятся узким горлышком.
Настроив CI/CD для PHP, вы получаете:
- Автоматическое тестирование при каждом пуше.
- Проверку стиля кода (PSR-12, ваш внутренний стандарт).
- Автоматический деплой на staging/production.
- Уведомления в Telegram/Slack о статусе сборки.
Выбираем инструмент: почему GitHub Actions?
Есть много вариантов: Jenkins, GitLab CI, Bitbucket Pipelines, CircleCI. Но для настройки CI/CD pipeline для PHP с нуля я рекомендую GitHub Actions. Почему?
- Бесплатно для публичных репозиториев (и 2000 минут в месяц для приватных — хватает за глаза).
- Всё в одном месте — код, issues, actions.
- Огромная экосистема готовых действий (actions) — не нужно изобретать велосипед.
Но если вы используете GitLab — принципы те же, просто синтаксис YAML другой. Поехали.
Шаг 1: Структура репозитория и базовые файлы
Для демонстрации возьмём типичный PHP-проект на Laravel (но подход работает для любого фреймворка). Ваша структура должна выглядеть так:
my-php-app/
├── .github/
│ └── workflows/
│ └── ci-cd.yml
├── src/
├── tests/
├── composer.json
├── phpunit.xml
└── .php_cs.dist (если используете PHP-CS-Fixer)
Создайте папку .github/workflows/ и файл ci-cd.yml — это сердце вашего пайплайна.
Шаг 2: Базовый YAML — непрерывная интеграция (CI)
Начнём с простого: прогон тестов и линтинг при каждом пуше в main или pull request. Вот минимальный CI/CD pipeline для PHP:
name: PHP CI/CD Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
extensions: mbstring, pdo, sqlite, gd
coverage: xdebug
- name: Install dependencies
run: composer install --prefer-dist --no-progress
- name: Run PHP CS Fixer (Lint)
run: vendor/bin/php-cs-fixer fix --dry-run --diff
- name: Run PHPStan (Static Analysis)
run: vendor/bin/phpstan analyse src --level=5
- name: Run Tests
run: vendor/bin/phpunit --coverage-text
Что здесь происходит?
- checkout — забираем код.
- setup-php — устанавливаем нужную версию PHP и расширения.
- composer install — ставим зависимости.
- php-cs-fixer — проверяем код на соответствие стандартам (без исправления, только dry-run).
- phpstan — статический анализ (ловит ошибки типов, неиспользуемые переменные).
- phpunit — юнит-тесты с отчётом о покрытии.
Это классический CI/CD для PHP на минималках. После пуша GitHub сам запустит эти шаги. Если тесты упадут — в PR появится красная метка.
Шаг 3: Добавляем деплой (CD) — Continuous Delivery
Теперь самое интересное — автоматический деплой. Допустим, у вас есть сервер на Ubuntu с PHP и Nginx. Мы будем деплоить через SSH с помощью rsync (быстро и безопасно).
Сначала добавьте секреты в GitHub: Settings → Secrets and variables → Actions. Создайте:
SSH_PRIVATE_KEY— ваш приватный ключ (без пароля).SSH_HOST— IP сервера.SSH_USER— пользователь (обычноdeployилиwww-data).
Теперь дополните ваш ci-cd.yml новым job'ом:
deploy:
needs: build-and-test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' # только для main
steps:
- uses: actions/checkout@v4
- name: Setup SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts
- name: Deploy via rsync
run: |
rsync -avz --exclude '.git' --exclude 'vendor' --exclude 'tests' \
--delete ./ ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:/var/www/my-app/
- name: Run post-deploy commands
run: |
ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "
cd /var/www/my-app &&
php artisan migrate --force &&
php artisan cache:clear &&
php artisan config:cache
"
Важно: needs: build-and-test гарантирует, что деплой запустится только после успешных тестов. Никакого «я задеплоил, а оно не работает».
Если вы используете Docker, вместо rsync можно собрать