GitHub Actions for PHP
debt(d7/e3/b3/t3)
Closest to 'only careful code review or runtime testing' (d7). The detection_hints list github-actions and act as tools, but missing composer cache, absent matrix testing, unpinned actions, and secrets in repo are not flagged automatically by default — they require code review of the workflow YAML or noticing slow CI runtimes. Act (local runner) can surface some issues but won't catch all misconfigurations without deliberate inspection.
Closest to 'simple parameterised fix' (e3). The quick_fix is caching vendor/ with actions/cache — a small YAML addition. Other common mistakes (pinning versions, adding --no-dev, moving secrets) are similarly small, targeted changes within the workflow file(s). No cross-cutting refactor needed, but more than a single-line patch since multiple workflow adjustments may be needed.
Closest to 'localised tax' (b3). The CI workflow applies to the repository's devops layer and affects PR feedback loops, but the burden is contained — it doesn't shape application architecture or require every future maintainer to work around it constantly. A poorly configured workflow slows CI but doesn't spread into application code.
Closest to 'minor surprise' (t3). The misconception is that GitHub Actions only works for open source — a relatively benign confusion about availability rather than a dangerous behavioral gotcha. The common mistakes (missing cache, unpinned actions, --no-dev) are typical CI oversights but well-documented and not contradictory to reasonable assumptions about how CI pipelines work.
Also Known As
TL;DR
Explanation
GitHub Actions workflows are YAML files in .github/workflows/. Key steps for PHP: set up PHP version with shivammathur/setup-php (supports extensions, ini settings), cache Composer dependencies, run PHPUnit, PHPStan/Psalm, PHP_CodeSniffer, and deploy on merge to main. Matrix builds test against multiple PHP versions simultaneously. Secrets are stored in GitHub's encrypted secrets store and injected as environment variables.
Diagram
flowchart LR
PUSH[git push] --> TRIGGER[GitHub Actions<br/>workflow triggered]
TRIGGER --> MATRIX[Matrix: PHP 8.1 8.2 8.3]
MATRIX --> STEPS[Steps per version]
subgraph Pipeline
STEPS --> COMPOSER[composer install]
COMPOSER --> LINT[php-cs-fixer --dry-run]
LINT --> STAN[phpstan analyse]
STAN --> TEST[phpunit --coverage]
TEST --> REPORT[Coverage report]
end
REPORT -->|all pass| MERGE[Merge allowed]
LINT & STAN & TEST -->|fail| BLOCK[PR blocked]
style MERGE fill:#238636,color:#fff
style BLOCK fill:#f85149,color:#fff
Common Misconception
Why It Matters
Common Mistakes
- Not caching Composer dependencies — re-downloading vendor/ on every run adds 30-60 seconds unnecessarily.
- Running composer install instead of composer install --no-dev in deployment jobs — dev dependencies slow deployment and increase attack surface.
- Not pinning action versions — actions/checkout@v4 not actions/checkout@main — unpinned actions can change behaviour.
- Secrets checked into the repository instead of using GitHub Secrets — exposes credentials in git history.
Code Examples
# No caching, missing --no-dev, unpinned actions:
name: CI
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main # Unpinned
- run: composer install # No cache, includes dev deps
- run: vendor/bin/phpunit
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with: { php-version: '8.3', coverage: xdebug }
- uses: actions/cache@v4
with:
path: vendor
key: composer-${{ hashFiles('composer.lock') }}
- run: composer install --prefer-dist --no-interaction
- run: vendor/bin/phpstan analyse
- run: vendor/bin/phpunit --coverage-text