Skip to content

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.

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/.

0-ci.yml runs on push and pull request events for PHP74 and PHP81. It runs three jobs in order:

  1. PHPStan (Build Tools)composer analyse, level 10 for rector/rules and scripts.
  2. PHPUnit (Rector Rules)composer test, the fixture suites for the three custom rules.
  3. Build Dry Runcomposer 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.

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).

  1. 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-checks output/src on PHP 8.1, then installs the produced package with --no-dev. It uploads the output as an artifact.
  2. validate-php81 — downloads the artifact, then installs and tests it on a matrix of PHP 8.1, 8.2, 8.3, and 8.4.
  3. 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.
  1. build-php74 — checks out the build tools on the PHP74 branch, provisions PHP 8.5, clones only the core source repository at the tag, runs scripts/build.php --target=php74, switches to PHP 7.4, and syntax-checks the output on PHP 7.4.
  2. validate-php74 — installs and tests on a matrix of PHP 7.4 and 8.0.
  3. 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 concurrency group backport-build with cancel-in-progress: false runs builds one at a time, so two source releases cannot race.

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).

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/.

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/*').

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.