# 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 --strict` - `composer install` - `composer 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 {doc}`appendix-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 {doc}`appendix-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 - {doc}`appendix` — the same reference map for core, by namespace. - {doc}`appendix-packages` — the same reference map for every satellite package, by namespace. - {doc}`testing` — `TestClient`, for exercising a `Kernel` end-to-end in a consumer's own test suite.