Search by

contenir / contenir-qa-tools

peptolab

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.

Package info

github.com/contenir/contenir-qa-tools

pkg:composer/contenir/contenir-qa-tools

Statistics

Installs: 7 546

Dependents: 55

Suggesters: 0

Stars: 0

v0.2.0 2026-10-07 03:00 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 attributions job 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-packages input — installs Ubuntu packages before the test and mutation-test jobs, for system tools the tests shell out to (e.g. imagemagick).
  • Pinned runners — every job runs on ubuntu-24.04 rather than ubuntu-latest.
  • Pinned Mago — the mago-version input (default 1.52.0) fixes the Mago release CI installs, instead of whatever setup-php resolves as latest.
  • Codecov and mutation testing on by default — enable-codecov and enable-infection default to true. A package with no executable code opts out by setting them to false.
  • Diff-only mutation testing on pull requests — infection-diff-on-pull-requests: true mutates 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.