From 92cb1405fa97fa908acd1f043c9eece59b6a7de4 Mon Sep 17 00:00:00 2001 From: Ismo Vuorinen Date: Fri, 27 Feb 2026 04:28:05 +0200 Subject: [PATCH] feat: add PHP 8.5 support and improve CI builds (#81) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(renovate): pin PHP base images to digest-only updates * feat(php85): add PHP 8.5 with Imagick and Composer * ci: add php85 to build and PR matrices * docs: update supported PHP version range to 8.5 * ci: enable GHA build cache for Docker image builds * fix: address CR feedback — fix DOCKERFILE_PATH, renovate match, composer verify, drop python3-dev * ci: use native arm64 runners for arm64 Docker builds * ci: use build-by-digest with manifest merge for multi-arch images Switch from direct per-arch push to a two-phase workflow: 1. Build phase pushes images by digest and uploads artifacts 2. Merge phase creates multi-arch manifest lists per PHP version This ensures proper multi-arch manifest tags instead of last-writer-wins race conditions between arch builds. * fix: remove continue-on-error and suppress SC2046 shellcheck warning Remove continue-on-error from build job so failed arch builds correctly block the merge job from pushing incomplete manifests. Add shellcheck disable directive for intentional word-splitting in manifest creation. --- .github/renovate.json | 11 ++++- .github/workflows/docker-image.yml | 77 +++++++++++++++++++++++++++--- .github/workflows/pr-build.yml | 8 ++-- README.md | 2 +- php85/Dockerfile | 43 +++++++++++++++++ 5 files changed, 129 insertions(+), 12 deletions(-) create mode 100644 php85/Dockerfile diff --git a/.github/renovate.json b/.github/renovate.json index 66f4a27..5f2f168 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -1,4 +1,13 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": ["github>ivuorinen/renovate-config"] + "extends": ["github>ivuorinen/renovate-config"], + "packageRules": [ + { + "description": "Only allow digest updates for PHP base images, not version upgrades", + "matchDatasources": ["docker"], + "matchPackageNames": ["php", "library/php"], + "matchUpdateTypes": ["major", "minor", "patch"], + "enabled": false + } + ] } diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index e74e30a..3e20dfa 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -18,9 +18,8 @@ env: permissions: read-all jobs: - BuildAndRelease: - runs-on: ubuntu-latest - continue-on-error: true + build: + runs-on: ${{ matrix.arch == 'linux/arm64' && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} permissions: contents: read packages: write @@ -28,7 +27,7 @@ jobs: strategy: fail-fast: false matrix: - php: [php74, php80, php81, php82, php83, php84] + php: [php74, php80, php81, php82, php83, php84, php85] arch: ["linux/amd64", "linux/arm64"] steps: @@ -44,10 +43,74 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and Push (${{ matrix.php }} / ${{ matrix.arch }}) + - name: Determine platform pair + id: platform + run: | + echo "pair=$(echo '${{ matrix.arch }}' | tr '/' '-')" >> "$GITHUB_OUTPUT" + + - name: Build and push by digest + id: build uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2 with: file: ${{ matrix.php }}/Dockerfile platforms: ${{ matrix.arch }} - push: true - tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ matrix.php }} + outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true + cache-from: type=gha,scope=${{ matrix.php }}-${{ matrix.arch }} + cache-to: type=gha,mode=max,scope=${{ matrix.php }}-${{ matrix.arch }} + + - name: Export digest + run: | + mkdir -p /tmp/digests + digest="${{ steps.build.outputs.digest }}" + touch "/tmp/digests/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: digests-${{ matrix.php }}-${{ steps.platform.outputs.pair }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 + + merge: + runs-on: ubuntu-latest + needs: [build] + permissions: + contents: read + packages: write + + strategy: + fail-fast: false + matrix: + php: [php74, php80, php81, php82, php83, php84, php85] + + steps: + - name: Download digests + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + with: + path: /tmp/digests + pattern: digests-${{ matrix.php }}-* + merge-multiple: true + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 + + - name: Log in to the Container registry + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Create manifest list and push + working-directory: /tmp/digests + run: | + # shellcheck disable=SC2046 + docker buildx imagetools create \ + --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ matrix.php }} \ + $(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *) + + - name: Inspect image + run: | + docker buildx imagetools inspect \ + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ matrix.php }} diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index aeaa90f..2262a66 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -21,7 +21,7 @@ permissions: read-all jobs: pr-build: name: PR Build - runs-on: ubuntu-latest + runs-on: ${{ matrix.arch == 'linux/arm64' && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} timeout-minutes: 30 permissions: @@ -33,7 +33,7 @@ jobs: strategy: fail-fast: false matrix: - php: [php74, php80, php81, php82, php83, php84] + php: [php74, php80, php81, php82, php83, php84, php85] arch: ["linux/amd64", "linux/arm64"] steps: @@ -69,7 +69,7 @@ jobs: - name: Extract base image id: baseimage run: | - BASE_IMAGE="$(grep -m1 '^FROM ' ${{ env.DOCKERFILE_PATH }} | awk '{print $2}')" + BASE_IMAGE="$(grep -m1 '^FROM ' ./${{ matrix.php }}/Dockerfile | awk '{print $2}')" echo "base_image=$BASE_IMAGE" >> "$GITHUB_OUTPUT" - name: Build Docker image (capture cache usage) @@ -82,6 +82,8 @@ jobs: docker buildx build \ --platform ${{ matrix.arch }} \ --tag "$IMAGE_TAG" \ + --cache-from type=gha,scope=${{ matrix.php }}-${{ matrix.arch }} \ + --cache-to type=gha,mode=max,scope=${{ matrix.php }}-${{ matrix.arch }} \ --progress plain \ --load ./${{ matrix.php }}/ | tee build.log END="$(date +%s)" diff --git a/README.md b/README.md index 43bd9be..fcbc5cd 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # docker-php-with-imagick-multi -PHP 7.4-8.4 with imagick for arm64 and amd64 +PHP 7.4-8.5 with imagick for arm64 and amd64 diff --git a/php85/Dockerfile b/php85/Dockerfile new file mode 100644 index 0000000..09831a0 --- /dev/null +++ b/php85/Dockerfile @@ -0,0 +1,43 @@ +# vim: set ft=dockerfile ts=2 sw=2 sts=2 et: +FROM php:8.5@sha256:2d7a7e055947c398314250fb6d657c85979872c44ce9912b9dc9d1456439b0b3 + +LABEL \ + maintainer="Ismo Vuorinen " \ + version="1.0" \ + description="PHP 8.5 with Imagick and Composer" + +# Install PHP extensions and required libraries +RUN \ + apt-get update \ + && apt-get install -y --no-install-recommends \ + libicu-dev \ + libxml2-dev \ + libfreetype6-dev \ + libjpeg62-turbo-dev \ + libpng-dev \ + libonig-dev \ + libmagickwand-dev \ + unzip \ + && docker-php-ext-configure gd --with-freetype --with-jpeg \ + && docker-php-ext-install -j$(nproc) \ + bcmath \ + intl \ + mbstring \ + pdo \ + xml \ + gd \ + exif \ + && docker-php-ext-configure pcntl \ + && docker-php-ext-install pcntl \ + && yes '' | pecl install imagick \ + && docker-php-ext-enable imagick \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* \ + && curl -sS -o composer-setup.php https://getcomposer.org/installer \ + && EXPECTED_HASH="$(curl -sS https://composer.github.io/installer.sig)" \ + && ACTUAL_HASH="$(php -r "echo hash_file('sha384', 'composer-setup.php');")" \ + && if [ "$EXPECTED_HASH" != "$ACTUAL_HASH" ]; then echo 'Composer installer corrupt'; rm composer-setup.php; exit 1; fi \ + && php composer-setup.php --install-dir=/usr/local/bin --filename=composer \ + && rm composer-setup.php \ + && php --version \ + && composer --version