From 770b6f15311c93b92435d2255ce276119d39125c Mon Sep 17 00:00:00 2001 From: Lucas Mirloup <97196064+lucasmirloup@users.noreply.github.com> Date: Mon, 9 Feb 2026 11:40:26 +0100 Subject: [PATCH 1/2] feat: handle multiple Debian versions, migrate to Docker Bake HCL --- .github/workflows/main.yml | 3 +- Dockerfile | 3 +- Makefile | 4 +-- compose.yaml | 44 --------------------------- docker-bake.hcl | 62 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 68 insertions(+), 48 deletions(-) delete mode 100644 compose.yaml create mode 100644 docker-bake.hcl diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fa68a93..df5612d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -37,4 +37,5 @@ jobs: - name: Build and push uses: docker/bake-action@v6 with: + targets: image-all push: ${{ github.event_name != 'pull_request' }} diff --git a/Dockerfile b/Dockerfile index fae1b4f..646dfd6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,11 @@ # syntax=docker/dockerfile:1 # check=error=true +ARG DEBIAN_VERSION=trixie ARG PHP_VERSION=8.5 FROM mlocati/php-extension-installer:2 AS extension-installer -FROM php:${PHP_VERSION}-fpm AS base +FROM php:${PHP_VERSION}-fpm-${DEBIAN_VERSION} AS base FROM base ARG PHP_EXTENSIONS ARG PHP_TIMEZONE=UTC diff --git a/Makefile b/Makefile index d6e34b1..0446567 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ build: - docker buildx bake --pull --set '*.platform=linux/amd64' + docker buildx bake --pull build-arm: docker run --privileged --rm tonistiigi/binfmt --install all - docker buildx bake --pull --set '*.platform=linux/arm64' + docker buildx bake --pull --set '*.platform=linux/arm64' image-all diff --git a/compose.yaml b/compose.yaml deleted file mode 100644 index 14a717d..0000000 --- a/compose.yaml +++ /dev/null @@ -1,44 +0,0 @@ -services: - php_82: - image: lephare/php:8.2 - build: - args: &build-args - PHP_VERSION: "8.2" - PHP_EXTENSIONS: "@composer apcu exif gd imagick intl memcached opcache pdo_mysql pdo_pgsql pgsql soap xdebug zip" - PHP_TIMEZONE: "Europe/Paris" - x-bake: &build-x-bake - platforms: - - linux/amd64 - - linux/arm64 - - php_83: - image: lephare/php:8.3 - build: - args: - <<: *build-args - PHP_VERSION: "8.3" - x-bake: *build-x-bake - - php_84: - image: lephare/php:8.4 - build: - args: - <<: *build-args - PHP_VERSION: "8.4" - PHP_TIMEZONE: "UTC" - x-bake: *build-x-bake - tags: - - lephare/php:8.4 - - php_85: - image: lephare/php:8.5 - build: - args: - <<: *build-args - PHP_VERSION: "8.5" - PHP_TIMEZONE: "UTC" - x-bake: *build-x-bake - tags: - - lephare/php:8.5 - - lephare/php:8 - - lephare/php:latest diff --git a/docker-bake.hcl b/docker-bake.hcl new file mode 100644 index 0000000..0deb29b --- /dev/null +++ b/docker-bake.hcl @@ -0,0 +1,62 @@ +variable "IMAGE_NAME" { + default = "lephare/php" +} + +variable "DEBIAN_VERSIONS" { + default = "bookworm,trixie" +} + +variable "LATEST_DEBIAN_VERSION" { + default = "trixie" +} + +variable "PHP_VERSIONS" { + default = "8.4,8.5" +} + +variable "LATEST_PHP_VERSION" { + default = "8.5" +} + +target "default" { + matrix = { + debian-version = split(",", DEBIAN_VERSIONS) + php-version = split(",", PHP_VERSIONS) + } + name = "image-local-${replace(php-version, ".", "-")}-${debian-version}" + inherits = ["image-${replace(php-version, ".", "-")}-${debian-version}"] + output = ["type=docker"] +} + +target "image" { + matrix = { + debian-version = split(",", DEBIAN_VERSIONS) + php-version = split(",", PHP_VERSIONS) + } + name = "image-${replace(php-version, ".", "-")}-${debian-version}" + args = { + DEBIAN_VERSION = debian-version + PHP_EXTENSIONS = "@composer apcu exif gd imagick intl memcached opcache pdo_mysql pdo_pgsql pgsql soap zip" + PHP_TIMEZONE = php-version == "8.2" || php-version == "8.3" ? "Europe/Paris" : "UTC" + PHP_VERSION = php-version + } + tags = [ + "${IMAGE_NAME}:${php-version}-${debian-version}", + php-version == LATEST_PHP_VERSION ? "${IMAGE_NAME}:${debian-version}" : "", + debian-version == LATEST_DEBIAN_VERSION ? "${IMAGE_NAME}:${php-version}" : "", + debian-version == LATEST_DEBIAN_VERSION && php-version == LATEST_PHP_VERSION ? "${IMAGE_NAME}:latest" : "" + ] +} + +target "image-all" { + matrix = { + debian-version = split(",", DEBIAN_VERSIONS) + php-version = split(",", PHP_VERSIONS) + } + name = "image-all-${replace(php-version, ".", "-")}-${debian-version}" + inherits = ["image-${replace(php-version, ".", "-")}-${debian-version}"] + platforms = [ + "linux/amd64", + "linux/arm64" + ] +} From d8d30acd688fc354130f6df5b840a0e3a07a3155 Mon Sep 17 00:00:00 2001 From: Lucas Mirloup <97196064+lucasmirloup@users.noreply.github.com> Date: Fri, 27 Feb 2026 17:45:27 +0100 Subject: [PATCH 2/2] feat(ci): Docker: build across multiple GitHub runners Optimize and simplify Dockerfile Disable PHP GD AVIF support on Debian Bullseye because of bugged Google servers See https://github.com/mlocati/docker-php-extension-installer?tab=readme-ov-file#configuration --- .github/workflows/main.yml | 152 +++++++++++++++++++++++++++++++++---- Dockerfile | 32 ++++---- Makefile | 4 +- docker-bake.hcl | 98 ++++++++++++++++-------- 4 files changed, 220 insertions(+), 66 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index df5612d..7e4ab99 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,7 +1,8 @@ -name: Build PHP docker images +name: Build and push lephare/php Docker images + +# Adapted from the Docker documentation "Multi-platform image with GitHub Actions > With Bake": https://docs.docker.com/build/ci/github-actions/multi-platform/#with-bake +# With 2 more matrix levels: Debian version and PHP version. -# Controls when the action will run. Triggers the workflow on push or pull request -# events but only for the master branch on: push: branches: @@ -10,23 +11,61 @@ on: branches: - master schedule: - - cron: '0 12 * * SUN' # every sunday + - cron: '0 12 * * SUN' # every Sunday workflow_dispatch: +env: + REGISTRY_IMAGE: lephare/php + jobs: - build: - name: PHP + prepare: runs-on: ubuntu-latest - steps: - name: Checkout uses: actions/checkout@v6 - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY_IMAGE }} + + - name: Rename meta bake definition file + run: | + mv "${{ steps.meta.outputs.bake-file }}" "${{ runner.temp }}/bake-meta.json" - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + - name: Upload meta bake definition + uses: actions/upload-artifact@v4 + with: + name: bake-meta + path: ${{ runner.temp }}/bake-meta.json + if-no-files-found: error + retention-days: 1 + + build: + needs: + - prepare + strategy: + fail-fast: false + matrix: + debian-version: ['bullseye', 'bookworm', 'trixie'] + php-version: ['8-2', '8-3', '8-4', '8-5'] + platform: ['linux/amd64', 'linux/arm64'] + exclude: + - debian-version: 'bullseye' + php-version: '8-5' + runs-on: ${{ startsWith(matrix.platform, 'linux/arm') && 'ubuntu-24.04-arm' || 'ubuntu-latest' }} + steps: + - name: Prepare + run: | + platform=${{ matrix.platform }} + echo "MATRIX_PAIR=${{ matrix.php-version }}-${{ matrix.debian-version }}-${platform//\//-}" >> $GITHUB_ENV + + - name: Download meta bake definition + uses: actions/download-artifact@v4 + with: + name: bake-meta + path: ${{ runner.temp }} - name: Login to Docker Hub uses: docker/login-action@v3 @@ -34,8 +73,93 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Build and push + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build + id: bake uses: docker/bake-action@v6 with: - targets: image-all - push: ${{ github.event_name != 'pull_request' }} + files: | + ./docker-bake.hcl + cwd://${{ runner.temp }}/bake-meta.json + targets: php-${{ matrix.php-version }}-${{ matrix.debian-version }} + set: | + *.cache-from=type=gha + *.cache-to=type=gha,mode=max + *.output=type=image,push-by-digest=true,name-canonical=true,push=true + *.platform=${{ matrix.platform }} + *.tags=${{ env.REGISTRY_IMAGE }} + + - name: Export digest + run: | + mkdir -p ${{ runner.temp }}/digests + digest="${{ fromJSON(steps.bake.outputs.metadata)[format('php-{0}-{1}', matrix.php-version, matrix.debian-version)]['containerimage.digest'] }}" + touch "${{ runner.temp }}/digests/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ env.MATRIX_PAIR }} + path: ${{ runner.temp }}/digests/* + if-no-files-found: error + retention-days: 1 + + merge: + strategy: + fail-fast: false + matrix: + debian-version: ['bullseye', 'bookworm', 'trixie'] + php-version: ['8-2', '8-3', '8-4', '8-5'] + exclude: + - debian-version: 'bullseye' + php-version: '8-5' + env: + DOCKER_BAKE_TARGET: php-${{ matrix.php-version }}-${{ matrix.debian-version }} + runs-on: ubuntu-latest + needs: + - prepare + - build + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Download meta bake definition + uses: actions/download-artifact@v4 + with: + name: bake-meta + path: ${{ runner.temp }} + + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: ${{ runner.temp }}/digests + pattern: digests-${{ matrix.php-version }}-${{ matrix.debian-version }}-* + merge-multiple: true + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Extract Docker Bake metadata + id: bake-metadata + run: | + echo "tags=$(docker buildx bake ${{ env.DOCKER_BAKE_TARGET }} --print | jq -cr '.target."${{ env.DOCKER_BAKE_TARGET }}".tags')" >>${GITHUB_OUTPUT} + + - name: Create manifest list and push + working-directory: ${{ runner.temp }}/digests + env: + DOCKER_IMAGETOOLS_ARGS: ${{ github.event_name == 'pull_request' && '--dry-run' || '' }} + run: | + docker buildx imagetools create ${{ env.DOCKER_IMAGETOOLS_ARGS}} $(echo '${{ steps.bake-metadata.outputs.tags }}' | jq -cr 'map(select(startswith("${{ env.REGISTRY_IMAGE }}")) | "-t " + .) | join(" ")') \ + $(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *) + + - name: Inspect image + if: ${{ github.event_name != 'pull_request' }} + run: | + docker buildx imagetools inspect $(echo '${{ steps.bake-metadata.outputs.tags }}' | jq -cr '.[0]') diff --git a/Dockerfile b/Dockerfile index 646dfd6..c89ee42 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,28 +4,26 @@ ARG DEBIAN_VERSION=trixie ARG PHP_VERSION=8.5 -FROM mlocati/php-extension-installer:2 AS extension-installer -FROM php:${PHP_VERSION}-fpm-${DEBIAN_VERSION} AS base -FROM base +FROM php:${PHP_VERSION}-fpm-${DEBIAN_VERSION} + ARG PHP_EXTENSIONS ARG PHP_TIMEZONE=UTC -WORKDIR /usr/local/etc/php -COPY --link symfony.ini ./conf.d/ - -# override symfony.ini timezone placeholder -RUN sed -i "s@PHP_TIMEZONE@${PHP_TIMEZONE}@g" /usr/local/etc/php/conf.d/symfony.ini - -COPY --link symfony.pool.conf ./pool.d/ - -COPY --from=extension-installer /usr/bin/install-php-extensions /usr/local/bin +COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ -RUN apt-get update && apt-get install -y --no-install-recommends \ - git=1:2.* \ - && rm -rf /var/lib/apt/lists/* +COPY --link symfony.ini /usr/local/etc/php/conf.d/ -RUN install-php-extensions ${PHP_EXTENSIONS} +COPY --link symfony.pool.conf /usr/local/etc/php/pool.d/ -RUN usermod -u 1000 www-data && usermod -G staff www-data +# Override symfony.ini timezone placeholder +# PHP GD AVIF support on Debian Bullseye requires compiling libaom, downloaded from Google servers which are bugged at the moment, so we disable it with IPE_GD_WITHOUTAVIF=1 +RUN sed -i "s@PHP_TIMEZONE@${PHP_TIMEZONE}@g" /usr/local/etc/php/conf.d/symfony.ini \ + && apt-get update \ + && apt-get upgrade -y \ + && apt-get install -y --no-install-recommends git \ + && IPE_GD_WITHOUTAVIF=1 install-php-extensions ${PHP_EXTENSIONS} \ + && rm -rf /var/lib/apt/lists/* \ + && usermod -u 1000 -G staff www-data \ + && groupmod -g 1000 www-data WORKDIR /var/www/symfony diff --git a/Makefile b/Makefile index 0446567..a0cd649 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ build: - docker buildx bake --pull + docker buildx bake --pull --set '*.platform=linux/amd64' 'php-*' build-arm: docker run --privileged --rm tonistiigi/binfmt --install all - docker buildx bake --pull --set '*.platform=linux/arm64' image-all + docker buildx bake --pull --set '*.platform=linux/arm64' 'php-*' diff --git a/docker-bake.hcl b/docker-bake.hcl index 0deb29b..0bc9b58 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -2,61 +2,93 @@ variable "IMAGE_NAME" { default = "lephare/php" } -variable "DEBIAN_VERSIONS" { - default = "bookworm,trixie" +variable "DEBIAN_LATEST_VERSION" { + default = "trixie" } -variable "LATEST_DEBIAN_VERSION" { - default = "trixie" +variable "PHP_LATEST_VERSION" { + default = "8.5" } -variable "PHP_VERSIONS" { - default = "8.4,8.5" +// Special target: https://github.com/docker/metadata-action#bake-definition +target "docker-metadata-action" { + args = { + PHP_EXTENSIONS = "@composer apcu exif gd imagick intl memcached opcache pdo_mysql pdo_pgsql pgsql soap zip" + } + platforms = [ + "linux/amd64", + "linux/arm64" + ] } -variable "LATEST_PHP_VERSION" { - default = "8.5" +target "php-8-2" { + inherits = ["docker-metadata-action"] + name = "php-8-2-${debian-version}" + matrix = { + debian-version = ["bullseye", "bookworm", "trixie"] + } + args = { + DEBIAN_VERSION = debian-version + PHP_TIMEZONE = "Europe/Paris" + PHP_VERSION = "8.2" + } + tags = [ + "${IMAGE_NAME}:8.2-${debian-version}", + debian-version == DEBIAN_LATEST_VERSION ? "${IMAGE_NAME}:8.2" : "", + ] } -target "default" { +target "php-8-3" { + inherits = ["docker-metadata-action"] + name = "php-8-3-${debian-version}" matrix = { - debian-version = split(",", DEBIAN_VERSIONS) - php-version = split(",", PHP_VERSIONS) + debian-version = ["bullseye", "bookworm", "trixie"] + } + args = { + DEBIAN_VERSION = debian-version + PHP_TIMEZONE = "Europe/Paris" + PHP_VERSION = "8.3" } - name = "image-local-${replace(php-version, ".", "-")}-${debian-version}" - inherits = ["image-${replace(php-version, ".", "-")}-${debian-version}"] - output = ["type=docker"] + tags = [ + "${IMAGE_NAME}:8.3-${debian-version}", + debian-version == DEBIAN_LATEST_VERSION ? "${IMAGE_NAME}:8.3" : "", + ] } -target "image" { +target "php-8-4" { + inherits = ["docker-metadata-action"] + name = "php-8-4-${debian-version}" matrix = { - debian-version = split(",", DEBIAN_VERSIONS) - php-version = split(",", PHP_VERSIONS) + debian-version = ["bullseye", "bookworm", "trixie"] } - name = "image-${replace(php-version, ".", "-")}-${debian-version}" args = { DEBIAN_VERSION = debian-version - PHP_EXTENSIONS = "@composer apcu exif gd imagick intl memcached opcache pdo_mysql pdo_pgsql pgsql soap zip" - PHP_TIMEZONE = php-version == "8.2" || php-version == "8.3" ? "Europe/Paris" : "UTC" - PHP_VERSION = php-version + PHP_TIMEZONE = "UTC" + PHP_VERSION = "8.4" } tags = [ - "${IMAGE_NAME}:${php-version}-${debian-version}", - php-version == LATEST_PHP_VERSION ? "${IMAGE_NAME}:${debian-version}" : "", - debian-version == LATEST_DEBIAN_VERSION ? "${IMAGE_NAME}:${php-version}" : "", - debian-version == LATEST_DEBIAN_VERSION && php-version == LATEST_PHP_VERSION ? "${IMAGE_NAME}:latest" : "" + "${IMAGE_NAME}:8.4-${debian-version}", + debian-version == DEBIAN_LATEST_VERSION ? "${IMAGE_NAME}:8.4" : "", ] } -target "image-all" { +target "php-8-5" { + inherits = ["docker-metadata-action"] + name = "php-8-5-${debian-version}" matrix = { - debian-version = split(",", DEBIAN_VERSIONS) - php-version = split(",", PHP_VERSIONS) + debian-version = ["bookworm", "trixie"] } - name = "image-all-${replace(php-version, ".", "-")}-${debian-version}" - inherits = ["image-${replace(php-version, ".", "-")}-${debian-version}"] - platforms = [ - "linux/amd64", - "linux/arm64" + args = { + DEBIAN_VERSION = debian-version + PHP_TIMEZONE = "UTC" + PHP_VERSION = "8.5" + } + tags = [ + "${IMAGE_NAME}:8.5-${debian-version}", + debian-version == DEBIAN_LATEST_VERSION ? "${IMAGE_NAME}:8.5" : "", + "${IMAGE_NAME}:8-${debian-version}", + debian-version == DEBIAN_LATEST_VERSION ? "${IMAGE_NAME}:8" : "", + "${IMAGE_NAME}:${debian-version}", + debian-version == DEBIAN_LATEST_VERSION ? "${IMAGE_NAME}:latest" : "" ] }