Code Quality Gates: Laravel Pint and PHPStan in CI

작성자

카테고리:

← 피드로
DEV Community · Dinesh Wijethunga · 2026-07-25 개발(SW)
Cover image for Code Quality Gates: Laravel Pint and PHPStan in CI

Dinesh Wijethunga

The moment you add a code quality gate to CI, something quietly changes on your team. Developers stop arguing about code style in reviews because the robot handles it. Static-analysis errors stop reaching production because they can’t pass the gate. You stop inheriting other people’s technical debt because new errors are blocked immediately, even if old ones exist.

That last point is the PHPStan baseline trick. We’ll get to it.

The Code Quality Job

This job runs on every push and is intentionally fast — no database, no migrations, just PHP and a vendor folder:

  quality:
    name: Code Quality
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup PHP 8.4
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.4'
          extensions: mbstring, pdo, bcmath, zip, intl
          coverage: none
          tools: composer:v2

      - name: Cache Composer dependencies
        uses: actions/cache@v4
        with:
          path: api/vendor
          key: php-8.4-composer-${{ hashFiles('api/composer.lock') }}
          restore-keys: php-8.4-composer-

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

      - name: Check formatting (Laravel Pint)
        run: ./vendor/bin/pint --test

      - name: Static analysis (PHPStan)
        run: ./vendor/bin/phpstan analyse --memory-limit=2G --no-progress

pint --test runs in read-only mode — it checks without modifying files. Exit code 1 if any file has style drift. Run ./vendor/bin/pint locally (without --test) to auto-fix before pushing.

Configuring PHPStan

Add a phpstan.neon to your API root:

parameters:
    level: 8
    paths:
        - app
        - Modules
    excludePaths:
        - vendor
    ignoreErrors:
        - identifier: missingType.iterableValue
        - identifier: missingType.generics

Level 8 is strict. On a fresh project, aim for it from day one. On an existing codebase, start at level 4 and raise it gradually.

The Memory Problem

PHPStan at level 8 on a large codebase needs more than 512MB of RAM. Use --memory-limit=2G in CI. Locally, add a shortcut to composer.json:

"scripts": {
    "analyse": "php -d memory_limit=2G vendor/bin/phpstan analyse --no-progress"
}

The Baseline Trick for Legacy Codebases

Here’s the situation I encounter on almost every inherited project: PHPStan level 8 reports 2,600 existing errors. You can’t fix them all in one PR. But you also can’t ignore level 8 — you need it to catch new errors.

The solution is a baseline file. You generate it once. It records all current errors as “accepted.” From that point, any new error fails CI, but existing ones are silenced:

# Run once locally, commit the result
php -d memory_limit=2G vendor/bin/phpstan analyse --generate-baseline phpstan-baseline.neon

Then include it in phpstan.neon:

includes:
    - phpstan-baseline.neon

parameters:
    level: 8
    paths:
        - app
        - Modules

Commit phpstan-baseline.neon. From now on: CI passes on existing debt, fails on new debt. As you fix old errors, re-generate the baseline with fewer entries. Eventually you won’t need it.

Ordering Jobs: Quality Before Tests

Use needs: to enforce ordering:

  tests:
    name: Tests (PHP 8.4, MySQL 8.4)
    needs: quality   # this line
    runs-on: ubuntu-latest

Now the test job only starts if quality passed. This saves CI minutes — there’s no point running a 3-minute test suite against code that won’t pass style checks.

Developer Workflow With These Gates

The new routine before any push:

# Fix style issues automatically
./vendor/bin/pint

# Check for type errors
php -d memory_limit=2G vendor/bin/phpstan analyse --no-progress

# Run tests locally
php artisan test

# Now push — CI will agree
git push origin feature/my-feature

After a few weeks this becomes muscle memory. The quality gate stops being something that catches you and starts being something you pre-empt.

In the next post we cover secrets and environment variables — the right way to handle API keys, database passwords, and OAuth keys in CI without ever putting sensitive values in your YAML files.

Originally published at dineshstack.com — read the full version with code samples and updates there.

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다