Use continuous-integration.yml from both your pull request and default-branch
workflows. Pin the same reviewed commit in each caller. An application can use
lint and tests without enabling Composer package publication.
permissions: {}
jobs:
checks:
uses: hoverkraft-tech/ci-github-php/.github/workflows/continuous-integration.yml@<commit-sha>
permissions:
contents: read
id-token: writeDefine lint:ci and test:ci in composer.json. These are explicit project commands;
they execute even when dependency installation scripts are disabled. An empty
lint or test input disables that job; test: "false" also disables tests. Missing named scripts fail the job. lint also accepts a JSON object such as
lint: '{"command":"lint:custom"}', or "false" to skip linting.
Use runs-on for a JSON array of Linux runner labels, for example
runs-on: '["self-hosted", "linux"]'. Every job uses those labels.
Docker must be available for the PHP version discovery action.
Set checks: false to skip lint, tests, dependency review and distribution checks.
With no php-versions input, CI reads composer.json's require.php and
discovers released stable versions from PHP.net using Composer's constraint parser.
Discovery runs in its own container without changing the runner's PHP installation.
It tests the newest compatible patch of every matching minor, including end-of-life
versions still allowed by the project. Prereleases are excluded. Missing or invalid
constraints and release-catalog failures stop discovery instead of choosing an arbitrary runtime.
The highest discovered version runs lint and package checks and publishes coverage.
Set php-versions explicitly to override the matrix, for example '["8.2", "8.5"]'.
The last explicit entry is primary; an empty entry delegates to the setup action,
which honors .php-version and Composer platform overrides before automatic discovery.
See PHP version selection for local Docker builds.
Composer projects with or without a committed lockfile are supported. Each
project is installed independently. By default, setup installs the root manifest
and tools/composer.json when it exists. It does not search nested directories.
Use composer-directories to override discovery, for example ["."] for the root
only or [".", "dev-tools"] for another tooling directory. Only Composer's download cache is shared between
runs; dependencies are installed and platform requirements checked each time.
Installation scripts and plugins are disabled by default. Use
ignore-scripts: false when installation hooks are required. Use
ignore-plugins: false for a project with explicit Composer allow-plugins
configuration, such as PHPStan's extension installer. These options affect
installation, not explicit lint or test commands.
For custom jobs, call actions/setup-php after checkout, then the individual
lint or test actions. install-dependencies: false prepares the runtime
without installing the project, for example before isolated package checks.
The lint and test actions reuse ci-github-common/actions/quality/parse-ci-reports for
GitHub annotations and job summaries. The test action runs your Composer script unchanged. The script selects its test tool,
preparation, configuration and report formats. For PHPUnit, keep the everyday test
command and add a CI script:
{
"scripts": {
"test": "phpunit --configuration tests/phpunit.xml",
"test:ci": "@test --log-junit=junit.xml --coverage-cobertura=cobertura.xml"
}
}The action sets CI=true and adds no test-runner arguments. JUnit reports are
parsed even after the command fails. The same Composer command runs on every PHP
version; only the last version publishes coverage. The coverage option controls
publication, not collection. Set it to an empty string to skip coverage publication;
omit coverage arguments from your script when collection is not needed.
For Codecov, use --coverage-clover=clover.xml in the script and configure:
with:
test: '{"coverage":"codecov"}'Codecov requires id-token: write and repository access. The action discovers
Clover reports, falling back to Cobertura. For GitHub coverage summaries, generate
Cobertura. Add generated report files to .gitignore. Clean any retained report
files in your project command if it reuses an existing workspace.
The lint and test actions upload only parsed native reports, including after a
failed command, and retain them for seven days. Their report-artifact-id
output identifies the artifact; it is empty when no supported reports were
found. Test artifacts include coverage only with coverage: github.
Codecov upload remains separate. Console-only lint output creates no artifact;
configure a supported file report such as PHPStan Checkstyle for consolidation.
Individual comments are disabled by default, keeping producer jobs read-only.
When calling an action directly, set comment: true and grant
pull-requests: write to opt in. Human and bot actors follow the same rules;
fork PRs keep summaries, annotations and artifacts without commenting.
For custom producer jobs named lint and test, leave individual comments
disabled and add one final job using the common reporting workflow:
reports:
needs: [lint, test]
if: always()
uses: hoverkraft-tech/ci-github-common/.github/workflows/ci-reports.yml@d07ee49b460ae2f3815804b5153fe918092aa321 # 4.2.0
permissions:
contents: read
pull-requests: write
with:
job-results: ${{ toJSON(needs) }}This updates one comment each for tests, coverage and lint, with details
collapsed. Keep producer job IDs aligned with the shared reporter's contract;
when wrapping producers in reusable workflows, use matching caller and inner
job IDs. The full CI workflow uses inner lint and test jobs; use the
individual actions when you need to compose them with this final reporter.
Keep producer jobs as required checks: report publication does not replace them.
Preparation belongs in the Composer script, with report arguments attached only to the test command:
{
"scripts": {
"test:ci": [
"@test:load-fixtures",
"@test --log-junit=junit.xml --coverage-clover=clover.xml"
]
}
}Pest and Codeception projects use the same contract; define their commands and
report options in Composer. No test-runner selection is needed in the action.
Use command for a script name other than test:ci. Group custom settings in one
input, omitting fields that use defaults:
with:
test: '{"coverage":"codecov","cache":".cache"}'The test cache option can retain a directory of generated test fixtures or results. Do not
cache vendor; Composer installation manages dependencies.
Dependency review runs only on pull requests. PHP CodeQL analysis is not part of the workflow because PHP is not a supported CodeQL language. Run your PHP static analyzer through the lint script.
Projects needing asset compilation or generated code keep those steps in their Composer commands or dedicated caller jobs. PHP libraries need no generic build step; Composer distributes committed source selected by Git export rules.
For API documentation, use documentation.yml, with optional runs-on labels
for a Linux runner supporting Docker. It uses the project's phpDocumentor
configuration when present, otherwise src/, and uploads a site containing
phpdoc/. Run it during pull request validation and restore its artifact-id into
_site for a separate deployment job using ci-github-publish actions. Site
publishing and project-specific documentation transformations remain in the caller.
Set package: "true" for a public Composer package at the Git repository root. The
test action snapshots the checked-out commit using git archive, respecting
.gitattributes export-ignore and export-subst. Uncommitted files and generated
build output are not included.
The test action validates composer.json, verifies declared PSR autoload paths,
autoload files, and binaries, installs the archive in an isolated consumer with
production dependencies, checks platform requirements, and loads the autoloader.
Installation hooks and plugins remain disabled in that consumer.
Distribution testing is disabled by default. Omit package or pass "false" to skip it.
The input is a string, so boolean values must be quoted. A JSON object enables
distribution testing with specific options; unknown options and non-string tests fail
configuration validation.
Use tests only for behavior specific to your package, such as rendering a
template or invoking a CLI:
with:
package: '{"tests":"my-cli --help"}'Commands execute in the consumer, with vendor/bin
on PATH and PACKAGE_NAME, PACKAGE_INSTALL_PATH, and PACKAGE_ARCHIVE_PATH
available. The temporary consumer is removed after success or failure. Modifying
the temporary source archive fails validation. Nothing is uploaded: this tests
the files GitHub will distribute, without adding a package publication step.
Package checks run on the primary PHP version. The source test matrix remains
responsible for compatibility across every supported PHP version. Release callers should depend on successful validation with needs.
contents:readid-token:write
name: Continuous integration
on:
push:
branches:
- main
permissions: {}
jobs:
continuous-integration:
uses: hoverkraft-tech/ci-github-php/.github/workflows/continuous-integration.yml@7b1303edc25e97da2a297dd285a54cf4b6ae76a1 # 1.1.2
permissions:
contents: read
id-token: write
with:
# JSON array of Linux runner labels. Applies to every job, including the configuration job.
# Default: `["ubuntu-latest"]`
runs-on: '["ubuntu-latest"]'
# Enable lint, tests, dependency review and optional distribution checks.
# Default: `true`
checks: true
# Composer project directory relative to the checkout.
# Default: `.`
working-directory: .
# Optional JSON array overriding Composer-based PHP discovery. Empty discovers the newest stable compatible patch per minor. The last version runs lint/package checks and publishes coverage. An empty entry uses the setup action default.
php-versions: ""
# PHP extensions forwarded to setup-php.
# Default: `mbstring, dom, xml, xmlwriter, curl, zip, intl`
extensions: mbstring, dom, xml, xmlwriter, curl, zip, intl
# Optional JSON array of Composer directories relative to working-directory. Empty installs the root project and tools/composer.json when present, without recursive discovery.
composer-directories: ""
# Disable Composer installation scripts. Explicit project commands still run.
# Default: `true`
ignore-scripts: true
# Disable Composer plugins during installation. When false, Composer allow-plugins configuration still applies.
# Default: `true`
ignore-plugins: true
# Composer lint script name or a JSON object with `command` (default `lint:ci`). Pass "false", "null" or an empty string to disable.
# Default: `true`
lint: "true"
# Run the Composer `test:ci` script unchanged, or provide another script name.
# Pass "false", "null" or an empty string to disable tests.
# A JSON object groups test options: `command` (default `test:ci`),
# `coverage` (`github`, `codecov` or empty), and `cache` (one directory relative to working-directory).
# Configure report/coverage generation in the Composer script itself.
# Coverage is published on the last PHP version only; the command controls collection on every version. Codecov requires id-token: write.
# Example: `{"coverage":"codecov","cache":".cache"}`.
#
# Default: `true`
test: "true"
# Validate a public Composer source distribution from the Git repository root.
# Pass "false" (the default) to disable, "true" for archive and autoload checks,
# or a JSON object with `tests` for additional commands in an isolated Composer consumer.
# Quote booleans because this input has type string. Example: `{"tests":"my-cli --help"}`.
# vendor/bin is on PATH; PACKAGE_NAME, PACKAGE_INSTALL_PATH and PACKAGE_ARCHIVE_PATH identify the package.
# Unknown options and non-string tests are rejected before distribution checks.
#
# Default: `false`
package: "false"
# Review dependency changes on pull requests.
# Default: `true`
dependency-review: true| Input | Description | Required | Type | Default |
|---|---|---|---|---|
runs-on |
JSON array of Linux runner labels. Applies to every job, including the configuration job. | false | string | ["ubuntu-latest"] |
checks |
Enable lint, tests, dependency review and optional distribution checks. | false | boolean | true |
working-directory |
Composer project directory relative to the checkout. | false | string | . |
php-versions |
Optional JSON array overriding Composer-based PHP discovery. Empty discovers the newest stable compatible patch per minor. The last version runs lint/package checks and publishes coverage. An empty entry uses the setup action default. | false | string | - |
extensions |
PHP extensions forwarded to setup-php. | false | string | mbstring, dom, xml, xmlwriter, curl, zip, intl |
composer-directories |
Optional JSON array of Composer directories relative to working-directory. Empty installs the root project and tools/composer.json when present, without recursive discovery. | false | string | - |
ignore-scripts |
Disable Composer installation scripts. Explicit project commands still run. | false | boolean | true |
ignore-plugins |
Disable Composer plugins during installation. When false, Composer allow-plugins configuration still applies. | false | boolean | true |
lint |
Composer lint script name or a JSON object with command (default lint:ci). Pass "false", "null" or an empty string to disable. |
false | string | true |
test |
Run the Composer test:ci script unchanged, or provide another script name. |
false | string | true |
| Pass "false", "null" or an empty string to disable tests. | ||||
A JSON object groups test options: command (default test:ci), |
||||
coverage (github, codecov or empty), and cache (one directory relative to working-directory). |
||||
| Configure report/coverage generation in the Composer script itself. | ||||
| Coverage is published on the last PHP version only; the command controls collection on every version. Codecov requires id-token: write. | ||||
Example: {"coverage":"codecov","cache":".cache"}. |
||||
package |
Validate a public Composer source distribution from the Git repository root. | false | string | false |
| Pass "false" (the default) to disable, "true" for archive and autoload checks, | ||||
or a JSON object with tests for additional commands in an isolated Composer consumer. |
||||
Quote booleans because this input has type string. Example: {"tests":"my-cli --help"}. |
||||
| vendor/bin is on PATH; PACKAGE_NAME, PACKAGE_INSTALL_PATH and PACKAGE_ARCHIVE_PATH identify the package. | ||||
| Unknown options and non-string tests are rejected before distribution checks. | ||||
dependency-review |
Review dependency changes on pull requests. | false | boolean | true |
Contributions are welcome! Please see the contributing guidelines for more details.
This project is licensed under the MIT License.
SPDX-License-Identifier: MIT
Copyright © 2026 hoverkraft-tech
For more details, see the license.
This documentation was automatically generated by CI Dokumentor.