From 502db2080a719551cb285cbc18d500c4fad92830 Mon Sep 17 00:00:00 2001 From: ToshY <31921460+ToshY@users.noreply.github.com> Date: Fri, 4 Sep 2026 19:45:57 +0200 Subject: [PATCH] missing arch should not fail entire build; skip image build instead --- .github/workflows/release.yml | 67 ++++++++++++++++++++++++++++++++--- AGENTS.md | 2 ++ docs/usage.md | 2 +- 3 files changed, 65 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 717130d..a23dba8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,6 +43,8 @@ jobs: outputs: variants: ${{ steps.matrix.outputs.variants }} platforms: ${{ steps.matrix.outputs.platforms }} + build_matrix: ${{ steps.matrix.outputs.build_matrix }} + build_variants: ${{ steps.matrix.outputs.build_variants }} targets: ${{ steps.matrix.outputs.targets }} flavors: ${{ steps.matrix.outputs.flavors }} metadata: ${{ steps.matrix.outputs.metadata }} @@ -154,12 +156,67 @@ jobs: VARIANTS="$(jq -c --arg re "$SUFFIX_REGEX" '.group.default.targets|map(sub($re; ""))|unique' <<< "${METADATA}")" FLAVORS="$(jq -c 'map(split("-")[-2])|unique' <<< "${VARIANTS}")" PLATFORMS="$(jq -c 'first(.target[]) | .platforms' <<< "${METADATA}")" + + # In case an arch is missing for a base image, filter it out and warn, but continue. + BUILD_MATRIX='[]' + while read -r variant; do + TARGET_DEF="$(jq -c --arg v "$variant" 'first(.target | to_entries[] | select(.key | startswith($v + "-")) | .value)' <<< "${METADATA}")" + WANTED="$(jq -c '.platforms' <<< "${TARGET_DEF}")" + BASE_IMAGE="$(jq -r '.contexts["php-base"] | sub("^docker-image://"; "")' <<< "${TARGET_DEF}")" + case "${BASE_IMAGE}" in + */*) BASE_REF="${BASE_IMAGE}" ;; + *) BASE_REF="docker.io/library/${BASE_IMAGE}" ;; + esac + + AVAILABLE='[]' + for attempt in 1 2 3; do + if RAW_MANIFEST="$(skopeo inspect --raw "docker://${BASE_REF}")"; then + # Collect "os/arch" and "os/arch/variant" so bake's `linux/arm64` + # matches the upstream `linux/arm64/v8` manifest entry. + AVAILABLE="$(jq -c ' + [ (.manifests // [])[] + | .platform // empty + | select(.architecture != "unknown") + | (.os + "/" + .architecture) as $p + | if (.variant // "") != "" then ($p + "/" + .variant), $p else $p end + ] | unique' <<< "${RAW_MANIFEST}")" + break + fi + echo "Attempt ${attempt}/3 to inspect '${BASE_REF}' failed." + sleep "$((attempt * 5))" + done + + if [ "$(jq 'length' <<< "${AVAILABLE}")" -eq 0 ]; then + echo "::warning::Could not resolve platforms for '${BASE_REF}'; keeping all platforms for '${variant}'." + AVAILABLE="${WANTED}" + fi + + SUPPORTED="$(jq -c --argjson avail "${AVAILABLE}" '[.[] | select(. as $x | $avail | index($x))]' <<< "${WANTED}")" + SKIPPED="$(jq -r --argjson avail "${AVAILABLE}" '[.[] | select(. as $x | $avail | index($x) | not)] | join(", ")' <<< "${WANTED}")" + if [ -n "${SKIPPED}" ]; then + echo "::warning::Upstream image '${BASE_REF}' has no manifest for: ${SKIPPED}. Skipping those platforms for '${variant}'." + fi + if [ "$(jq 'length' <<< "${SUPPORTED}")" -eq 0 ]; then + echo "::warning::Upstream image '${BASE_REF}' provides none of the requested platforms; skipping variant '${variant}' entirely." + continue + fi + echo "Platforms for '${variant}': $(jq -r 'join(", ")' <<< "${SUPPORTED}")" + + BUILD_MATRIX="$(jq -c --arg v "$variant" --argjson p "${SUPPORTED}" '. + [$p[] | {variant: $v, platform: .}]' <<< "${BUILD_MATRIX}")" + done < <(jq -r '.[]' <<< "${VARIANTS}") + + # `merge` must only fan out over variants that actually produced digests, + # otherwise it would look for artifacts that were never uploaded. + BUILD_VARIANTS="$(jq -c '[.[].variant] | unique' <<< "${BUILD_MATRIX}")" + { echo metadata="$METADATA" echo targets="$TARGETS" echo flavors="$FLAVORS" echo variants="$VARIANTS" echo platforms="$PLATFORMS" + echo build_matrix="$BUILD_MATRIX" + echo build_variants="$BUILD_VARIANTS" } >> "${GITHUB_OUTPUT}" env: SHA: ${{ github.sha }} @@ -170,15 +227,15 @@ jobs: runs-on: ${{ startsWith(matrix.platform, 'linux/arm') && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} needs: - prepare - if: ${{ needs.prepare.outputs.php_versions != '' }} + if: ${{ needs.prepare.outputs.php_versions != '' && needs.prepare.outputs.build_matrix != '[]' }} permissions: contents: read packages: write strategy: fail-fast: false matrix: - variant: ${{ fromJson(needs.prepare.outputs.variants) }} - platform: ${{ fromJson(needs.prepare.outputs.platforms) }} + # Variants whose upstream `php-base` image lacks a platform are excluded by the `prepare` job. + include: ${{ fromJson(needs.prepare.outputs.build_matrix) }} steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -270,7 +327,7 @@ jobs: merge: runs-on: ubuntu-24.04 - if: github.event_name != 'pull_request' + if: ${{ github.event_name != 'pull_request' && needs.prepare.outputs.build_variants != '[]' }} needs: - prepare - build @@ -280,7 +337,7 @@ jobs: strategy: fail-fast: false matrix: - variant: ${{ fromJson(needs.prepare.outputs.variants) }} + variant: ${{ fromJson(needs.prepare.outputs.build_variants) }} target: ${{ fromJson(needs.prepare.outputs.targets) }} steps: - name: Download digests diff --git a/AGENTS.md b/AGENTS.md index 780dde7..1c805c9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -52,6 +52,8 @@ - Bake target naming convention is `php----` and `` may itself contain `-` (for example `otel-ffmpeg`). Always strip the target suffix using the metadata-derived list with a longest-first regex; never use positional `split("-")` indexes on the full target name. - Per-platform digest artifacts use a `--` (double-hyphen) delimiter: `digest----`. The merge job's `actions/download-artifact` `pattern` (`digest----*`) relies on this so variant boundaries are unambiguous. Do not introduce a variant or platform name containing `--`. - When adding a new target: update the `Dockerfile` stage and `docker-bake.hcl` `TARGETS`. The `release.yml` workflow picks it up automatically. Then update `security.yml` scan matrix and docs (`docs/usage.md`, `docs/images.md`). +- `docker-bake.hcl` declares the *desired* platforms (`linux/amd64`, `linux/386`, `linux/arm/v7`, `linux/arm64`), but upstream `php:--` images do not always publish all of them: an arch disappears from the manifest list when its Debian base image is missing for that build (see [docker-library/php#1683](https://github.com/docker-library/php/issues/1683) — e.g. `php:8.5.10-zts-trixie` has no `linux/386` while `php:8.5.10-cli-trixie` does). The `prepare` job therefore inspects each variant's `php-base` manifest with `skopeo inspect --raw`, intersects it with the bake platforms and emits `build_matrix` (an explicit `{variant, platform}` include-matrix). Missing platforms are skipped with a `::warning::` instead of failing the run with `no match for platform in manifest`. Keep the `build` job on `matrix.include` — do NOT go back to a full `variant x platform` cross product. +- `merge` stays a `variant x target` matrix (a manifest list is platform-agnostic and is assembled from whatever digests `build` uploaded), but its variant axis comes from `build_variants` (unique variants of `build_matrix`), not `variants`. A variant with zero available platforms is skipped everywhere instead of producing a merge job that fails on missing digest artifacts. ## Trivy CRITICALs on published images (stale base packages) - Symptom: `task trivy:remote` (or `security.yml`) reports `fixed` CRITICALs (e.g. `ffmpeg`/`libav*`, `libgnutls30t64`) on some PHP lines (commonly older ones like 8.2/8.3) but not others (8.4/8.5). diff --git a/docs/usage.md b/docs/usage.md index da15ed1..977e791 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -38,7 +38,7 @@ ghcr.io/toshy/php:--(-) - Contains the following PHP versions: `8.2`, `8.3`, `8.4`, `8.5`. - Contains the following flavors: `cli`, `fpm`, `apache`, `zts`. - Contains the following OS: `trixie` (deprecated: `bookworm`). -- Contains the following [targets](images.md#targets): `base`, `ffmpeg`, `otel`, `otel-ffmpeg`, `otel-grpc,`, `otel-grpc-ffmpeg`. +- Contains the following [targets](images.md#targets): `base`, `ffmpeg`, `otel`, `otel-ffmpeg`, `otel-grpc`, `otel-grpc-ffmpeg`. !!!question