michaelmoussa/php-coverage-checker
A PHP tool that reads Clover-format code coverage XML and fails the build when coverage drops below a configured threshold. Drop it into CI to enforce minimum coverage on every run.
This package is a single-purpose CLI gate: it reads a Clover-style XML coverage report and exits non-zero if line coverage falls below a threshold. It does not generate coverage — that is still PHPUnit/Pest + Xdebug or PCOV's job.
Minimal path from zero:
composer require --dev michaelmoussa/php-coverage-checker
Then produce a report and check it, in that order:
# 1. Generate Clover XML (PHPUnit)
XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-clover build/logs/clover.xml
# 2. Enforce a floor
vendor/bin/php-coverage-checker build/logs/clover.xml 80
That is genuinely the whole API surface: <clover.xml> <minimum-percentage>. Exit code 0 = at or above threshold, non-zero = below. Run vendor/bin/php-coverage-checker --help on the version you install before scripting against optional flags — the last release is from 2015, so argument/option details vary between tags.
Where to look first:
vendor/michaelmoussa/php-coverage-checker/README.md — the examples are the de-facto spec.src/ — it is a handful of small classes; if you need to change behaviour (different metric, rounding), you will be reading these, not configuring them.phpunit.xml — confirm <coverage> / the CLI flag actually writes Clover, and that the path matches what you pass the checker.First real use case: a single CI job that fails the build on a coverage regression, with the threshold committed in the repo rather than typed ad hoc.
Composer script as the single entry point. Keep the threshold in composer.json so local and CI runs are identical:
{
"scripts": {
"test:coverage": [
"XDEBUG_MODE=coverage phpunit --coverage-clover build/logs/clover.xml",
"php-coverage-checker build/logs/clover.xml 80"
]
}
}
Composer aborts the script chain on the first non-zero exit, so you get fail-fast for free.
CI gate (GitHub Actions shape):
- run: composer install --prefer-dist --no-progress
- run: composer test:coverage
Useful variants: pin the exact tool version (composer require --dev michaelmoussa/php-coverage-checker:1.0.3) so a CI image refresh cannot silently change the metric; upload clover.xml as an artifact on failure so you can diff it locally.
Ratchet pattern. The tool only knows a number, so encode progress in the pipeline:
max(baseline, absolute_floor).Makefile variable COVERAGE_MIN ?= 80 that you bump manually each release, checked into the repo and reviewed like code.Multiple gates from multiple reports. The checker evaluates one file at a time, which is actually convenient: run PHPUnit per test suite (--testsuite=unit, --testsuite=integration), emit separate Clovers, and call the checker once per suite with a per-suite floor (e.g. 90 for pure unit tests, 50 for integration). This is the main reason to prefer it over a single aggregate number.
Structured output for dashboards. The command can emit a machine-readable summary of per-file coverage in addition to the pass/fail exit code (verify the option name against --help on your tag). If present, use it to post a PR comment or push a series to your metrics store rather than scraping stdout, which is human-formatted and unstable across versions.
Wrap it, don't fork it. For anything beyond pass/fail, write a thin bin/coverage-gate that calls the checker, captures the exit code, and prints a message with the actual percentage and the required one. Keeps the package as a dumb comparator and puts your policy (branch exemptions, [skip coverage] labels in the PR body, docs-only change detection) in your own code.
composer install may fail on platform requirements or emit deprecations; --ignore-platform-reqs is a workaround, not a fix. For a new project, prefer a maintained equivalent (rregeer/phpunit-coverage-check is the common spiritual successor) or a two-line script that reads metrics/@line-rate from the Clover XML and compares it yourself — the tool is small enough that reimplementing it is often cheaper than keeping it alive.--coverage-text stdout. If your toolchain (Pest, ParaTest, infection) writes a different format, you must still get PHPUnit to write Clover somewhere.metrics attributes, typically line coverage. Your browser HTML report surfaces classes/methods/branches alongside it. Expect the two numbers to differ and do not file a bug about it; decide which one you actually want to gate on.clover.xml as a hard failure in CI, not as a pass. Always generate the report before the check in the same script step and clean build/logs first so a stale file from a previous run can't produce a false green.XDEBUG_MODE=coverage you get a valid-looking report where everything is 0% — or no report at all. PCOV users need pcov.enabled=1. This is the single most common cause of "the gate is failing and I don't know why."working-directory:, Docker WORKDIR, and Composer script cwd can all differ. Use an absolute path (${{ github.workspace }}/build/logs/clover.xml) in CI if you see "file not found" only in pipelines.set -e in a bash script, the non-zero exit kills your step — intended, but it also means any post-processing (uploading artifacts, PR comments) must be wired to run before the check or with if: always().path repository) or your own wrapper. Given 24 stars and an archived repo, plan on owning the fork if you keep it.How can I help you explore Laravel packages today?