Skip to content

Merge pull request #130 from sparkfabrik/chore/add-nginx-1-30-3 #376

Merge pull request #130 from sparkfabrik/chore/add-nginx-1-30-3

Merge pull request #130 from sparkfabrik/chore/add-nginx-1-30-3 #376

name: Docker
on:
pull_request:
push:
branches: "feature/d8"
env:
IMAGE_NAME: docker-php-drupal-nginx
TAG_D8: d8
TAG_D8_RL: d8-rootless
# Single source of truth for the nginx base versions built across every job.
# Add or remove a version here and the test, build and merge matrices follow.
NGINX_TAGS: >-
[
"1.30.3-alpine-slim",
"1.30.2-alpine-slim",
"1.26.0-alpine-slim",
"1.25.5-alpine-slim",
"1.25.3-alpine-slim",
"1.25.1-alpine-slim"
]
# The version whose images also receive the rolling :d8 / :d8-rootless tags.
PRIMARY_TAG: "1.30.3-alpine-slim"
# Least-privilege token: read the repo for checkout, push images to GHCR.
permissions:
contents: read
packages: write
jobs:
prepare:
runs-on: ubuntu-latest
outputs:
tags: ${{ steps.set.outputs.tags }}
steps:
- id: set
# The `env` context is not available to `strategy.matrix` expressions, so
# surface NGINX_TAGS as a job output that downstream matrices can read.
run: |
TAGS=$(jq -c . <<< "${NGINX_TAGS}")
echo "tags=${TAGS}" >> "$GITHUB_OUTPUT"
test:
needs: prepare
runs-on: ubuntu-latest
name: test ${{ matrix.version }}${{ matrix.flavour }}
strategy:
fail-fast: false
matrix:
version: ${{ fromJSON(needs.prepare.outputs.tags) }}
flavour: ["", "-rootless"]
steps:
- uses: actions/checkout@v6
# The test job builds a single native (amd64) image with --load, so it
# does not need QEMU.
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Build and test the image
env:
SCOPE: ${{ matrix.version }}${{ matrix.flavour }}
run: |
USER_ARG="root"
IMAGE_USER="root"
BASE_TESTS_PORT="80"
if [ "${{ matrix.flavour }}" = "-rootless" ]; then
USER_ARG="1001"
IMAGE_USER="unknown uid 1001"
BASE_TESTS_PORT="8080"
fi
docker buildx build --load . \
--file Dockerfile \
--cache-from type=gha,scope=${SCOPE} \
--cache-to type=gha,mode=max,scope=${SCOPE} \
--tag "${IMAGE_NAME}:${{ matrix.version }}.d8${{ matrix.flavour }}" \
--build-arg NGINX_IMAGE_TAG=${{ matrix.version }} \
--build-arg user="${USER_ARG}"
IMAGE_TAG="${{ matrix.version }}.d8${{ matrix.flavour }}" \
IMAGE_USER="${IMAGE_USER}" \
BASE_TESTS_PORT="${BASE_TESTS_PORT}" \
./tests/tests.sh
build:
needs: [prepare, test]
if: github.ref == 'refs/heads/feature/d8'
runs-on: ${{ matrix.platform.runner }}
name: build ${{ matrix.version }}${{ matrix.flavour }} (${{ matrix.platform.arch }})
strategy:
fail-fast: false
matrix:
version: ${{ fromJSON(needs.prepare.outputs.tags) }}
flavour: ["", "-rootless"]
# Each platform is a self-contained object (name/runner/arch) so the
# runner and arch are always defined; amd64 builds on ubuntu-latest and
# arm64 on a native ubuntu-24.04-arm runner (no QEMU emulation).
platform:
- name: linux/amd64
runner: ubuntu-latest
arch: amd64
- name: linux/arm64
runner: ubuntu-24.04-arm
arch: arm64
steps:
- uses: actions/checkout@v6
# Refs https://github.com/docker/login-action#github-container-registry
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Build and push image digest
run: |
IMAGE_ID=ghcr.io/${{ github.repository_owner }}/${IMAGE_NAME}
# Change all uppercase to lowercase.
IMAGE_ID=$(echo "${IMAGE_ID}" | tr '[A-Z]' '[a-z]')
echo "IMAGE_ID=${IMAGE_ID}"
SCOPE="${{ matrix.version }}${{ matrix.flavour }}-${{ matrix.platform.arch }}"
USER_ARG="root"
if [ "${{ matrix.flavour }}" = "-rootless" ]; then
USER_ARG="1001"
fi
# Build single-arch and push by digest (no tag); the merge job
# assembles the final multi-arch tags from the per-arch digests.
docker buildx build --push . \
--file Dockerfile \
--platform ${{ matrix.platform.name }} \
--build-arg NGINX_IMAGE_TAG=${{ matrix.version }} \
--build-arg user="${USER_ARG}" \
--cache-from type=gha,scope=${SCOPE} \
--cache-to type=gha,mode=max,scope=${SCOPE} \
--output type=image,name=${IMAGE_ID},push-by-digest=true,name-canonical=true \
--metadata-file image-meta.json
mkdir -p digests
# -e fails the job if the digest key is absent, instead of writing a
# literal "null" that would later produce an invalid image@null ref.
jq -e -r '.["containerimage.digest"]' image-meta.json > "digests/image-${{ matrix.platform.arch }}"
- name: Upload digests
uses: actions/upload-artifact@v4
with:
# Use a non-empty flavour label ("default"/"rootless") so the
# non-rootless artifact name is not a prefix of the rootless one;
# otherwise the merge download glob would match both flavours.
name: digests-${{ matrix.version }}-${{ matrix.flavour == '-rootless' && 'rootless' || 'default' }}-${{ matrix.platform.arch }}
path: digests/*
if-no-files-found: error
retention-days: 1
merge:
needs: [prepare, build]
if: github.ref == 'refs/heads/feature/d8'
runs-on: ubuntu-latest
name: merge ${{ matrix.version }}${{ matrix.flavour }}
strategy:
fail-fast: false
matrix:
version: ${{ fromJSON(needs.prepare.outputs.tags) }}
flavour: ["", "-rootless"]
steps:
# Refs https://github.com/docker/login-action#github-container-registry
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Download digests
uses: actions/download-artifact@v4
with:
path: digests
pattern: digests-${{ matrix.version }}-${{ matrix.flavour == '-rootless' && 'rootless' || 'default' }}-*
merge-multiple: true
- name: Create and push multi-arch manifest
run: |
IMAGE_ID=ghcr.io/${{ github.repository_owner }}/${IMAGE_NAME}
IMAGE_ID=$(echo "${IMAGE_ID}" | tr '[A-Z]' '[a-z]')
TAG="${{ matrix.version }}.d8${{ matrix.flavour }}"
# Do not let an unmatched glob expand to the literal pattern: if the
# digests were not downloaded the ref array stays empty and we fail loudly.
shopt -s nullglob
refs=()
for f in digests/image-*; do refs+=("${IMAGE_ID}@$(cat "${f}")"); done
if [ "${#refs[@]}" -eq 0 ]; then
echo "No digests found for ${TAG}; expected one digest per architecture." >&2
exit 1
fi
# Versioned tag: :<version>.d8[-rootless]
docker buildx imagetools create -t "${IMAGE_ID}:${TAG}" "${refs[@]}"
# Rolling tag from the primary version: :d8 / :d8-rootless
if [ "${{ matrix.version }}" = "${PRIMARY_TAG}" ]; then
ROLLING="${TAG_D8}"
if [ "${{ matrix.flavour }}" = "-rootless" ]; then
ROLLING="${TAG_D8_RL}"
fi
docker buildx imagetools create -t "${IMAGE_ID}:${ROLLING}" "${refs[@]}"
fi