Appendix: Continuous Integration¶
This project’s CI runs entirely on GitHub Actions — no separate CI
service to configure. A reference map of what runs on every push/PR
and what it checks, for the CI configuration itself, not the
framework’s own code. Five workflow files, .github/workflows/:
ci.yml, semgrep.yml, integration.yml, infection.yml,
sonarqube.yml, plus .github/dependabot.yml.
ci.yml — static checks and unit tests, per package¶
One job per package (core plus every satellite package, 14 in total), each running against the exact same Docker images used for local development:
composer validate --strictcomposer installcomposer audit— checks every installed dependency against the FriendsOfPHP security advisory database.PHPUnit — every package’s own existing, fake-backed unit test suite. Skipped for
kinetis/skeleton, which has none by design (see Appendix: Satellite Packages).PHPStan, level 8.
Psalm,
--taint-analysis— data-flow analysis for injection-style bugs (SQL injection, XSS, …), a different lens than PHPStan’s type-correctness checking. Results upload to GitHub’s code-scanning tab as SARIF.
A separate job builds the Sphinx documentation with -W (warnings fail
the build), so a broken docs page can’t merge silently.
semgrep.yml — pattern-based security scanning¶
p/php, p/security-audit, and p/secrets rulesets, scanning the
whole repository once (not per package — Semgrep doesn’t need to know
about Composer package boundaries). Same SARIF upload as Psalm’s.
Two findings are suppressed in .semgrepignore, with the reasoning
recorded there:
docs/_templates/page.html— mirrors the Furo Sphinx theme’s own footer template verbatim; the flagged template variables are Sphinx-internal, build-time navigation values, never live request input, since this renders to static HTML.packages/auth-jwt/tests/JwtAuthMiddlewareTest.php— a synthetic RSA key pair generated solely to test RS256 JWT verification, not a real credential.
integration.yml — real backends, not fakes¶
Several classes across this project are tested only against real
service containers, not mocks — a mocked “was this method called with
X” test can’t prove backend-specific correctness (a reliable queue’s
ack/release mechanics, FOR UPDATE SKIP LOCKED, priority-queue
fallthrough). Each is a standalone PHP script (tests-integration/run.php,
or a descriptively-named file at the relevant package root), not a
disguised PHPUnit test.
query-builder(MySQL 8.4, MariaDB 11.4, Postgres 16) —Query::get()/first()/count()/insertGetId()/update()/delete()/join()/paginate()/cursorPaginate().queue(Redis 7, MySQL 8.4, MariaDB 11.4) —RedisQueue/SqlQueue: push/pop/ack/release/fail, attempts, priority queues.core(MySQL 8.4, MariaDB 11.4, Postgres 16, Redis 7) —TransactionGuard: commit/rollback/rollbackDangling();RedisSimpleCache: the full PSR-16 surface, TTL expiry.mailer(Mailpit) —MailerFactory: a real SMTP send, read back through the mail server’s own API.migrations(MySQL 8.4, MariaDB 11.4, Postgres 16) —MigrationRunner/SqlMigrationRepository: migrate/status/rollback against a real fixture migration file.localstack(LocalStack: SQS + S3) —SqsQueue: push/pop/ack/ release/fail,maxAttempts, priority queues;S3FilesystemFactory: write/read/exists/list/delete.
query-builder, queue, core, and migrations each run twice — once
against MySQL, once against MariaDB — via a matrix over the database
image, not separate jobs or duplicated scripts. Only the service
container’s image and health-check command (mysqladmin vs.
mariadb-admin) differ between the two matrix entries.
infection.yml — mutation testing¶
Mutates source code (flipping a comparison, removing a statement, incrementing a constant, …) and re-runs the covering tests per mutant — a mutant the suite doesn’t catch (“escaped”) is a gap in assertion rigor, not just a coverage gap.
One matrix job per package, core plus every satellite package except
kinetis/skeleton (no PHPUnit tests, nothing to mutate against). Each:
composer install, then Infection with PCOV as the coverage driver,
informational only (--min-msi=0 --min-covered-msi=0, not gating the
build on a mutation-score threshold).
queue/queue-sqs/queue-rabbitmq/storage-s3 score lower here:
their real backend-specific logic (RedisQueue/SqlQueue/SqsQueue/
RabbitMqQueue) has no PHPUnit coverage (see Appendix: Satellite Packages),
so Infection there only scores the thin config-parsing factory classes
that are unit-tested.
sonarqube.yml — SonarQube Cloud¶
A repo-wide static-analysis and coverage pass via the official
SonarSource/sonarqube-scan-action, configured by
sonar-project.properties at the repo root (source paths spanning
core’s src/ plus every satellite package’s own src/).
Runs PHPUnit with PCOV coverage for core and every satellite package
with a PHPUnit suite, feeding the resulting Clover reports into the
scan via sonar.php.coverage.reportPaths. RedisQueue, SqlQueue,
SqsQueue (and its SqsQueueException), and RabbitMqQueue are
excluded from the coverage calculation via sonar.coverage.exclusions,
matching their real-backend-only testing in integration.yml.
Requires a SONAR_TOKEN repository secret from the project’s own
SonarCloud dashboard; analysis method is “With GitHub Actions.”
dependabot.yml¶
One composer ecosystem entry per package directory, plus one
github-actions entry for the workflow files themselves. A dependency
bump opens a real pull request, which re-runs every workflow above
against it before a human ever looks at it.
See also¶
Appendix: System Layout — the same reference map for core, by namespace.
Appendix: Satellite Packages — the same reference map for every satellite package, by namespace.
Testing —
TestClient, for exercising aKernelend-to-end in a consumer’s own test suite.