Мінімальний CI для PHP-проєкту складається з чотирьох кроків. Встановлення залежностей: composer validate, composer install строго за lock-файлом з кешем vendor по хешу composer.lock, composer audit для відомих вразливостей. Перевірка стилю: Pint або PHP-CS-Fixer у режимі перевірки без правок. Статичний аналіз: PHPStan або Psalm на зафіксованому рівні, для легасі з baseline. Тести: PHPUnit або Pest на тій самій базі, що в продакшені, піднятій як service-контейнер, бо SQLite відрізняється в типах, JSON і ALTER TABLE.
Порядок визначає принцип fail fast. Лінтер і аналіз відпрацьовують за секунди, тому вони йдуть першими або паралельно, а хвилинні тести після. Збірка Docker-образу відбувається лише після зелених перевірок і лише з гілки, з якої деплоять. Образ тегується SHA коміту, щоб будь-який деплой можна було відкотити до конкретної збірки.
Коли пайплайн росте до 20 хвилин, лікують не вимкненням кроків, а вимірюванням. Зазвичай час іде на залежності без кешу й на послідовні тести: кеш шарів, паралельні джоби, шарди тестів через --parallel, транзакції замість міграцій з нуля в кожному тесті, і винесення важких інтеграційних сьютів на етап merge. Статичний аналіз при цьому не прибирають: він ловить помилки типів у коді, до якого тести не дійшли, і робить це за секунди.
# .github/workflows/ci.yml — мінімальний, але повний пайплайн
name: CI
on: [push, pull_request]
jobs:
static: # секунди: падаємо рано
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with: { php-version: '8.4', coverage: none }
- uses: actions/cache@v4
with: { path: vendor, key: composer-${{ hashFiles('composer.lock') }} }
- run: composer validate --strict && composer install --no-interaction --prefer-dist
- run: composer audit
- run: vendor/bin/pint --test
- run: vendor/bin/phpstan analyse --no-progress --memory-limit=1G
tests: # хвилини: реальна база, як у продакшені
runs-on: ubuntu-latest
needs: static
services:
postgres:
image: postgres:17
env: { POSTGRES_DB: app_test, POSTGRES_USER: app, POSTGRES_PASSWORD: secret }
options: --health-cmd pg_isready --health-interval 5s
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with: { php-version: '8.4', extensions: pdo_pgsql }
- uses: actions/cache@v4
with: { path: vendor, key: composer-${{ hashFiles('composer.lock') }} }
- run: composer install --no-interaction --prefer-dist
- run: vendor/bin/pest --parallel
env: { DB_CONNECTION: pgsql, DB_HOST: localhost, DB_DATABASE: app_test, DB_USERNAME: app, DB_PASSWORD: secret }
build: # лише після зелених перевірок
runs-on: ubuntu-latest
needs: tests
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- run: docker build -t registry.example.com/app:${{ github.sha }} .
Скажіть, який рівень PHPStan тримаєте і чому саме такий — це показує реальний досвід, а не список інструментів. Додайте, як розбили довгі тести на паралельні джоби.