contenir / contenir-qa-tools
Shared QA toolchain for Contenir components: Mago formatting, linting and static analysis, plus the PHPUnit baseline and CI workflow. Forked from php-db/phpdb-qa-tools.
Requires
- php: ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0
Requires (Dev)
None
Suggests
- phpunit/phpunit: To run the shared test configuration (^11.5 || ^12.0)
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-08 02:52:25 UTC
README
Shared Mago + PHPUnit configuration and reusable CI workflow for Contenir components.
Fork of php-db/phpdb-qa-tools
This repository is a fork of php-db/phpdb-qa-tools. The Mago base configuration and the PHPUnit baseline are kept in step with upstream; the reusable CI workflow carries Contenir-specific changes:
- No AI attributions — an extra
attributionsjob fails the build when a pull request title, description or commit message carries an AI attribution, or a commit's author or committer is an AI identity. - Codecov failures fail the build —
fail_ci_if_error: true, because Contenir packages are held at full coverage and a silent upload failure would hide a regression. apt-packagesinput — installs Ubuntu packages before thetestandmutation-testjobs, for system tools the tests shell out to (e.g.imagemagick).- Pinned runners — every job runs on
ubuntu-24.04rather thanubuntu-latest. - Pinned Mago — the
mago-versioninput (default1.52.0) fixes the Mago release CI installs, instead of whatever setup-php resolves as latest. - Codecov and mutation testing on by default —
enable-codecovandenable-infectiondefault totrue. A package with no executable code opts out by setting them tofalse. - Diff-only mutation testing on pull requests —
infection-diff-on-pull-requests: truemutates only the lines a pull request changes; pushes to release branches still mutate everything.
Upstream changes are merged in from the upstream remote:
git remote add upstream https://github.com/php-db/phpdb-qa-tools.git git fetch upstream git merge upstream/0.1.x
Prerequisite: install Mago
Mago is a self-contained static binary and is not delivered through Composer. Install it once per machine:
curl --proto '=https' --tlsv1.2 -sSf https://carthage.software/mago.sh | bash # or brew install mago # or cargo install mago
The shared configuration pins the expected Mago version, so a stale or too-new binary is flagged immediately.
Installation
composer require --dev contenir/contenir-qa-tools
Usage
1. Mago
Create a mago.toml in your repository root that extends the shared base and
adds only the project-specific facts. Contenir components keep their suites under
tests/, while the shared base assumes test/, so the test-path rules are
overridden locally:
extends = "vendor/contenir/contenir-qa-tools/mago.toml" php-version = "8.3.0" [source] paths = ["src", "tests"] includes = ["vendor"] [formatter] # Keep `(new Foo())->bar()`: CI formats under each job's PHP version, and the # unparenthesised form PHP 8.4+ allows does not parse on 8.3, the minimum. parentheses-around-new-in-member-access = true [linter.rules] too-many-methods = { exclude = ["tests/"] } [analyzer] excludes = ["tests"]
Merge semantics: nested tables merge deeply, arrays concatenate (parent first), and child scalars win — so you can tighten or relax individual rules locally without forking the whole standard.
2. PHPUnit
Copy the strict baseline into your repository (PHPUnit has no config inheritance):
cp vendor/contenir/contenir-qa-tools/templates/phpunit.xml.dist .
The template's suites point at test/unit and test/integration. Contenir components
use tests/Unit and tests/Integration with suites named unit and integration, so
adjust the <testsuites> block after copying.
3. Composer scripts
Add the standard scripts to your composer.json:
{
"scripts": {
"check": ["@cs-check", "@static-analysis", "@test", "@test-integration"],
"cs-check": ["mago format --check", "mago lint"],
"cs-fix": ["mago format", "mago lint --fix"],
"static-analysis": "mago analyze",
"test": "phpunit --colors=always --testsuite unit",
"test-integration": "phpunit --colors=always --testsuite integration",
"test-coverage": "phpunit --colors=always --coverage-clover clover.xml",
"mutation-test": "infection"
}
}
4. CI
This repository ships a reusable CI workflow
(.github/workflows/continuous-integration.yml)
with six jobs: attributions (no AI attributions), mago (format/lint/analyze/guard,
optional Rector), test (unit + optional integration, across a
php x [lowest, locked, latest] matrix), an optional composer job (validate/audit),
and two downstream jobs, codecov and mutation-test, both gated on test succeeding
and both on by default. A consuming library's entire CI file becomes:
# .github/workflows/continuous-integration.yml name: "Continuous Integration" on: push: pull_request: jobs: qa: uses: contenir/contenir-qa-tools/.github/workflows/continuous-integration.yml@0.1.x secrets: inherit with: php-versions: '["8.3", "8.4", "8.5"]' run-integration: true coverage-php-version: "8.4" min-msi: "100" min-covered-msi: "100" # Only when the tests shell out to system tools. apt-packages: "imagemagick"
Mutation testing needs infection/infection in require-dev, the mutation-test script
above and an infection.json5.dist:
composer require --dev infection/infection
cp vendor/contenir/contenir-qa-tools/templates/infection.json5.dist .
An application tests only its lock file and usually needs extensions, a .env and
sometimes a private dependency:
jobs: qa: uses: contenir/contenir-qa-tools/.github/workflows/continuous-integration.yml@0.1.x secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} # Read-only deploy key for a private VCS dependency. SSH_PRIVATE_KEY: ${{ secrets.PRIVATE_DEPENDENCY_DEPLOY_KEY }} with: php-versions: '["8.3"]' dependency-versions: '["locked"]' php-extensions: "intl, pdo_mysql, gd" dotenv: | APP_ENV=testing enable-rector: true enable-composer-audit: true # Codecov and mutation testing are on by default. Remove this line once # the application runs Infection. enable-infection: false
Mago version
CI installs the Mago release named by the mago-version input (default 1.52.0, the
version the #:schema line in the shared mago.toml points at). Mago releases change
formatter output and analyzer findings, so an unpinned install would break
mago format --check, mago lint and mago analyze in every consumer without a code
change. Use an exact release tag: setup-php silently falls back to the latest release
when the tag doesn't exist.
Bumping the pin, whether the default here or a consumer's own mago-version, is a
breaking change for consumers. Each one has to reformat and regenerate its baselines
with the new binary before its CI goes green again (the baseline commands write to the
baseline paths set in its mago.toml):
mago format mago lint --generate-baseline mago analyze --generate-baseline
Install the same version locally so composer cs-check matches CI.
Every input carries a description in the workflow file. See Workflow architecture for the full input list, the job graph, the DB-service mechanics, and the Codecov/Infection secrets wiring.
Documentation
- Migration guide — moving a Contenir repository onto the shared toolchain.
- Rule rationale — why the non-default choices are what they are.
- Workflow architecture — job-split design for DB-backed integration tests, Codecov, and Infection.
- Auto-dev — reusable workflow that triages issues and turns accepted ones into draft PRs.
- llms.txt — condensed setup facts for coding agents.
License
BSD-3-Clause. See LICENSE.