From 32bc26c34098ccb73c22a532eb7a8451d5da94eb Mon Sep 17 00:00:00 2001 From: Jake Jackson Date: Wed, 30 Sep 2026 08:55:33 +1000 Subject: [PATCH 1/4] Run a smaller PHP matrix on pull requests and cancel runs a newer push replaces A pull request runs Tests on PHP 5.6 and 8.5 on Ubuntu and Windows, and on 7.4 and 8.0 on Ubuntu: 6 of the 24 jobs. The label full-ci gives it the full matrix, which pushes to gravitypdf, a nightly schedule and a manual dispatch always run. A newer push to a pull request cancels the run it replaces; pushes to gravitypdf never cancel each other. The coding standard and PHPStan run as one Lint workflow. The e-invoice interop and PDF/X-4 policy workflows run on a pull request only when it changes the code they cover, and on every push to gravitypdf. Tests run with opcache.enable_cli. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/conformance.yml | 5 ++ .github/workflows/cs.yml | 56 ------------------- .github/workflows/einvoice-interop.yml | 23 +++++++- .../{static-analysis.yml => lint.yml} | 27 ++++++--- .github/workflows/pdfx4-policy.yml | 20 +++++++ .github/workflows/snapshots.yml | 6 ++ .github/workflows/tests.yml | 45 +++++++++------ 7 files changed, 101 insertions(+), 81 deletions(-) delete mode 100644 .github/workflows/cs.yml rename .github/workflows/{static-analysis.yml => lint.yml} (62%) diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index d43681074..569113e96 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -11,6 +11,11 @@ on: - "test" - "gravitypdf" +# A push to a pull request cancels the run it replaces. Other events run in a group of their own +concurrency: + group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}" + cancel-in-progress: true + jobs: tests: diff --git a/.github/workflows/cs.yml b/.github/workflows/cs.yml deleted file mode 100644 index b0dba1ed8..000000000 --- a/.github/workflows/cs.yml +++ /dev/null @@ -1,56 +0,0 @@ -# https://help.github.com/en/categories/automating-your-workflow-with-github-actions - -name: "Coding standard check" - -on: - pull_request: - push: - branches: - - "development" - - "test" - - "gravitypdf" - -jobs: - - cs: - - name: "Coding standard" - - runs-on: ${{ matrix.operating-system }} - - strategy: - matrix: - php-version: - - "7.4" - - operating-system: [ubuntu-latest] - - steps: - - name: "Checkout" - uses: "actions/checkout@v7" - - - name: "Install PHP" - uses: "shivammathur/setup-php@v2" - with: - coverage: "none" - php-version: "${{ matrix.php-version }}" - extensions: "mbstring" - tools: composer:v2 - - - name: "Locate Composer cache" - id: composer-cache - shell: bash - run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT" - - - name: "Cache Composer downloads" - uses: "actions/cache@v6" - with: - path: "${{ steps.composer-cache.outputs.dir }}" - key: "composer-${{ runner.os }}-php${{ matrix.php-version }}-${{ hashFiles('composer.json') }}" - restore-keys: "composer-${{ runner.os }}-php${{ matrix.php-version }}-" - - - name: "Install dependencies" - run: "composer install --no-interaction --no-progress" - - - name: "CS" - run: composer cs diff --git a/.github/workflows/einvoice-interop.yml b/.github/workflows/einvoice-interop.yml index 964c61cbb..622e82956 100644 --- a/.github/workflows/einvoice-interop.yml +++ b/.github/workflows/einvoice-interop.yml @@ -3,7 +3,23 @@ name: "E-invoice interop" on: + # Pull requests run this only when they change one of these paths pull_request: + paths: + - ".github/workflows/einvoice-interop.yml" + - "composer.json" + - "phpunit.xml" + - "src/Invoice/**" + - "src/Pdf/**" + - "src/Utils/**" + - "src/Writer/**" + - "src/Xmp/**" + - "tests/Mpdf/Invoice/**" + - "tests/Mpdf/InteropPackage.php" + - "tests/Mpdf/InvoiceFixtures.php" + - "tests/Mpdf/PageStreams.php" + - "tests/data/invoice/**" + workflow_dispatch: push: branches: - "master" @@ -11,6 +27,11 @@ on: - "test" - "gravitypdf" +# A push to a pull request cancels the run it replaces. Other events run in a group of their own +concurrency: + group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}" + cancel-in-progress: true + jobs: interop: @@ -56,7 +77,7 @@ jobs: restore-keys: "composer-${{ runner.os }}-php${{ matrix.php-version }}-einvoice-" - name: "Install dependencies" - run: "composer install --no-interaction --no-progress && composer require --dev ${{ matrix.packages }} --no-interaction --no-progress" + run: "composer require --dev ${{ matrix.packages }} --no-update && composer install --no-interaction --no-progress" - name: "Tests" run: composer test -- --group=interop diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/lint.yml similarity index 62% rename from .github/workflows/static-analysis.yml rename to .github/workflows/lint.yml index 073bb5b85..f2d9b0b46 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/lint.yml @@ -1,6 +1,6 @@ # https://help.github.com/en/categories/automating-your-workflow-with-github-actions -name: "Static Analysis check" +name: "Lint" on: pull_request: @@ -11,22 +11,27 @@ on: - "test" - "gravitypdf" +# A push to a pull request cancels the run it replaces. Other events run in a group of their own +concurrency: + group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}" + cancel-in-progress: true + jobs: - stan: + lint: - name: "Static Analysis check" + name: "Lint" - runs-on: ${{ matrix.operating-system }} + runs-on: ubuntu-latest strategy: + fail-fast: false matrix: + # PHPStan reports differ between PHP versions, so it runs on both php-version: - "8.2" - "8.5" - operating-system: [ubuntu-latest] - steps: - name: "Checkout" uses: "actions/checkout@v7" @@ -52,7 +57,13 @@ jobs: restore-keys: "composer-${{ runner.os }}-php${{ matrix.php-version }}-phpstan-" - name: "Install dependencies" - run: "composer install --no-interaction --no-progress && composer require \"phpstan/phpstan:^2.0\"" + id: install + run: "composer require \"phpstan/phpstan:^2.0\" --no-update && composer install --no-interaction --no-progress" + + - name: "Coding standard" + if: matrix.php-version == '8.2' + run: composer cs - - name: "Static Analysis check" + - name: "Static analysis" + if: "!cancelled() && steps.install.outcome == 'success'" run: vendor/bin/phpstan --no-progress diff --git a/.github/workflows/pdfx4-policy.yml b/.github/workflows/pdfx4-policy.yml index 7f2c7958f..95b5e39a0 100644 --- a/.github/workflows/pdfx4-policy.yml +++ b/.github/workflows/pdfx4-policy.yml @@ -6,7 +6,22 @@ name: "PDF/X-4 Policy" on: + # Pull requests run this only when they change one of these paths pull_request: + paths: + - ".github/workflows/pdfx4-policy.yml" + - "composer.json" + - "data/iccprofiles/**" + - "src/Color/**" + - "src/Fonts/Color/**" + - "src/Gradient.php" + - "src/Image/**" + - "src/Pdf/**" + - "src/Writer/**" + - "src/Xmp/**" + - "tests/data/icc/**" + - "utils/verapdf/**" + workflow_dispatch: push: branches: - "master" @@ -14,6 +29,11 @@ on: - "test" - "gravitypdf" +# A push to a pull request cancels the run it replaces. Other events run in a group of their own +concurrency: + group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}" + cancel-in-progress: true + jobs: policy: diff --git a/.github/workflows/snapshots.yml b/.github/workflows/snapshots.yml index f09fbcaa9..8b709309d 100644 --- a/.github/workflows/snapshots.yml +++ b/.github/workflows/snapshots.yml @@ -11,6 +11,11 @@ on: - "test" - "gravitypdf" +# A push to a pull request cancels the run it replaces. Other events run in a group of their own +concurrency: + group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}" + cancel-in-progress: true + jobs: tests: @@ -43,6 +48,7 @@ jobs: run: | # Older policies forbid the PDF coder. Snapshots only rasterise when they differ, so read one now to fail early sudo sed -i 's/rights="none" pattern="PDF"/rights="read|write" pattern="PDF"/' /etc/ImageMagick-*/policy.xml + # shellcheck disable=SC2016 php -r '$i = new Imagick(); $i->setResolution(120, 120); $i->readImage("tests/data/snapshots/fixed-position-html.pdf"); printf("%s read %d page of %dx%d px\n", Imagick::getVersion()["versionString"], $i->getNumberImages(), $i->getImageWidth(), $i->getImageHeight());' - name: "Locate Composer cache" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a005d661a..d678353cd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,12 +4,21 @@ name: "Tests" on: pull_request: + types: [opened, synchronize, reopened, labeled] push: branches: - "master" - "development" - "test" - "gravitypdf" + schedule: + - cron: "0 17 * * *" + workflow_dispatch: + +# A push to a pull request cancels the run it replaces. Other events, and a label other than full-ci, run in a group of their own +concurrency: + group: "${{ github.workflow }}-${{ (github.event.action != 'labeled' || github.event.label.name == 'full-ci') && github.event.pull_request.number || github.run_id }}" + cancel-in-progress: true jobs: @@ -17,25 +26,29 @@ jobs: name: "Tests" + if: github.event.action != 'labeled' || github.event.label.name == 'full-ci' + runs-on: ${{ matrix.operating-system }} strategy: fail-fast: false - matrix: - php-version: - - "5.6" - - "7.0" - - "7.1" - - "7.2" - - "7.3" - - "7.4" - - "8.0" - - "8.1" - - "8.2" - - "8.3" - - "8.4" - - "8.5" - operating-system: [ubuntu-latest, windows-latest] + # A pull request without the full-ci label runs 5.6 and 8.5 on both systems, and 7.4 and 8.0 on Ubuntu + matrix: >- + ${{ fromJSON( + (github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'full-ci')) + && '{ + "php-version": ["5.6", "7.0", "7.1", "7.2", "7.3", "7.4", "8.0", "8.1", "8.2", "8.3", "8.4", "8.5"], + "operating-system": ["ubuntu-latest", "windows-latest"] + }' + || '{ + "php-version": ["5.6", "7.4", "8.0", "8.5"], + "operating-system": ["ubuntu-latest", "windows-latest"], + "exclude": [ + {"php-version": "7.4", "operating-system": "windows-latest"}, + {"php-version": "8.0", "operating-system": "windows-latest"} + ] + }' + ) }} steps: - name: "Checkout" @@ -48,7 +61,7 @@ jobs: php-version: "${{ matrix.php-version }}" extensions: "mbstring, gd, bcmath, bz2, fileinfo" tools: composer:v2 - ini-values: error_reporting=-1 + ini-values: error_reporting=-1, opcache.enable_cli=1 - name: "Locate Composer cache" id: composer-cache From b8bdbaf961b5115fb9ccdd45f8bb479353bbc95e Mon Sep 17 00:00:00 2001 From: Jake Jackson Date: Wed, 30 Sep 2026 08:59:49 +1000 Subject: [PATCH 2/4] Log the smaller pull request matrix in the GravityPDF changelog Co-Authored-By: Claude Opus 5.5 --- CHANGELOG-GRAVITYPDF.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-GRAVITYPDF.md b/CHANGELOG-GRAVITYPDF.md index 9ace4854d..2824e1b53 100644 --- a/CHANGELOG-GRAVITYPDF.md +++ b/CHANGELOG-GRAVITYPDF.md @@ -324,10 +324,12 @@ These changes do not change output. * **UTF-8 decoding.** A valid UTF-8 string is decoded with mbstring. [#364] * **Snapshot tests.** Snapshot tests compare bytes first, then PDF objects, then pixels. Snapshot documents carry nothing of the machine that made them. [#72] * **Data scripts.** Composer scripts regenerate the Unicode, emoji, joining, language system, font cache, subset, shaping and grey profile data, and update snapshots. -* **CI.** [#5] [#310] [#317] [#376] [#377] +* **CI.** [#5] [#310] [#317] [#376] [#377] [#615] * CI runs on the `gravitypdf` branch, caches Composer downloads and uses current action versions. * It checks PDF/A and PDF/X-4 output with veraPDF, and tests e-invoices against the XML packages `composer.json` suggests. * The test suite runs under a 512M memory limit. + * Pull requests run 6 of the 24 PHP and OS test jobs unless labelled `full-ci`. Pushes to `gravitypdf`, a nightly run and a manual run get all 24. A newer push to a pull request cancels the run it replaces. + * The coding standard and PHPStan run as one Lint workflow. The e-invoice and PDF/X-4 checks run on a pull request only when it changes the code they cover. * **PHPStan.** Fixes for newer PHP versions and PHPStan 2.2.15. [#6] [#295] [#319] [#451] * **Branch alias.** The Composer branch alias maps `dev-gravitypdf` to `8.x-dev`. [#3] @@ -735,3 +737,4 @@ These changes do not change output. [#564]: https://github.com/GravityPDF/mpdf/pull/564 [#563]: https://github.com/GravityPDF/mpdf/pull/563 [#565]: https://github.com/GravityPDF/mpdf/pull/565 +[#615]: https://github.com/GravityPDF/mpdf/pull/615 From 79750ef8ad772c17cc2e990e7988b3b974f1c111 Mon Sep 17 00:00:00 2001 From: Jake Jackson Date: Wed, 30 Sep 2026 09:08:54 +1000 Subject: [PATCH 3/4] Run every workflow nightly and by hand Co-Authored-By: Claude Opus 5.5 --- .github/workflows/conformance.yml | 3 +++ .github/workflows/coverage.yml | 3 +++ .github/workflows/einvoice-interop.yml | 4 +++- .github/workflows/lint.yml | 3 +++ .github/workflows/pdfx4-policy.yml | 4 +++- .github/workflows/snapshots.yml | 3 +++ CHANGELOG-GRAVITYPDF.md | 1 + 7 files changed, 19 insertions(+), 2 deletions(-) diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index 569113e96..b0adbaffa 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -10,6 +10,9 @@ on: - "development" - "test" - "gravitypdf" + schedule: + - cron: "0 17 * * *" + workflow_dispatch: # A push to a pull request cancels the run it replaces. Other events run in a group of their own concurrency: diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 1232ea2f5..4bc2d328b 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -7,6 +7,9 @@ on: branches: - "development" - "coverage" + schedule: + - cron: "0 17 * * *" + workflow_dispatch: jobs: diff --git a/.github/workflows/einvoice-interop.yml b/.github/workflows/einvoice-interop.yml index 622e82956..1666fdc17 100644 --- a/.github/workflows/einvoice-interop.yml +++ b/.github/workflows/einvoice-interop.yml @@ -19,13 +19,15 @@ on: - "tests/Mpdf/InvoiceFixtures.php" - "tests/Mpdf/PageStreams.php" - "tests/data/invoice/**" - workflow_dispatch: push: branches: - "master" - "development" - "test" - "gravitypdf" + schedule: + - cron: "0 17 * * *" + workflow_dispatch: # A push to a pull request cancels the run it replaces. Other events run in a group of their own concurrency: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index f2d9b0b46..9540488f8 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -10,6 +10,9 @@ on: - "development" - "test" - "gravitypdf" + schedule: + - cron: "0 17 * * *" + workflow_dispatch: # A push to a pull request cancels the run it replaces. Other events run in a group of their own concurrency: diff --git a/.github/workflows/pdfx4-policy.yml b/.github/workflows/pdfx4-policy.yml index 95b5e39a0..f2e6a1ae5 100644 --- a/.github/workflows/pdfx4-policy.yml +++ b/.github/workflows/pdfx4-policy.yml @@ -21,13 +21,15 @@ on: - "src/Xmp/**" - "tests/data/icc/**" - "utils/verapdf/**" - workflow_dispatch: push: branches: - "master" - "development" - "test" - "gravitypdf" + schedule: + - cron: "0 17 * * *" + workflow_dispatch: # A push to a pull request cancels the run it replaces. Other events run in a group of their own concurrency: diff --git a/.github/workflows/snapshots.yml b/.github/workflows/snapshots.yml index 8b709309d..4b1a80b62 100644 --- a/.github/workflows/snapshots.yml +++ b/.github/workflows/snapshots.yml @@ -10,6 +10,9 @@ on: - "development" - "test" - "gravitypdf" + schedule: + - cron: "0 17 * * *" + workflow_dispatch: # A push to a pull request cancels the run it replaces. Other events run in a group of their own concurrency: diff --git a/CHANGELOG-GRAVITYPDF.md b/CHANGELOG-GRAVITYPDF.md index 2824e1b53..32125b486 100644 --- a/CHANGELOG-GRAVITYPDF.md +++ b/CHANGELOG-GRAVITYPDF.md @@ -329,6 +329,7 @@ These changes do not change output. * It checks PDF/A and PDF/X-4 output with veraPDF, and tests e-invoices against the XML packages `composer.json` suggests. * The test suite runs under a 512M memory limit. * Pull requests run 6 of the 24 PHP and OS test jobs unless labelled `full-ci`. Pushes to `gravitypdf`, a nightly run and a manual run get all 24. A newer push to a pull request cancels the run it replaces. + * Every workflow, code coverage included, also runs nightly on `gravitypdf` and can be run by hand. * The coding standard and PHPStan run as one Lint workflow. The e-invoice and PDF/X-4 checks run on a pull request only when it changes the code they cover. * **PHPStan.** Fixes for newer PHP versions and PHPStan 2.2.15. [#6] [#295] [#319] [#451] * **Branch alias.** The Composer branch alias maps `dev-gravitypdf` to `8.x-dev`. [#3] From 8e88643616004ecbdeca99c5396a5b0981af788f Mon Sep 17 00:00:00 2001 From: Jake Jackson Date: Wed, 30 Sep 2026 09:35:38 +1000 Subject: [PATCH 4/4] Keep the JIT off in the test jobs that turn opcache on setup-php enables the JIT on PHP 8. With opcache.enable_cli it compiled the OpenType positioning code wrongly on 8.0, 8.4 and 8.5: kerning in the TJ arrays came out as a float's bits read as an integer, and 14 shaping snapshots failed. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d678353cd..fe4d9eb31 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -61,7 +61,8 @@ jobs: php-version: "${{ matrix.php-version }}" extensions: "mbstring, gd, bcmath, bz2, fileinfo" tools: composer:v2 - ini-values: error_reporting=-1, opcache.enable_cli=1 + # setup-php turns the JIT on for PHP 8, and on 8.0, 8.4 and 8.5 it corrupts OpenType positioning in the snapshots + ini-values: error_reporting=-1, opcache.enable_cli=1, opcache.jit=disable - name: "Locate Composer cache" id: composer-cache