diff --git a/.github/scripts/ci-build-matrix-test.sh b/.github/scripts/ci-build-matrix-test.sh new file mode 100755 index 0000000000..d6e82b781b --- /dev/null +++ b/.github/scripts/ci-build-matrix-test.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" + +assert_output() { + local kind="$1" + local full_matrix="$2" + local expected="$3" + local actual + + actual="$(METADATA="${TEST_METADATA}" REBUILD_VARIANTS="${REBUILD_VARIANTS:-}" "${SCRIPT_DIR}/ci-build-matrix.sh" "${kind}" "${full_matrix}")" + if [[ "${actual}" != "${expected}" ]]; then + printf 'expected:\n%s\nactual:\n%s\n' "${expected}" "${actual}" >&2 + return 1 + fi +} + +TEST_METADATA='{ + "group": { + "default": { + "targets": [ + "builder-php-8-2-bookworm", + "builder-php-8-2-trixie", + "builder-php-8-3-bookworm", + "builder-php-8-3-trixie", + "runner-php-8-2-bookworm", + "runner-php-8-2-trixie", + "runner-php-8-3-bookworm", + "runner-php-8-3-trixie" + ] + } + }, + "target": { + "builder-php-8-2-bookworm": { + "platforms": ["linux/amd64", "linux/arm64"] + }, + "static-builder-musl": { + "platforms": ["linux/amd64", "linux/arm64"] + } + } +}' + +assert_output docker false $'variants=["php-8-2-bookworm","php-8-3-bookworm"]\nplatforms=["linux/amd64"]' +assert_output docker true $'variants=["php-8-2-bookworm","php-8-2-trixie","php-8-3-bookworm","php-8-3-trixie"]\nplatforms=["linux/amd64","linux/arm64"]' +assert_output static false 'platforms=["linux/amd64"]' +assert_output static true 'platforms=["linux/amd64","linux/arm64"]' + +# On scheduled rebuilds REBUILD_VARIANTS narrows the docker matrix to the changed bases only. +REBUILD_VARIANTS='["php-8-2-trixie","php-8-3-bookworm"]' \ + assert_output docker true $'variants=["php-8-2-trixie","php-8-3-bookworm"]\nplatforms=["linux/amd64","linux/arm64"]' +# The empty-array sentinel leaves the matrix untouched. +REBUILD_VARIANTS='[]' \ + assert_output docker true $'variants=["php-8-2-bookworm","php-8-2-trixie","php-8-3-bookworm","php-8-3-trixie"]\nplatforms=["linux/amd64","linux/arm64"]' diff --git a/.github/scripts/ci-build-matrix.sh b/.github/scripts/ci-build-matrix.sh new file mode 100755 index 0000000000..261f98d865 --- /dev/null +++ b/.github/scripts/ci-build-matrix.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Platforms built on reduced pull-request runs. +readonly REDUCED_PLATFORMS='["linux/amd64"]' +# Sentinel emitted by docker-compute-fingerprints.sh when no variant needs a rebuild. +readonly EMPTY_JSON_ARRAY='[]' + +write_output() { + if [[ -n "${GITHUB_OUTPUT:-}" ]]; then + echo "$1" >>"${GITHUB_OUTPUT}" + else + echo "$1" + fi +} + +kind="${1:?matrix kind is required}" +full_matrix="${2:?full matrix flag is required}" + +case "${kind}" in +docker) + if [[ "${full_matrix}" == "true" ]]; then + variants="$( + jq -c '.group.default.targets | map(sub("runner-|builder-"; "")) | unique' <<<"${METADATA}" + )" + platforms="$(jq -c 'first(.target[]) | .platforms' <<<"${METADATA}")" + else + variants="$( + jq -c '.group.default.targets + | map(sub("runner-|builder-"; "")) + | unique + | map(select(endswith("-bookworm")))' <<<"${METADATA}" + )" + platforms="${REDUCED_PLATFORMS}" + fi + + # On scheduled rebuilds, only build the variants whose base images changed. + if [[ -n "${REBUILD_VARIANTS:-}" && "${REBUILD_VARIANTS}" != "${EMPTY_JSON_ARRAY}" ]]; then + variants="$( + jq -c --argjson rebuild "${REBUILD_VARIANTS}" \ + 'map(select(. as $v | $rebuild | index($v)))' <<<"${variants}" + )" + fi + + write_output "variants=${variants}" + write_output "platforms=${platforms}" + ;; +static) + if [[ "${full_matrix}" == "true" ]]; then + platforms="$(jq -c 'first(.target[]) | .platforms' <<<"${METADATA}")" + else + platforms="${REDUCED_PLATFORMS}" + fi + + write_output "platforms=${platforms}" + ;; +*) + echo "unknown matrix kind: ${kind}" >&2 + exit 1 + ;; +esac diff --git a/.github/scripts/docker-compute-fingerprints.sh b/.github/scripts/docker-compute-fingerprints.sh index 259d01b993..e3f97dda28 100755 --- a/.github/scripts/docker-compute-fingerprints.sh +++ b/.github/scripts/docker-compute-fingerprints.sh @@ -71,6 +71,17 @@ main() { local METADATA METADATA="$(PHP_VERSION="${PHP_VERSION}" docker buildx bake --print | jq -c)" + # On reduced pull-request runs only the Bookworm variants are built, so restrict the + # metadata to them and avoid fingerprinting (and failing on) base images we never build. + if [[ "${FULL_BUILD_MATRIX:-true}" != "true" ]]; then + METADATA="$( + jq -c ' + .group.default.targets |= map(select(endswith("-bookworm"))) + | .target |= with_entries(select(.key | endswith("-bookworm"))) + ' <<<"${METADATA}" + )" + fi + # Collect the base images (docker-image:// contexts) of each variant. The variant key # is derived from the php-base ref (e.g. "php:8.4.23-zts-trixie" -> "8.4.23-trixie") and # matches the "${php-version}-${os}" keys expected by docker-bake.hcl for BASE_FINGERPRINTS. diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index e7ee3c98db..00dc191d51 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -9,6 +9,7 @@ on: - main paths: - "docker-bake.hcl" + - ".github/scripts/ci-build-matrix.sh" - ".github/workflows/docker.yaml" - "**cgo.go" - "**Dockerfile" @@ -73,6 +74,7 @@ jobs: id: check env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + FULL_BUILD_MATRIX: ${{ github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'full-build-matrix') }} run: ./.github/scripts/docker-compute-fingerprints.sh - name: Create variants matrix if: ${{ !fromJson(steps.check.outputs.skip) }} @@ -81,21 +83,14 @@ jobs: run: | set -e METADATA="$(docker buildx bake --print | jq -c)" - variants="$(jq -c '.group.default.targets|map(sub("runner-|builder-"; ""))|unique' <<< "${METADATA}")" - # On scheduled rebuilds, only build the variants whose base images changed - if [[ -n "${REBUILD_VARIANTS}" && "${REBUILD_VARIANTS}" != "[]" ]]; then - variants="$(jq -c --argjson rebuild "${REBUILD_VARIANTS}" 'map(select(. as $v | $rebuild | index($v)))' <<< "${variants}")" - fi - { - echo metadata="${METADATA}" - echo variants="${variants}" - echo platforms="$(jq -c 'first(.target[]) | .platforms' <<< "${METADATA}")" - } >> "${GITHUB_OUTPUT}" + echo metadata="${METADATA}" >> "${GITHUB_OUTPUT}" + METADATA="${METADATA}" ./.github/scripts/ci-build-matrix.sh docker "${FULL_BUILD_MATRIX}" env: SHA: ${{ github.sha }} VERSION: ${{ (github.ref_type == 'tag' && github.ref_name) || steps.check.outputs.ref || 'dev' }} PHP_VERSION: ${{ steps.check.outputs.php_version }} REBUILD_VARIANTS: ${{ steps.check.outputs.rebuild_variants }} + FULL_BUILD_MATRIX: ${{ github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'full-build-matrix') }} build: runs-on: ${{ startsWith(matrix.platform, 'linux/arm') && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} needs: diff --git a/.github/workflows/static.yaml b/.github/workflows/static.yaml index 61854f9edf..dee0f79091 100644 --- a/.github/workflows/static.yaml +++ b/.github/workflows/static.yaml @@ -10,6 +10,7 @@ on: - main paths: - "docker-bake.hcl" + - ".github/scripts/ci-build-matrix.sh" - ".github/workflows/static.yaml" - "**cgo.go" - "**Dockerfile" @@ -74,14 +75,13 @@ jobs: run: | METADATA="$(docker buildx bake --print static-builder-musl | jq -c)" GNU_METADATA="$(docker buildx bake --print static-builder-gnu | jq -c)" - { - echo metadata="${METADATA}" - echo platforms="$(jq -c 'first(.target[]) | .platforms' <<< "${METADATA}")" - echo gnu_metadata="${GNU_METADATA}" - } >> "${GITHUB_OUTPUT}" + echo metadata="${METADATA}" >> "${GITHUB_OUTPUT}" + METADATA="${METADATA}" ./.github/scripts/ci-build-matrix.sh static "${FULL_BUILD_MATRIX}" + echo gnu_metadata="${GNU_METADATA}" >> "${GITHUB_OUTPUT}" env: SHA: ${{ github.sha }} VERSION: ${{ steps.check.outputs.ref || 'dev' }} + FULL_BUILD_MATRIX: ${{ github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'full-build-matrix') }} build-linux-musl: permissions: