diff --git a/.github/workflows/legacy-v5.yml b/.github/workflows/legacy-v5.yml index 575a238..66a3476 100644 --- a/.github/workflows/legacy-v5.yml +++ b/.github/workflows/legacy-v5.yml @@ -8,11 +8,14 @@ jobs: strategy: fail-fast: false matrix: - php_version: ['8.1', '8.0', '7.4'] + php_version: ['8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '7.4'] variant: ['apache','cli','fpm'] builder: [ {arch: "amd64", os: "ubuntu-24.04"}, {arch: "arm64", os: "ubuntu-24.04-arm"}] runs-on: ${{ matrix.builder.os }} - name: Build ${{ matrix.php_version }}-${{ matrix.variant }} ${{ matrix.builder.arch }} + env: + # Non-maintained PHP versions: all the images. Maintained PHP versions: only the non-maintained NodeJS versions + BAKE_GROUP: php${{ matrix.php_version }}-${{ matrix.variant }}-${{ contains(fromJSON('["8.1", "8.0", "7.4"]'), matrix.php_version) && 'all' || 'legacy-node' }} + name: Build&test ${{ matrix.php_version }}-${{ matrix.variant }} ${{ matrix.builder.arch }} steps: - name: Checkout uses: actions/checkout@v7 @@ -26,35 +29,49 @@ jobs: uses: docker/setup-buildx-action@v4 - name: Build run: | - PHP_VERSION="${{ matrix.php_version }}" - # Get last cached successful builds - TARGETS=$(REPO="ghcr.io/${{ github.repository_owner }}/php" TAG_PREFIX="" docker buildx bake php${PHP_VERSION//.}-${{ matrix.variant }}-all --progress=quiet --print | jq -r '.target[].tags[]') - for TARGET in $TARGETS; do - docker pull --quiet ${TARGET} || echo "" - done; - # Build the current builds REPO="ghcr.io/${{ github.repository_owner }}/php" \ ARCH_SUFFIX="-${{ matrix.builder.arch }}" \ TAG_PREFIX="rc${GITHUB_RUN_ID}-" \ docker buildx bake \ --set "*.platform=linux/${{ matrix.builder.arch }}" \ - --set "*.output=type=registry" \ - --progress=quiet \ - --load \ - php${PHP_VERSION//.}-${{ matrix.variant }}-all + --set "*.output=type=docker" \ + --load --progress=quiet \ + ${BAKE_GROUP//.} - name: Display tags built run: | docker image ls --filter="reference=ghcr.io/${{ github.repository_owner }}/php" --format "{{.CreatedAt}}\t{{.Size}}\t{{.Repository}}:{{.Tag}}" + - name: Test + run: | + REPO="ghcr.io/${{ github.repository_owner }}/php" \ + TAG_PREFIX="rc${GITHUB_RUN_ID}-" \ + PHP_VERSION="${{ matrix.php_version }}" \ + ARCH_SUFFIX="-${{ matrix.builder.arch }}" \ + BRANCH="v5" \ + VARIANT="${{ matrix.variant }}" \ + PLATFORM="linux/${{ matrix.builder.arch }}" \ + ./tests-suite/bash_unit -f tap ./tests-suite/*.sh + - name: Publish specific arch version + run: | + TAGS=$(REPO="ghcr.io/${{ github.repository_owner }}/php" \ + ARCH_SUFFIX="-${{ matrix.builder.arch }}" \ + TAG_PREFIX="rc${GITHUB_RUN_ID}-" \ + docker buildx bake \ + --print --progress=quiet \ + ${BAKE_GROUP//.} | jq -r '.target[].tags[]'); + for TAG in $TAGS; do docker push --quiet ${TAG}; done; merge-and-publish: needs: - build runs-on: ubuntu-latest + env: + # Non-maintained PHP versions: all the images. Maintained PHP versions: only the non-maintained NodeJS versions + BAKE_GROUP: php${{ matrix.php_version }}-${{ matrix.variant }}-${{ contains(fromJSON('["8.1", "8.0", "7.4"]'), matrix.php_version) && 'all' || 'legacy-node' }} name: Merge and publish ${{ matrix.php_version }}-${{ matrix.variant }} strategy: fail-fast: false matrix: - php_version: ['8.1', '8.0', '7.4'] + php_version: ['8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '7.4'] variant: ['apache','cli','fpm'] steps: - name: Checkout @@ -88,18 +105,19 @@ jobs: - name: Get list of images for this variant id: get-images run: | - PHP_VERSION="${{ matrix.php_version }}" REPO="ghcr.io/${{ github.repository_owner }}/php" \ TAG_PREFIX="rc${GITHUB_RUN_ID}-" \ docker buildx bake \ --print --progress=quiet \ - php${PHP_VERSION//.}-${{ matrix.variant }}-all | jq '.target[].tags[]' + ${BAKE_GROUP//.} | jq '.target[].tags[]' - name: Create multiarch manifests run: | - PHP_VERSION="${{ matrix.php_version }}" - # Get all targets from the bake group - TARGETS=$(docker buildx bake php${PHP_VERSION//.}-${{ matrix.variant }}-all --progress=quiet --print | jq -r ".group[\"php${PHP_VERSION//.}-${{ matrix.variant }}-all\"].targets[]") + TARGETS=$(docker buildx bake ${BAKE_GROUP//.} --progress=quiet --print | jq -r ".group[\"${BAKE_GROUP//.}\"].targets[]") + # The slim and fat images of the maintained PHP versions are published by the main workflow + if [[ "${BAKE_GROUP}" == *-legacy-node ]]; then + TARGETS=$(echo "${TARGETS}" | grep -- '-node') + fi for TARGET in $TARGETS; do TAG_ORI=$(REPO="ghcr.io/${{ github.repository_owner }}/php" TAG_PREFIX="rc${GITHUB_RUN_ID}-" docker buildx bake ${TARGET} --progress=quiet --print | jq -r ".target | to_entries[] | select(.key == \"${TARGET}\") | .value.tags[0]") diff --git a/CHANGELOG.md b/CHANGELOG.md index 903ed35..298ea32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,14 @@ ### Minor changes +* **2026-10-21** + * Security: fix of php2shell (CVE-2026-109618 and CVE-2026-109619, CVSS 9.2) in all the v5 amd64 images. PHP 7.4, 8.0 and 8.1 images are rebuilt with the backport of the fix (same PHP version: 7.4.33, 8.0.30, 8.1.34). The arm64 images will be fixed when the fixed PHP packages are published for arm64. See [php2shell](docs/security-php2shell.md) + * NodeJS 18 and 20 variants are built again for all the PHP versions, by the legacy workflow + * New test checking that the images are not vulnerable to php2shell (only a warning on arm64 until the fixed packages are published) + * Legacy build (`legacy-v5.yml`): the images are tested before being published, like in the main build + * PHP 8.0 and 8.1: the `swoole` extension is removed, packages.sury.org does not provide it for these versions + * The build of an extension fails when its package does not exist for the PHP version, instead of installing the package of another PHP version + * **2026-10-19** * New documentation page for the fpm variant: serving HTTP (built-in Apache or another web server), constraints of PHP-FPM and migration from the apache variant * fpm variant: the time the built-in Apache waits for PHP-FPM can be configured with `APACHE_PROXY_TIMEOUT` (300 seconds by default) diff --git a/README.md b/README.md index 44ff52e..794ce11 100644 --- a/README.md +++ b/README.md @@ -3,23 +3,12 @@ # General purpose PHP images for Docker -> **New: built-in Apache in the fpm variant (`PHP_FPM_WEB_SERVER=apache`)** +> **Security: php2shell (CVE-2026-109618 and CVE-2026-109619, CVSS 9.2) is fixed in all the v5 amd64 images** > -> The *fpm* variant can now run Apache in front of PHP-FPM, in the same container. It is a drop-in alternative to the -> *apache* variant: same Apache features (`.htaccess`, `APACHE_DOCUMENT_ROOT`, `APACHE_EXTENSION_*`), but PHP runs in -> PHP-FPM instead of `mod_php`. -> -> **Why?** Under the same load ([benchmark](benchmarks/fpm-apache)), the container serves **2 to 3 times more traffic -> with 4 times less memory** (30 vs 10 pages/s on 2 CPUs, ~90 vs ~380 MiB). `mod_php` forces Apache to dedicate a -> whole process embedding PHP to each connection, including idle keep-alive connections and static files. With -> PHP-FPM, Apache uses its threaded MPM (`mpm_event`) and only PHP requests reach the PHP workers. -> -> ```bash -> $ docker run -p 80:80 -e PHP_FPM_WEB_SERVER=apache -v "$PWD":/var/www/html thecodingmachine/php:8.4-v5-fpm -> ``` -> -> **[Read more: advantages, constraints of PHP-FPM and migration from the *apache* variant](docs/fpm.md)** -> (for instance, `php_value` directives are not supported in `.htaccess` files). The *apache* variant is still available. +> Pull the images again, recreate your containers and rebuild the images built from ours. The **arm64** images +> (Apple Silicon, AWS Graviton...) are **not fixed yet**: the fixed PHP packages for arm64 are still being built +> upstream, and the images will be updated as soon as they are published. The v4 images are **not** fixed: migrate to +> v5. See [what to do](docs/security-php2shell.md). This repository contains a set of developer-friendly, general purpose PHP images for Docker. @@ -155,6 +144,7 @@ Bugs are fixed on maintained versions only. | 20 | **Not maintained** (end of life: 2026-04-30) | Images of non-maintained versions are still available but no longer receive fixes: use them at your own risk and plan your upgrade. +Critical vulnerabilities may get an exceptional fix (e.g. [php2shell](docs/security-php2shell.md)). ## Usage @@ -209,6 +199,7 @@ Most features are configured with environment variables: | [Processes](docs/processes.md) | Startup commands, cron jobs and daemons run along with the main command | `STARTUP_COMMAND_*`, `CRON_*`, `DAEMON_*` | | [Debugging and profiling](docs/debugging.md) | Xdebug and Blackfire | `PHP_EXTENSION_XDEBUG`, `XDEBUG_CLIENT_HOST`, `BLACKFIRE_AGENT` | | [Advanced features](docs/advanced.md) | Permissions, NodeJS, Composer version, locales, SSH keys, Kubernetes | `DOCKER_USER`, `COMPOSER_VERSION`, `LOCALES` | +| [php2shell](docs/security-php2shell.md) | What to do about CVE-2026-109618 / CVE-2026-109619 | | The slim images install some tools during the build of your image with an ARG before the `FROM`: `PHP_EXTENSIONS`, `INSTALL_CRON`, `INSTALL_LOCALES`, `NODE_VERSION` and `COMPOSER_VERSION`. diff --git a/docker-bake.hcl b/docker-bake.hcl index 0b28e93..171dad4 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -20,6 +20,14 @@ group "php85-apache-all" { "php85-apache-node26","php85-apache-node24","php85-apache-node22", ] } +# NodeJS versions no longer maintained, built by the legacy workflow +group "php85-apache-legacy-node" { + targets = [ + "php85-slim-apache", + "php85-apache", + "php85-apache-node20","php85-apache-node18", + ] +} group "php85-fpm-all" { targets = [ "php85-slim-fpm", @@ -27,6 +35,14 @@ group "php85-fpm-all" { "php85-fpm-node26","php85-fpm-node24","php85-fpm-node22", ] } +# NodeJS versions no longer maintained, built by the legacy workflow +group "php85-fpm-legacy-node" { + targets = [ + "php85-slim-fpm", + "php85-fpm", + "php85-fpm-node20","php85-fpm-node18", + ] +} group "php85-cli-all" { targets = [ "php85-slim-cli", @@ -34,6 +50,14 @@ group "php85-cli-all" { "php85-cli-node26","php85-cli-node24","php85-cli-node22", ] } +# NodeJS versions no longer maintained, built by the legacy workflow +group "php85-cli-legacy-node" { + targets = [ + "php85-slim-cli", + "php85-cli", + "php85-cli-node20","php85-cli-node18", + ] +} group "php84-apache-all" { targets = [ "php84-slim-apache", @@ -41,6 +65,14 @@ group "php84-apache-all" { "php84-apache-node26","php84-apache-node24","php84-apache-node22", ] } +# NodeJS versions no longer maintained, built by the legacy workflow +group "php84-apache-legacy-node" { + targets = [ + "php84-slim-apache", + "php84-apache", + "php84-apache-node20","php84-apache-node18", + ] +} group "php84-fpm-all" { targets = [ "php84-slim-fpm", @@ -48,6 +80,14 @@ group "php84-fpm-all" { "php84-fpm-node26","php84-fpm-node24","php84-fpm-node22", ] } +# NodeJS versions no longer maintained, built by the legacy workflow +group "php84-fpm-legacy-node" { + targets = [ + "php84-slim-fpm", + "php84-fpm", + "php84-fpm-node20","php84-fpm-node18", + ] +} group "php84-cli-all" { targets = [ "php84-slim-cli", @@ -55,6 +95,14 @@ group "php84-cli-all" { "php84-cli-node26","php84-cli-node24","php84-cli-node22", ] } +# NodeJS versions no longer maintained, built by the legacy workflow +group "php84-cli-legacy-node" { + targets = [ + "php84-slim-cli", + "php84-cli", + "php84-cli-node20","php84-cli-node18", + ] +} group "php83-apache-all" { targets = [ "php83-slim-apache", @@ -62,6 +110,14 @@ group "php83-apache-all" { "php83-apache-node26","php83-apache-node24","php83-apache-node22", ] } +# NodeJS versions no longer maintained, built by the legacy workflow +group "php83-apache-legacy-node" { + targets = [ + "php83-slim-apache", + "php83-apache", + "php83-apache-node20","php83-apache-node18", + ] +} group "php83-fpm-all" { targets = [ "php83-slim-fpm", @@ -69,6 +125,14 @@ group "php83-fpm-all" { "php83-fpm-node26","php83-fpm-node24","php83-fpm-node22", ] } +# NodeJS versions no longer maintained, built by the legacy workflow +group "php83-fpm-legacy-node" { + targets = [ + "php83-slim-fpm", + "php83-fpm", + "php83-fpm-node20","php83-fpm-node18", + ] +} group "php83-cli-all" { targets = [ "php83-slim-cli", @@ -76,6 +140,14 @@ group "php83-cli-all" { "php83-cli-node26","php83-cli-node24","php83-cli-node22", ] } +# NodeJS versions no longer maintained, built by the legacy workflow +group "php83-cli-legacy-node" { + targets = [ + "php83-slim-cli", + "php83-cli", + "php83-cli-node20","php83-cli-node18", + ] +} group "php82-apache-all" { targets = [ "php82-slim-apache", @@ -83,6 +155,14 @@ group "php82-apache-all" { "php82-apache-node26","php82-apache-node24","php82-apache-node22", ] } +# NodeJS versions no longer maintained, built by the legacy workflow +group "php82-apache-legacy-node" { + targets = [ + "php82-slim-apache", + "php82-apache", + "php82-apache-node20","php82-apache-node18", + ] +} group "php82-fpm-all" { targets = [ "php82-slim-fpm", @@ -90,6 +170,14 @@ group "php82-fpm-all" { "php82-fpm-node26","php82-fpm-node24","php82-fpm-node22", ] } +# NodeJS versions no longer maintained, built by the legacy workflow +group "php82-fpm-legacy-node" { + targets = [ + "php82-slim-fpm", + "php82-fpm", + "php82-fpm-node20","php82-fpm-node18", + ] +} group "php82-cli-all" { targets = [ "php82-slim-cli", @@ -97,67 +185,75 @@ group "php82-cli-all" { "php82-cli-node26","php82-cli-node24","php82-cli-node22", ] } +# NodeJS versions no longer maintained, built by the legacy workflow +group "php82-cli-legacy-node" { + targets = [ + "php82-slim-cli", + "php82-cli", + "php82-cli-node20","php82-cli-node18", + ] +} group "php81-apache-all" { targets = [ "php81-slim-apache", "php81-apache", - "php81-apache-node26","php81-apache-node24","php81-apache-node22","php81-apache-node20", + "php81-apache-node26","php81-apache-node24","php81-apache-node22","php81-apache-node20","php81-apache-node18", ] } group "php81-fpm-all" { targets = [ "php81-slim-fpm", "php81-fpm", - "php81-fpm-node26","php81-fpm-node24","php81-fpm-node22","php81-fpm-node20", + "php81-fpm-node26","php81-fpm-node24","php81-fpm-node22","php81-fpm-node20","php81-fpm-node18", ] } group "php81-cli-all" { targets = [ "php81-slim-cli", "php81-cli", - "php81-cli-node26","php81-cli-node24","php81-cli-node22","php81-cli-node20", + "php81-cli-node26","php81-cli-node24","php81-cli-node22","php81-cli-node20","php81-cli-node18", ] } group "php80-apache-all" { targets = [ "php80-slim-apache", "php80-apache", - "php80-apache-node26","php80-apache-node24","php80-apache-node22","php80-apache-node20", + "php80-apache-node26","php80-apache-node24","php80-apache-node22","php80-apache-node20","php80-apache-node18", ] } group "php80-fpm-all" { targets = [ "php80-slim-fpm", "php80-fpm", - "php80-fpm-node26","php80-fpm-node24","php80-fpm-node22","php80-fpm-node20", + "php80-fpm-node26","php80-fpm-node24","php80-fpm-node22","php80-fpm-node20","php80-fpm-node18", ] } group "php80-cli-all" { targets = [ "php80-slim-cli", "php80-cli", - "php80-cli-node26","php80-cli-node24","php80-cli-node22","php80-cli-node20", + "php80-cli-node26","php80-cli-node24","php80-cli-node22","php80-cli-node20","php80-cli-node18", ] } group "php74-apache-all" { targets = [ "php74-slim-apache", "php74-apache", - "php74-apache-node26","php74-apache-node24","php74-apache-node22","php74-apache-node20", + "php74-apache-node26","php74-apache-node24","php74-apache-node22","php74-apache-node20","php74-apache-node18", ] } group "php74-fpm-all" { targets = [ "php74-slim-fpm", "php74-fpm", - "php74-fpm-node26","php74-fpm-node24","php74-fpm-node22","php74-fpm-node20", + "php74-fpm-node26","php74-fpm-node24","php74-fpm-node22","php74-fpm-node20","php74-fpm-node18", ] } group "php74-cli-all" { targets = [ "php74-slim-cli", "php74-cli", - "php74-cli-node26","php74-cli-node24","php74-cli-node22","php74-cli-node20", + "php74-cli-node26","php74-cli-node24","php74-cli-node22","php74-cli-node20","php74-cli-node18", ] } @@ -289,6 +385,38 @@ target "php85-apache-node22" { } } +# thecodingmachine/php:8.5-v5-apache-node20 +target "php85-apache-node20" { + inherits = ["default"] + tags = tag("8.5", "apache-node20") + dockerfile = "Dockerfile.apache.node" + args = { + PHP_VERSION = "8.5" + VARIANT = "apache-node20" + FROM_IMAGE = "fat" + NODE_VERSION = "20" + } + contexts = { + fat = "target:php85-apache" + } +} + +# thecodingmachine/php:8.5-v5-apache-node18 +target "php85-apache-node18" { + inherits = ["default"] + tags = tag("8.5", "apache-node18") + dockerfile = "Dockerfile.apache.node" + args = { + PHP_VERSION = "8.5" + VARIANT = "apache-node18" + FROM_IMAGE = "fat" + NODE_VERSION = "18" + } + contexts = { + fat = "target:php85-apache" + } +} + ########################### ## PHP 8.5 ########################### @@ -366,6 +494,38 @@ target "php85-fpm-node22" { } } +# thecodingmachine/php:8.5-v5-fpm-node20 +target "php85-fpm-node20" { + inherits = ["default"] + tags = tag("8.5", "fpm-node20") + dockerfile = "Dockerfile.fpm.node" + args = { + PHP_VERSION = "8.5" + VARIANT = "fpm-node20" + FROM_IMAGE = "fat" + NODE_VERSION = "20" + } + contexts = { + fat = "target:php85-fpm" + } +} + +# thecodingmachine/php:8.5-v5-fpm-node18 +target "php85-fpm-node18" { + inherits = ["default"] + tags = tag("8.5", "fpm-node18") + dockerfile = "Dockerfile.fpm.node" + args = { + PHP_VERSION = "8.5" + VARIANT = "fpm-node18" + FROM_IMAGE = "fat" + NODE_VERSION = "18" + } + contexts = { + fat = "target:php85-fpm" + } +} + ########################### ## PHP 8.5 ########################### @@ -443,6 +603,38 @@ target "php85-cli-node22" { } } +# thecodingmachine/php:8.5-v5-cli-node20 +target "php85-cli-node20" { + inherits = ["default"] + tags = tag("8.5", "cli-node20") + dockerfile = "Dockerfile.cli.node" + args = { + PHP_VERSION = "8.5" + VARIANT = "cli-node20" + FROM_IMAGE = "fat" + NODE_VERSION = "20" + } + contexts = { + fat = "target:php85-cli" + } +} + +# thecodingmachine/php:8.5-v5-cli-node18 +target "php85-cli-node18" { + inherits = ["default"] + tags = tag("8.5", "cli-node18") + dockerfile = "Dockerfile.cli.node" + args = { + PHP_VERSION = "8.5" + VARIANT = "cli-node18" + FROM_IMAGE = "fat" + NODE_VERSION = "18" + } + contexts = { + fat = "target:php85-cli" + } +} + ########################### ## PHP 8.4 ########################### @@ -520,6 +712,38 @@ target "php84-apache-node22" { } } +# thecodingmachine/php:8.4-v5-apache-node20 +target "php84-apache-node20" { + inherits = ["default"] + tags = tag("8.4", "apache-node20") + dockerfile = "Dockerfile.apache.node" + args = { + PHP_VERSION = "8.4" + VARIANT = "apache-node20" + FROM_IMAGE = "fat" + NODE_VERSION = "20" + } + contexts = { + fat = "target:php84-apache" + } +} + +# thecodingmachine/php:8.4-v5-apache-node18 +target "php84-apache-node18" { + inherits = ["default"] + tags = tag("8.4", "apache-node18") + dockerfile = "Dockerfile.apache.node" + args = { + PHP_VERSION = "8.4" + VARIANT = "apache-node18" + FROM_IMAGE = "fat" + NODE_VERSION = "18" + } + contexts = { + fat = "target:php84-apache" + } +} + ########################### ## PHP 8.4 ########################### @@ -597,6 +821,38 @@ target "php84-fpm-node22" { } } +# thecodingmachine/php:8.4-v5-fpm-node20 +target "php84-fpm-node20" { + inherits = ["default"] + tags = tag("8.4", "fpm-node20") + dockerfile = "Dockerfile.fpm.node" + args = { + PHP_VERSION = "8.4" + VARIANT = "fpm-node20" + FROM_IMAGE = "fat" + NODE_VERSION = "20" + } + contexts = { + fat = "target:php84-fpm" + } +} + +# thecodingmachine/php:8.4-v5-fpm-node18 +target "php84-fpm-node18" { + inherits = ["default"] + tags = tag("8.4", "fpm-node18") + dockerfile = "Dockerfile.fpm.node" + args = { + PHP_VERSION = "8.4" + VARIANT = "fpm-node18" + FROM_IMAGE = "fat" + NODE_VERSION = "18" + } + contexts = { + fat = "target:php84-fpm" + } +} + ########################### ## PHP 8.4 ########################### @@ -674,6 +930,38 @@ target "php84-cli-node22" { } } +# thecodingmachine/php:8.4-v5-cli-node20 +target "php84-cli-node20" { + inherits = ["default"] + tags = tag("8.4", "cli-node20") + dockerfile = "Dockerfile.cli.node" + args = { + PHP_VERSION = "8.4" + VARIANT = "cli-node20" + FROM_IMAGE = "fat" + NODE_VERSION = "20" + } + contexts = { + fat = "target:php84-cli" + } +} + +# thecodingmachine/php:8.4-v5-cli-node18 +target "php84-cli-node18" { + inherits = ["default"] + tags = tag("8.4", "cli-node18") + dockerfile = "Dockerfile.cli.node" + args = { + PHP_VERSION = "8.4" + VARIANT = "cli-node18" + FROM_IMAGE = "fat" + NODE_VERSION = "18" + } + contexts = { + fat = "target:php84-cli" + } +} + ########################### ## PHP 8.3 ########################### @@ -751,6 +1039,38 @@ target "php83-apache-node22" { } } +# thecodingmachine/php:8.3-v5-apache-node20 +target "php83-apache-node20" { + inherits = ["default"] + tags = tag("8.3", "apache-node20") + dockerfile = "Dockerfile.apache.node" + args = { + PHP_VERSION = "8.3" + VARIANT = "apache-node20" + FROM_IMAGE = "fat" + NODE_VERSION = "20" + } + contexts = { + fat = "target:php83-apache" + } +} + +# thecodingmachine/php:8.3-v5-apache-node18 +target "php83-apache-node18" { + inherits = ["default"] + tags = tag("8.3", "apache-node18") + dockerfile = "Dockerfile.apache.node" + args = { + PHP_VERSION = "8.3" + VARIANT = "apache-node18" + FROM_IMAGE = "fat" + NODE_VERSION = "18" + } + contexts = { + fat = "target:php83-apache" + } +} + ########################### ## PHP 8.3 ########################### @@ -828,6 +1148,38 @@ target "php83-fpm-node22" { } } +# thecodingmachine/php:8.3-v5-fpm-node20 +target "php83-fpm-node20" { + inherits = ["default"] + tags = tag("8.3", "fpm-node20") + dockerfile = "Dockerfile.fpm.node" + args = { + PHP_VERSION = "8.3" + VARIANT = "fpm-node20" + FROM_IMAGE = "fat" + NODE_VERSION = "20" + } + contexts = { + fat = "target:php83-fpm" + } +} + +# thecodingmachine/php:8.3-v5-fpm-node18 +target "php83-fpm-node18" { + inherits = ["default"] + tags = tag("8.3", "fpm-node18") + dockerfile = "Dockerfile.fpm.node" + args = { + PHP_VERSION = "8.3" + VARIANT = "fpm-node18" + FROM_IMAGE = "fat" + NODE_VERSION = "18" + } + contexts = { + fat = "target:php83-fpm" + } +} + ########################### ## PHP 8.3 ########################### @@ -905,6 +1257,38 @@ target "php83-cli-node22" { } } +# thecodingmachine/php:8.3-v5-cli-node20 +target "php83-cli-node20" { + inherits = ["default"] + tags = tag("8.3", "cli-node20") + dockerfile = "Dockerfile.cli.node" + args = { + PHP_VERSION = "8.3" + VARIANT = "cli-node20" + FROM_IMAGE = "fat" + NODE_VERSION = "20" + } + contexts = { + fat = "target:php83-cli" + } +} + +# thecodingmachine/php:8.3-v5-cli-node18 +target "php83-cli-node18" { + inherits = ["default"] + tags = tag("8.3", "cli-node18") + dockerfile = "Dockerfile.cli.node" + args = { + PHP_VERSION = "8.3" + VARIANT = "cli-node18" + FROM_IMAGE = "fat" + NODE_VERSION = "18" + } + contexts = { + fat = "target:php83-cli" + } +} + ########################### ## PHP 8.2 ########################### @@ -982,6 +1366,38 @@ target "php82-apache-node22" { } } +# thecodingmachine/php:8.2-v5-apache-node20 +target "php82-apache-node20" { + inherits = ["default"] + tags = tag("8.2", "apache-node20") + dockerfile = "Dockerfile.apache.node" + args = { + PHP_VERSION = "8.2" + VARIANT = "apache-node20" + FROM_IMAGE = "fat" + NODE_VERSION = "20" + } + contexts = { + fat = "target:php82-apache" + } +} + +# thecodingmachine/php:8.2-v5-apache-node18 +target "php82-apache-node18" { + inherits = ["default"] + tags = tag("8.2", "apache-node18") + dockerfile = "Dockerfile.apache.node" + args = { + PHP_VERSION = "8.2" + VARIANT = "apache-node18" + FROM_IMAGE = "fat" + NODE_VERSION = "18" + } + contexts = { + fat = "target:php82-apache" + } +} + ########################### ## PHP 8.2 ########################### @@ -1059,6 +1475,38 @@ target "php82-fpm-node22" { } } +# thecodingmachine/php:8.2-v5-fpm-node20 +target "php82-fpm-node20" { + inherits = ["default"] + tags = tag("8.2", "fpm-node20") + dockerfile = "Dockerfile.fpm.node" + args = { + PHP_VERSION = "8.2" + VARIANT = "fpm-node20" + FROM_IMAGE = "fat" + NODE_VERSION = "20" + } + contexts = { + fat = "target:php82-fpm" + } +} + +# thecodingmachine/php:8.2-v5-fpm-node18 +target "php82-fpm-node18" { + inherits = ["default"] + tags = tag("8.2", "fpm-node18") + dockerfile = "Dockerfile.fpm.node" + args = { + PHP_VERSION = "8.2" + VARIANT = "fpm-node18" + FROM_IMAGE = "fat" + NODE_VERSION = "18" + } + contexts = { + fat = "target:php82-fpm" + } +} + ########################### ## PHP 8.2 ########################### @@ -1136,6 +1584,38 @@ target "php82-cli-node22" { } } +# thecodingmachine/php:8.2-v5-cli-node20 +target "php82-cli-node20" { + inherits = ["default"] + tags = tag("8.2", "cli-node20") + dockerfile = "Dockerfile.cli.node" + args = { + PHP_VERSION = "8.2" + VARIANT = "cli-node20" + FROM_IMAGE = "fat" + NODE_VERSION = "20" + } + contexts = { + fat = "target:php82-cli" + } +} + +# thecodingmachine/php:8.2-v5-cli-node18 +target "php82-cli-node18" { + inherits = ["default"] + tags = tag("8.2", "cli-node18") + dockerfile = "Dockerfile.cli.node" + args = { + PHP_VERSION = "8.2" + VARIANT = "cli-node18" + FROM_IMAGE = "fat" + NODE_VERSION = "18" + } + contexts = { + fat = "target:php82-cli" + } +} + ########################### ## PHP 8.1 ########################### @@ -1229,6 +1709,22 @@ target "php81-apache-node20" { } } +# thecodingmachine/php:8.1-v5-apache-node18 +target "php81-apache-node18" { + inherits = ["default"] + tags = tag("8.1", "apache-node18") + dockerfile = "Dockerfile.apache.node" + args = { + PHP_VERSION = "8.1" + VARIANT = "apache-node18" + FROM_IMAGE = "fat" + NODE_VERSION = "18" + } + contexts = { + fat = "target:php81-apache" + } +} + ########################### ## PHP 8.1 ########################### @@ -1322,6 +1818,22 @@ target "php81-fpm-node20" { } } +# thecodingmachine/php:8.1-v5-fpm-node18 +target "php81-fpm-node18" { + inherits = ["default"] + tags = tag("8.1", "fpm-node18") + dockerfile = "Dockerfile.fpm.node" + args = { + PHP_VERSION = "8.1" + VARIANT = "fpm-node18" + FROM_IMAGE = "fat" + NODE_VERSION = "18" + } + contexts = { + fat = "target:php81-fpm" + } +} + ########################### ## PHP 8.1 ########################### @@ -1415,6 +1927,22 @@ target "php81-cli-node20" { } } +# thecodingmachine/php:8.1-v5-cli-node18 +target "php81-cli-node18" { + inherits = ["default"] + tags = tag("8.1", "cli-node18") + dockerfile = "Dockerfile.cli.node" + args = { + PHP_VERSION = "8.1" + VARIANT = "cli-node18" + FROM_IMAGE = "fat" + NODE_VERSION = "18" + } + contexts = { + fat = "target:php81-cli" + } +} + ########################### ## PHP 8.0 ########################### @@ -1508,6 +2036,22 @@ target "php80-apache-node20" { } } +# thecodingmachine/php:8.0-v5-apache-node18 +target "php80-apache-node18" { + inherits = ["default"] + tags = tag("8.0", "apache-node18") + dockerfile = "Dockerfile.apache.node" + args = { + PHP_VERSION = "8.0" + VARIANT = "apache-node18" + FROM_IMAGE = "fat" + NODE_VERSION = "18" + } + contexts = { + fat = "target:php80-apache" + } +} + ########################### ## PHP 8.0 ########################### @@ -1601,6 +2145,22 @@ target "php80-fpm-node20" { } } +# thecodingmachine/php:8.0-v5-fpm-node18 +target "php80-fpm-node18" { + inherits = ["default"] + tags = tag("8.0", "fpm-node18") + dockerfile = "Dockerfile.fpm.node" + args = { + PHP_VERSION = "8.0" + VARIANT = "fpm-node18" + FROM_IMAGE = "fat" + NODE_VERSION = "18" + } + contexts = { + fat = "target:php80-fpm" + } +} + ########################### ## PHP 8.0 ########################### @@ -1694,6 +2254,22 @@ target "php80-cli-node20" { } } +# thecodingmachine/php:8.0-v5-cli-node18 +target "php80-cli-node18" { + inherits = ["default"] + tags = tag("8.0", "cli-node18") + dockerfile = "Dockerfile.cli.node" + args = { + PHP_VERSION = "8.0" + VARIANT = "cli-node18" + FROM_IMAGE = "fat" + NODE_VERSION = "18" + } + contexts = { + fat = "target:php80-cli" + } +} + ########################### ## PHP 7.4 ########################### @@ -1787,6 +2363,22 @@ target "php74-apache-node20" { } } +# thecodingmachine/php:7.4-v5-apache-node18 +target "php74-apache-node18" { + inherits = ["default"] + tags = tag("7.4", "apache-node18") + dockerfile = "Dockerfile.apache.node" + args = { + PHP_VERSION = "7.4" + VARIANT = "apache-node18" + FROM_IMAGE = "fat" + NODE_VERSION = "18" + } + contexts = { + fat = "target:php74-apache" + } +} + ########################### ## PHP 7.4 ########################### @@ -1880,6 +2472,22 @@ target "php74-fpm-node20" { } } +# thecodingmachine/php:7.4-v5-fpm-node18 +target "php74-fpm-node18" { + inherits = ["default"] + tags = tag("7.4", "fpm-node18") + dockerfile = "Dockerfile.fpm.node" + args = { + PHP_VERSION = "7.4" + VARIANT = "fpm-node18" + FROM_IMAGE = "fat" + NODE_VERSION = "18" + } + contexts = { + fat = "target:php74-fpm" + } +} + ########################### ## PHP 7.4 ########################### @@ -1973,4 +2581,20 @@ target "php74-cli-node20" { } } +# thecodingmachine/php:7.4-v5-cli-node18 +target "php74-cli-node18" { + inherits = ["default"] + tags = tag("7.4", "cli-node18") + dockerfile = "Dockerfile.cli.node" + args = { + PHP_VERSION = "7.4" + VARIANT = "cli-node18" + FROM_IMAGE = "fat" + NODE_VERSION = "18" + } + contexts = { + fat = "target:php74-cli" + } +} + diff --git a/docs/contributing.md b/docs/contributing.md index 890c331..2ec34ad 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -38,6 +38,9 @@ built with `TAG_PREFIX=mytest- docker buildx bake ...`) or `PLATFORM` (e.g. `lin a `linux/arm64` image can fail on a Mac, while the images work as expected. Run the tests in a `linux/arm64` virtual machine instead. +**Non-maintained versions:** PHP 7.4, 8.0 and 8.1 (all their images) and NodeJS 18 and 20 (`-legacy-node` bake +groups of the maintained PHP versions) are built and tested by the `legacy-v5.yml` workflow, started manually. + ## Adding a PHP or NodeJS version - `utils/README.blueprint.md`: the version lists of the "Images" section diff --git a/docs/security-php2shell.md b/docs/security-php2shell.md new file mode 100644 index 0000000..e5aafce --- /dev/null +++ b/docs/security-php2shell.md @@ -0,0 +1,80 @@ +# php2shell: CVE-2026-109618 and CVE-2026-109619 + +Two critical vulnerabilities of PHP (CVSS 9.2), disclosed on 2026-10-21, allow remote code execution without +authentication. Exploits are public: update now. + +All the v5 images are fixed on amd64, from PHP 7.4 to 8.5, with or without NodeJS (`-node18` to `-node26`). The +arm64 images are not fixed yet: see [arm64 images](#arm64-images). + +## What to do + +1. Pull the images again and recreate your containers: + + ```bash + $ docker compose pull && docker compose up -d + ``` + +2. Rebuild the images built from ours, without the cache of the base image: + + ```bash + $ docker build --pull -t my-app . + ``` + + In your CI, check that the base image is pulled again and not taken from a cache. + +3. Use the `X.Y-v5-*` tags (e.g. `8.4-v5-apache`). The tags of older PHP patch versions (e.g. `8.4.26-v5-apache`) are + not fixed. + +PHP 7.4, 8.0 and 8.1 images still show 7.4.33, 8.0.30 and 8.1.34 in `php -v`: the fix does not change the version. + +## arm64 images + +The images install PHP from the packages of [packages.sury.org](https://packages.sury.org/php/), which publishes the +fixed packages for amd64 first: the arm64 packages are still being built. Until they are published, all the arm64 +images stay vulnerable, including those of the tags of the fixed PHP patch versions (on arm64, `8.4.27-v5-apache` +still contains PHP 8.4.26). The arm64 images will be rebuilt as soon as the packages are available. + +Check the PHP version of your image: 8.2.35, 8.3.36, 8.4.27, 8.5.12 or newer are fixed. PHP 7.4, 8.0 and 8.1 keep +their version number, and are fixed when the PHP binary contains the fix: + +```bash +$ docker run --rm thecodingmachine/php:8.1-v5-cli bash -c 'grep -c zend_mm_safe_error_ex /usr/bin/php${PHP_VERSION}' +``` + +`0` means that the image is vulnerable. + +In the meantime, an arm64 host can run the amd64 image through emulation (`--platform linux/amd64`, or +`platform: linux/amd64` in a Compose file), which is much slower. + +## NodeJS 16 and older + +The v5 images are not built with NodeJS 16 and older. Use a maintained NodeJS version (22, 24 or 26) when possible. + +If your project still needs an older NodeJS version, start from the slim image and install NodeJS with the +`NODE_VERSION` ARG, written before the `FROM` (see [NodeJS](advanced.md#nodejs)), and the extensions you need with +`PHP_EXTENSIONS` (see [PHP extensions](extensions.md)): + +```Dockerfile +ARG NODE_VERSION=16 +ARG PHP_EXTENSIONS="intl pdo_mysql" +FROM thecodingmachine/php:8.1-v5-slim-apache +``` + +These NodeJS versions no longer get security fixes: plan their upgrade. + +## v4 images + +The v4 images are **not fixed** and will not be. Migrate to v5: see the +[migration notes](../MIGRATING.md#migrating-from-v4-to-v5-images). + +## Bonus: update your containers automatically + +[Watchtower](https://github.com/nicholas-fedor/watchtower) pulls the new versions of the images and recreates the +containers, so that the next security fixes are applied without waiting for you: + +```bash +$ docker run -d --name watchtower -v /var/run/docker.sock:/var/run/docker.sock \ + ghcr.io/nicholas-fedor/watchtower +``` + +The original `containrrr/watchtower` project is no longer maintained: use this fork. diff --git a/extensions/7.4/event b/extensions/7.4/event deleted file mode 120000 index 67569f4..0000000 --- a/extensions/7.4/event +++ /dev/null @@ -1 +0,0 @@ -../core/event \ No newline at end of file diff --git a/extensions/7.4/event/install.sh b/extensions/7.4/event/install.sh new file mode 100755 index 0000000..0335405 --- /dev/null +++ b/extensions/7.4/event/install.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -e +# Sockets is required for event extension to work. +#export EXTENSION="sockets" +export USE_PECL=1 +# The pecl of the Ubuntu php-pear package cannot read the release metadata of event on PHP 7.4: install the archive. +export PECL_EXTENSION=https://pecl.php.net/get/event-3.1.6.tgz +export PHP_EXT_NAME=event +export DEV_DEPENDENCIES="libevent-dev libssl-dev" +export DEPENDENCIES="libevent-2.1-7t64 libevent-core-2.1-7t64 libevent-extra-2.1-7t64 libevent-openssl-2.1-7t64 libevent-pthreads-2.1-7t64 libssl3t64" + +../docker-install.sh diff --git a/extensions/8.0/swoole b/extensions/8.0/swoole deleted file mode 120000 index f20d56e..0000000 --- a/extensions/8.0/swoole +++ /dev/null @@ -1 +0,0 @@ -../core/swoole \ No newline at end of file diff --git a/extensions/8.1/swoole b/extensions/8.1/swoole deleted file mode 120000 index f20d56e..0000000 --- a/extensions/8.1/swoole +++ /dev/null @@ -1 +0,0 @@ -../core/swoole \ No newline at end of file diff --git a/extensions/core/docker-install.sh b/extensions/core/docker-install.sh index c407fce..eacaec5 100755 --- a/extensions/core/docker-install.sh +++ b/extensions/core/docker-install.sh @@ -21,8 +21,9 @@ if [ -n "$EXTENSION" ]; then set -e apt install -y --no-install-recommends php${PHP_VERSION}-$PACKAGE_NAME else - set -e - apt install -y --no-install-recommends php-$PACKAGE_NAME + # The unversioned php-* package would install another PHP version + echo "Package php${PHP_VERSION}-${PACKAGE_NAME} not found" >&2 + exit 1 fi fi fi diff --git a/tests-suite/php-security-patches.sh b/tests-suite/php-security-patches.sh new file mode 100755 index 0000000..8c38fdc --- /dev/null +++ b/tests-suite/php-security-patches.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +. ./config + +############################################################ +## php2shell: CVE-2026-109618 and CVE-2026-109619 +############################################################ +# Fixed upstream in 8.2.35, 8.3.36, 8.4.27 and 8.5.12. +# The end-of-life branches (7.4, 8.0, 8.1) only got a source backport that keeps the +# version number (7.4.33, 8.0.30, 8.1.34): it is detected with the zend_mm_safe_error_ex +# symbol it adds to the Zend engine, in every PHP SAPI shipped in the image. +# The fixed packages are not yet published for arm64: a vulnerable arm64 image is only a warning. +test_php2shellPatched() { + IMAGE="${REPO}:${TAG_PREFIX}${PHP_VERSION}-${BRANCH}-${BRANCH_VARIANT}${ARCH_SUFFIX}" + if [[ "$(printf '%s\n' "$PHP_VERSION" "8.2" | sort -V | head -n 1)" != "8.2" ]]; then + RESULT="$(docker run ${RUN_OPTIONS} --rm "${IMAGE}" \ + bash -c 'dpkg --print-architecture + for BIN in /usr/bin/php${PHP_VERSION} /usr/sbin/php-fpm${PHP_VERSION} /usr/lib/apache2/modules/libphp${PHP_VERSION}.so; do + [ -e "${BIN}" ] || continue + grep -q zend_mm_safe_error_ex "${BIN}" && echo "patched ${BIN}" || echo "vulnerable ${BIN}" + done' 2>/dev/null)" + if [[ "$RESULT" == arm64* ]] && [[ "$RESULT" == *vulnerable* ]]; then + echo "::warning::${IMAGE} (arm64) is vulnerable to CVE-2026-109618: PHP SAPI without the backport" + return + fi + assert_matches "patched /usr/bin/php${PHP_VERSION}" "$RESULT" "PHP CLI not found or not patched" + assert_not_matches "vulnerable" "$RESULT" "PHP SAPI without the CVE-2026-109618 backport" + else + case "${PHP_VERSION}" in + 8.2) FIXED_VERSION="8.2.35" ;; + 8.3) FIXED_VERSION="8.3.36" ;; + 8.4) FIXED_VERSION="8.4.27" ;; + *) FIXED_VERSION="8.5.12" ;; + esac + RESULT="$(docker run ${RUN_OPTIONS} --rm "${IMAGE}" \ + bash -c 'dpkg --print-architecture; php -r "echo PHP_VERSION;"' 2>/dev/null)" + ARCH="$(head -n 1 <<< "$RESULT")" + VERSION="$(tail -n 1 <<< "$RESULT")" + if [[ "$ARCH" == "arm64" ]] && [[ "$(printf '%s\n' "$VERSION" "${FIXED_VERSION}" | sort -V | head -n 1)" != "${FIXED_VERSION}" ]]; then + echo "::warning::${IMAGE} (arm64): PHP ${VERSION} is vulnerable to CVE-2026-109618 (fixed in ${FIXED_VERSION})" + return + fi + assert_equals "${FIXED_VERSION}" "$(printf '%s\n' "$VERSION" "${FIXED_VERSION}" | sort -V | head -n 1)" \ + "PHP ${VERSION} is vulnerable to CVE-2026-109618 (fixed in ${FIXED_VERSION})" + fi +} diff --git a/utils/README.blueprint.md b/utils/README.blueprint.md index c9619bd..e9e175f 100644 --- a/utils/README.blueprint.md +++ b/utils/README.blueprint.md @@ -2,23 +2,12 @@ # General purpose PHP images for Docker -> **New: built-in Apache in the fpm variant (`PHP_FPM_WEB_SERVER=apache`)** +> **Security: php2shell (CVE-2026-109618 and CVE-2026-109619, CVSS 9.2) is fixed in all the v5 amd64 images** > -> The *fpm* variant can now run Apache in front of PHP-FPM, in the same container. It is a drop-in alternative to the -> *apache* variant: same Apache features (`.htaccess`, `APACHE_DOCUMENT_ROOT`, `APACHE_EXTENSION_*`), but PHP runs in -> PHP-FPM instead of `mod_php`. -> -> **Why?** Under the same load ([benchmark](benchmarks/fpm-apache)), the container serves **2 to 3 times more traffic -> with 4 times less memory** (30 vs 10 pages/s on 2 CPUs, ~90 vs ~380 MiB). `mod_php` forces Apache to dedicate a -> whole process embedding PHP to each connection, including idle keep-alive connections and static files. With -> PHP-FPM, Apache uses its threaded MPM (`mpm_event`) and only PHP requests reach the PHP workers. -> -> ```bash -> $ docker run -p 80:80 -e PHP_FPM_WEB_SERVER=apache -v "$PWD":/var/www/html thecodingmachine/php:{{ .Orbit.Images.php_version }}-v5-fpm -> ``` -> -> **[Read more: advantages, constraints of PHP-FPM and migration from the *apache* variant](docs/fpm.md)** -> (for instance, `php_value` directives are not supported in `.htaccess` files). The *apache* variant is still available. +> Pull the images again, recreate your containers and rebuild the images built from ours. The **arm64** images +> (Apple Silicon, AWS Graviton...) are **not fixed yet**: the fixed PHP packages for arm64 are still being built +> upstream, and the images will be updated as soon as they are published. The v4 images are **not** fixed: migrate to +> v5. See [what to do](docs/security-php2shell.md). This repository contains a set of developer-friendly, general purpose PHP images for Docker. @@ -85,6 +74,7 @@ Bugs are fixed on maintained versions only. | 20 | **Not maintained** (end of life: 2026-04-30) | Images of non-maintained versions are still available but no longer receive fixes: use them at your own risk and plan your upgrade. +Critical vulnerabilities may get an exceptional fix (e.g. [php2shell](docs/security-php2shell.md)). ## Usage @@ -139,6 +129,7 @@ Most features are configured with environment variables: | [Processes](docs/processes.md) | Startup commands, cron jobs and daemons run along with the main command | `STARTUP_COMMAND_*`, `CRON_*`, `DAEMON_*` | | [Debugging and profiling](docs/debugging.md) | Xdebug and Blackfire | `PHP_EXTENSION_XDEBUG`, `XDEBUG_CLIENT_HOST`, `BLACKFIRE_AGENT` | | [Advanced features](docs/advanced.md) | Permissions, NodeJS, Composer version, locales, SSH keys, Kubernetes | `DOCKER_USER`, `COMPOSER_VERSION`, `LOCALES` | +| [php2shell](docs/security-php2shell.md) | What to do about CVE-2026-109618 / CVE-2026-109619 | | The slim images install some tools during the build of your image with an ARG before the `FROM`: `PHP_EXTENSIONS`, `INSTALL_CRON`, `INSTALL_LOCALES`, `NODE_VERSION` and `COMPOSER_VERSION`. diff --git a/utils/docker-bake.blueprint.hcl b/utils/docker-bake.blueprint.hcl index 3516949..0f1aa89 100644 --- a/utils/docker-bake.blueprint.hcl +++ b/utils/docker-bake.blueprint.hcl @@ -1,4 +1,4 @@ -{{ $variants := list "apache" "fpm" "cli" }}{{ $versions := list "8.5" "8.4" "8.3" "8.2" "8.1" "8.0" "7.4" }}{{ $nodeVersions := list "26" "24" "22" }}{{ $legacyVersions := list "8.1" "8.0" "7.4" }}{{ $legacyNodeVersions := list "26" "24" "22" "20" }} +{{ $variants := list "apache" "fpm" "cli" }}{{ $versions := list "8.5" "8.4" "8.3" "8.2" "8.1" "8.0" "7.4" }}{{ $nodeVersions := list "26" "24" "22" }}{{ $legacyVersions := list "8.1" "8.0" "7.4" }}{{ $legacyNodeVersions := list "26" "24" "22" "20" "18" }}{{ $oldNodeVersions := list "20" "18" }} group "default" { targets = [ {{range $phpV := $versions}} @@ -12,7 +12,15 @@ group "php{{ $phpV | replace "." "" }}-{{ $variant }}-all" { "php{{ $phpV | replace "." "" }}-{{ $variant }}", {{range $nodeV := $nodes}}"php{{ $phpV | replace "." "" }}-{{ $variant }}-node{{ $nodeV }}",{{end}} ] -}{{end}}{{end}} +}{{ if not (has $phpV $legacyVersions) }} +# NodeJS versions no longer maintained, built by the legacy workflow +group "php{{ $phpV | replace "." "" }}-{{ $variant }}-legacy-node" { + targets = [ + "php{{ $phpV | replace "." "" }}-slim-{{ $variant }}", + "php{{ $phpV | replace "." "" }}-{{ $variant }}", + {{range $nodeV := $oldNodeVersions}}"php{{ $phpV | replace "." "" }}-{{ $variant }}-node{{ $nodeV }}",{{end}} + ] +}{{ end }}{{end}}{{end}} {{range $phpV := $versions}} group "php{{ $phpV | replace "." "" }}" { @@ -46,7 +54,7 @@ target "default" { output = ["type=docker"] # export in local docker } -{{range $phpV := $versions}}{{range $variant := $variants}}{{ $nodes := $nodeVersions }}{{ if has $phpV $legacyVersions }}{{ $nodes = $legacyNodeVersions }}{{ end }} +{{range $phpV := $versions}}{{range $variant := $variants}}{{ $nodes := $legacyNodeVersions }} ########################### ## PHP {{ $phpV }} ###########################