diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fa68a93..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@v4 + uses: actions/checkout@v6 + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY_IMAGE }} - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + - 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,7 +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: - 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 fae1b4f..c89ee42 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,30 +1,29 @@ # 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 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 d6e34b1..a0cd649 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ build: - docker buildx bake --pull --set '*.platform=linux/amd64' + 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' + docker buildx bake --pull --set '*.platform=linux/arm64' 'php-*' 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..0bc9b58 --- /dev/null +++ b/docker-bake.hcl @@ -0,0 +1,94 @@ +variable "IMAGE_NAME" { + default = "lephare/php" +} + +variable "DEBIAN_LATEST_VERSION" { + default = "trixie" +} + +variable "PHP_LATEST_VERSION" { + default = "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" + ] +} + +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 "php-8-3" { + inherits = ["docker-metadata-action"] + name = "php-8-3-${debian-version}" + matrix = { + debian-version = ["bullseye", "bookworm", "trixie"] + } + args = { + DEBIAN_VERSION = debian-version + PHP_TIMEZONE = "Europe/Paris" + PHP_VERSION = "8.3" + } + tags = [ + "${IMAGE_NAME}:8.3-${debian-version}", + debian-version == DEBIAN_LATEST_VERSION ? "${IMAGE_NAME}:8.3" : "", + ] +} + +target "php-8-4" { + inherits = ["docker-metadata-action"] + name = "php-8-4-${debian-version}" + matrix = { + debian-version = ["bullseye", "bookworm", "trixie"] + } + args = { + DEBIAN_VERSION = debian-version + PHP_TIMEZONE = "UTC" + PHP_VERSION = "8.4" + } + tags = [ + "${IMAGE_NAME}:8.4-${debian-version}", + debian-version == DEBIAN_LATEST_VERSION ? "${IMAGE_NAME}:8.4" : "", + ] +} + +target "php-8-5" { + inherits = ["docker-metadata-action"] + name = "php-8-5-${debian-version}" + matrix = { + debian-version = ["bookworm", "trixie"] + } + 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" : "" + ] +}