Running the backport builder in a release pipeline
Build tooling, not a runtime dependency. This page explains how to operate the release pipeline that produces the backport. The pipeline runs in continuous integration (CI); it never runs in a downstream application.
At a glance
Section titled “At a glance”The production path uses two workflows. 0-ci.yml gates every change on each permanent branch. A source release triggers build.yml, which builds and releases the distribution. Both workflows are verified against .github/workflows/.
The CI gate
Section titled “The CI gate”0-ci.yml runs on push and pull request events for PHP74 and PHP81. It runs three jobs in order:
- PHPStan (Build Tools) —
composer analyse, level 10 forrector/rulesandscripts. - PHPUnit (Rector Rules) —
composer test, the fixture suites for the three custom rules. - Build Dry Run —
composer build:dry, gated behind the first two jobs.
Trusted events use self-hosted PHP runners. Fork pull requests and Dependabot use GitHub-hosted runners with PHP 8.5 provisioned. This is verified against 0-ci.yml (the runs-on expression and the conditional setup-php step). The dual-branch model gates changes that touch both targets independently on each branch’s pull request.
The release pipeline
Section titled “The release pipeline”build.yml is the production build. It runs on a repository_dispatch event of type source-release, or manually through workflow_dispatch with a tag input. The version tag drives the pipeline. A version number is MAJOR.MINOR.PATCH over a declared public API (Semantic Versioning 2.0.0 §2).
PHP 8.1 lane
Section titled “PHP 8.1 lane”- build-php81 — checks out the build tools, provisions PHP 8.5, installs build dependencies, clones the source repositories at the release tag, runs
scripts/build.php, switches the runner to PHP 8.1, syntax-checksoutput/srcon PHP 8.1, then installs the produced package with--no-dev. It uploads the output as an artifact. - validate-php81 — downloads the artifact, then installs and tests it on a matrix of PHP 8.1, 8.2, 8.3, and 8.4.
- release-php81 — downloads the artifact, commits and tags the generated tree, creates a zip archive that excludes
vendor/and.git/, and publishes a GitHub release with the archive attached.
PHP 7.4 lane
Section titled “PHP 7.4 lane”- build-php74 — checks out the build tools on the
PHP74branch, provisions PHP 8.5, clones only the core source repository at the tag, runsscripts/build.php --target=php74, switches to PHP 7.4, and syntax-checks the output on PHP 7.4. - validate-php74 — installs and tests on a matrix of PHP 7.4 and 8.0.
- release-php74 — creates a zip archive from the core-only output and attaches it to the same release as a second archive.
This flow is verified against build.yml job definitions and matrices.
The two lanes share one GitHub release. The PHP 8.1 lane creates the release, and the PHP 7.4 lane attaches its archive to the same tag. The
concurrencygroupbackport-buildwithcancel-in-progress: falseruns builds one at a time, so two source releases cannot race.
Operating the pipeline
Section titled “Operating the pipeline”Triggering a build
Section titled “Triggering a build”A build normally starts automatically when the source organization publishes a release. To rebuild a specific tag manually, dispatch build.yml with the tag input, such as v2.0.0. The dispatch token is secrets.BACKPORT_TRIGGER_TOKEN, and it authorizes source-repository clones. This is verified against build.yml (workflow_dispatch.inputs.tag, GH_TOKEN).
Reading a failed build
Section titled “Reading a failed build”The build script stops at the first failing stage and prints the stage name and error. The five stages are merge, Rector, composer.json generation, asset copy, and validation. If the syntax-check step after the build fails, Rector produced output that the target runtime rejects. That step protects the release. Map the failure to its cause in /integrations/backport/troubleshooting/.
What is published
Section titled “What is published”The release includes zipped distribution archives. The package ships as version tags, not branches. Consumers install nextpdf/backport (and optionally nextpdf/backport-pro) from the release channel. The archive excludes vendor/ and .git/. This is verified against build.yml (zip -r ... -x '*/vendor/*' '*/.git/*').
Versioning the output
Section titled “Versioning the output”The produced composer.json carries the version passed on the command line (--version, or the dispatch tag with the leading v stripped). The Pro package pins nextpdf/backport at the matching major.minor caret constraint. This is verified against scripts/adjust-composer.php (majorMinor(), generateProComposer()). Keep the source release tag and the backport version aligned so the replace map stays consistent.
- /integrations/backport/security-and-operations/ — the pipeline trust boundary and supply-chain posture.
- /integrations/backport/troubleshooting/ — failure reference for each stage.