diff --git a/.github/workflows/build-commitfest.yml b/.github/workflows/build-commitfest.yml index 7039295..b4f1de5 100644 --- a/.github/workflows/build-commitfest.yml +++ b/.github/workflows/build-commitfest.yml @@ -26,6 +26,7 @@ jobs: pg_branch: ${{ env.BRANCH }} minimal_tag: ${{ env.registry }}/postgresql-trunk:${{ env.PG_MAJOR }}-minimal-${{ env.TAG }} standard_tag: ${{ env.registry }}/postgresql-trunk:${{ env.PG_MAJOR }}-standard-${{ env.TAG }} + postgis_tag: ${{ env.registry }}/postgresql-trunk:${{ env.PG_MAJOR }}-postgis-${{ env.TAG }} steps: - name: Checkout Code uses: actions/checkout@v7 @@ -69,23 +70,45 @@ jobs: pg_branch: ${{ needs.prepare.outputs.pg_branch }} pg_major: ${{ needs.prepare.outputs.pg_major }} revision: ${{ github.sha }} - minimal_tags: ${{ needs.prepare.outputs.minimal_tag }} - standard_tags: ${{ needs.prepare.outputs.standard_tag }} + targets: | + minimal:${{ needs.prepare.outputs.minimal_tag }} + standard:${{ needs.prepare.outputs.standard_tag }} + + # PostGIS periodically fails to build against PostgreSQL trunk. Kept as its + # own best-effort job so a break there doesn't fail this run or block the + # minimal/standard images above. See #158. + build-postgis: + name: Build PostGIS image for the patch (best-effort) + needs: prepare + uses: ./.github/workflows/reusable-build.yml + permissions: + contents: read + packages: write + with: + pg_repo: https://github.com/postgresql-cfbot/postgresql.git + pg_branch: ${{ needs.prepare.outputs.pg_branch }} + pg_major: ${{ needs.prepare.outputs.pg_major }} + revision: ${{ github.sha }} + targets: | + postgis:${{ needs.prepare.outputs.postgis_tag }} + best_effort: true generate-summary: name: Commitfest Image Build summary runs-on: ubuntu-26.04 needs: - build-pg + - build-postgis steps: - name: Output summary env: INPUT_PATCH_ID: ${{ github.event.inputs.patch_id }} BUILD_PG_IMAGES: ${{ needs.build-pg.outputs.images }} + BUILD_POSTGIS_IMAGES: ${{ needs.build-postgis.outputs.images }} run: | commitFestPatchID="${INPUT_PATCH_ID}" commitFestURL="https://commitfest.postgresql.org/patch/${commitFestPatchID}" - images="${BUILD_PG_IMAGES}" + images="${BUILD_PG_IMAGES} ${BUILD_POSTGIS_IMAGES}" images_list="$(echo $images | tr ' ' '\n' | sed 's/^/https:\/\//')" minimalImage="$(echo $images | tr ' ' '\n' | grep minimal)" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9c8f5b4..f549461 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,6 +30,7 @@ jobs: pg_major: ${{ env.PG_MAJOR }} minimal_tag: ${{ env.registry }}/postgresql-trunk:${{ env.PG_MAJOR }}-minimal-${{ github.run_number }} standard_tag: ${{ env.registry }}/postgresql-trunk:${{ env.PG_MAJOR }}-standard-${{ github.run_number }} + postgis_tag: ${{ env.registry }}/postgresql-trunk:${{ env.PG_MAJOR }}-postgis-${{ github.run_number }} steps: - name: Checkout Code uses: actions/checkout@v7 @@ -64,8 +65,28 @@ jobs: pg_branch: ${{ github.event.inputs.pg_branch }} pg_major: ${{ needs.prepare.outputs.pg_major }} revision: ${{ github.sha }} - minimal_tags: ${{ needs.prepare.outputs.minimal_tag }} - standard_tags: ${{ needs.prepare.outputs.standard_tag }} + targets: | + minimal:${{ needs.prepare.outputs.minimal_tag }} + standard:${{ needs.prepare.outputs.standard_tag }} + + # PostGIS periodically fails to build against PostgreSQL trunk. Kept as its + # own best-effort job so a break there doesn't fail this run or block the + # minimal/standard images above. See #158. + build-postgis: + name: Build PostGIS image from sources (best-effort) + needs: prepare + uses: ./.github/workflows/reusable-build.yml + permissions: + contents: read + packages: write + with: + pg_repo: ${{ github.event.inputs.pg_repo }} + pg_branch: ${{ github.event.inputs.pg_branch }} + pg_major: ${{ needs.prepare.outputs.pg_major }} + revision: ${{ github.sha }} + targets: | + postgis:${{ needs.prepare.outputs.postgis_tag }} + best_effort: true generate-summary: name: PostgreSQL Image Build summary @@ -73,14 +94,16 @@ jobs: needs: - prepare - build-pg + - build-postgis steps: - name: Output summary env: BUILD_PG_MAJOR: ${{ needs.prepare.outputs.pg_major }} BUILD_PG_IMAGES: ${{ needs.build-pg.outputs.images }} + BUILD_POSTGIS_IMAGES: ${{ needs.build-postgis.outputs.images }} run: | pg_major="${BUILD_PG_MAJOR}" - images="${BUILD_PG_IMAGES}" + images="${BUILD_PG_IMAGES} ${BUILD_POSTGIS_IMAGES}" images_list="$(echo $images | tr ' ' '\n' | sed 's/^/https:\/\//')" minimalImage="$(echo $images | tr ' ' '\n' | grep minimal)" diff --git a/.github/workflows/continuous-delivery.yml b/.github/workflows/continuous-delivery.yml index 147741a..cd2931c 100644 --- a/.github/workflows/continuous-delivery.yml +++ b/.github/workflows/continuous-delivery.yml @@ -37,6 +37,7 @@ jobs: barman_plugin: ${{ env.BARMAN_PLUGIN }} minimal_tags: ${{ env.MINIMAL_TAGS }} standard_tags: ${{ env.STANDARD_TAGS }} + postgis_tags: ${{ env.POSTGIS_TAGS }} steps: - name: Checkout Code uses: actions/checkout@v7 @@ -74,7 +75,7 @@ jobs: run: | registry="ghcr.io/${{ github.repository_owner }}/postgresql-trunk" timestamp="$(date -u +%Y%m%d%H%M)" - for tgt in MINIMAL STANDARD; do + for tgt in MINIMAL STANDARD POSTGIS; do lower="${tgt,,}" echo "${tgt}_TAGS=${registry}:${PG_MAJOR}-${lower}-${DISTRO},${registry}:${PG_MAJOR}-${timestamp}-${lower}-${DISTRO}" >> $GITHUB_ENV done @@ -89,12 +90,32 @@ jobs: with: pg_major: ${{ needs.prepare.outputs.pg_major }} revision: ${{ github.sha }} - minimal_tags: ${{ needs.prepare.outputs.minimal_tags }} - standard_tags: ${{ needs.prepare.outputs.standard_tags }} + targets: | + minimal:${{ needs.prepare.outputs.minimal_tags }} + standard:${{ needs.prepare.outputs.standard_tags }} # On pull_request runs, only build each arch to validate the Dockerfile # compiles — don't push anything or publish a multi-arch manifest. push: ${{ github.event_name != 'pull_request' }} + # PostGIS periodically fails to build against PostgreSQL trunk. It's built + # as its own job, independent from build-pg, so a break there (best_effort + # keeps it from failing this run) doesn't block minimal/standard image + # publishing or the E2E job below, which only depends on build-pg. See #158. + build-postgis: + name: Build the Trunk of PostgreSQL (PostGIS, best-effort) + needs: prepare + uses: ./.github/workflows/reusable-build.yml + permissions: + contents: read + packages: write + with: + pg_major: ${{ needs.prepare.outputs.pg_major }} + revision: ${{ github.sha }} + targets: | + postgis:${{ needs.prepare.outputs.postgis_tags }} + push: ${{ github.event_name != 'pull_request' }} + best_effort: true + select-pg-image: name: Select the image built for E2E if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' diff --git a/.github/workflows/reusable-build.yml b/.github/workflows/reusable-build.yml index 6d00993..6295495 100644 --- a/.github/workflows/reusable-build.yml +++ b/.github/workflows/reusable-build.yml @@ -1,17 +1,21 @@ name: Reusable Multi-Arch PostgreSQL Build -# Builds the minimal/standard images for linux/amd64 and linux/arm64 +# Builds one or more docker-bake.hcl targets for linux/amd64 and linux/arm64 # natively (one arch per job, no QEMU emulation) and publishes a multi-arch # manifest list for each requested tag by merging the two per-arch images # with `docker buildx imagetools create`. # -# PostGIS is currently excluded from the default docker-bake.hcl matrix -# (see #158), so it isn't wired through here either. -# # Compiling PostgreSQL from source is CPU-bound, so cross-building linux/arm64 # under QEMU emulation on an amd64 runner would be dramatically slower than a # native build. This workflow instead relies on GitHub-hosted native arm64 -# runners (`ubuntu-24.04-arm`) for the arm64 leg. +# runners (`ubuntu-26.04-arm`) for the arm64 leg. +# +# `best_effort: true` lets a caller build a target (e.g. postgis, which +# periodically fails against PostgreSQL trunk -- see #158) without a failure +# here failing the calling workflow run. This has to live inside this +# workflow's own jobs, rather than as `continue-on-error` on the caller's job, +# because GitHub Actions doesn't allow `continue-on-error` on a job that calls +# a reusable workflow via `uses:`. on: workflow_call: @@ -34,12 +38,8 @@ on: description: "Value used for the org.opencontainers.image.revision label/annotation" required: true type: string - minimal_tags: - description: "Comma-separated list of final (arch-less) tags for the minimal image" - required: true - type: string - standard_tags: - description: "Comma-separated list of final (arch-less) tags for the standard image" + targets: + description: "One ':' entry per line, e.g. 'minimal:tag1,tag2'" required: true type: string push: @@ -47,6 +47,11 @@ on: required: false type: boolean default: true + best_effort: + description: "When true, a build/publish failure doesn't fail this workflow (and so doesn't fail a caller job that just `uses:` it -- GitHub Actions doesn't allow `continue-on-error` on a job that calls a reusable workflow). Use for targets that are allowed to be broken, like postgis (#158)." + required: false + type: boolean + default: false outputs: images: description: "Newline-separated list of the final multi-arch images that were built and pushed (empty when push=false)" @@ -62,6 +67,7 @@ permissions: {} jobs: build: name: Build (linux/${{ matrix.arch }}) + continue-on-error: ${{ inputs.best_effort }} strategy: fail-fast: false matrix: @@ -70,8 +76,8 @@ jobs: - arm64 runs-on: >- ${{ - matrix.arch == 'arm64' && 'ubuntu-24.04-arm' - || (github.repository_owner == 'cloudnative-pg' && 'ubuntu-latest-16-cores' || 'ubuntu-24.04') + matrix.arch == 'arm64' && 'ubuntu-26.04-arm' + || (github.repository_owner == 'cloudnative-pg' && 'ubuntu-latest-16-cores' || 'ubuntu-26.04') }} permissions: contents: read @@ -90,30 +96,42 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - # For each image flavor, each tag is appended with - # an "-$arch" suffix (e.g. ...:19-minimal-trixie-arm64). + # For each image flavor, each tag is appended with an "-$arch" suffix + # (e.g. ...:19-minimal-trixie-arm64). `docker/bake-action` only builds the + # "default" group (docker-bake.hcl's `target "default"` matrix) unless + # told otherwise, so also collect the bake target names to build + # explicitly -- this is what lets a caller build just "postgis", which is + # intentionally kept out of the "default" group (see docker-bake.hcl). - name: Compute bake overrides id: overrides env: ARCH: ${{ matrix.arch }} PG_REPO: ${{ inputs.pg_repo }} PG_BRANCH: ${{ inputs.pg_branch }} - MINIMAL_TAGS: ${{ inputs.minimal_tags }} - STANDARD_TAGS: ${{ inputs.standard_tags }} + TARGETS: ${{ inputs.targets }} run: | + { + echo "targets<> "$GITHUB_OUTPUT" { echo "set<> "$GITHUB_OUTPUT" @@ -124,13 +142,27 @@ jobs: revision: ${{ inputs.revision }} pgMajor: ${{ inputs.pg_major }} with: + targets: ${{ steps.overrides.outputs.targets }} set: ${{ steps.overrides.outputs.set }} push: ${{ inputs.push }} + # continue-on-error reports this job as successful even when it failed, so + # surface the failure explicitly (annotation + run summary). + - name: Flag ignored best-effort build failure + if: failure() && inputs.best_effort + env: + ARCH: ${{ matrix.arch }} + TARGETS: ${{ inputs.targets }} + run: | + msg="Best-effort build failed for linux/${ARCH} (ignored, run stays green): ${TARGETS//$'\n'/ }" + echo "::warning title=Best-effort build failed::${msg}" + echo "### :warning: ${msg}" >> "$GITHUB_STEP_SUMMARY" + merge: name: Publish multi-arch manifests if: inputs.push needs: build + continue-on-error: ${{ inputs.best_effort }} runs-on: ubuntu-26.04 permissions: contents: read @@ -151,11 +183,12 @@ jobs: - name: Create and push multi-arch manifests id: merge env: - MINIMAL_TAGS: ${{ inputs.minimal_tags }} - STANDARD_TAGS: ${{ inputs.standard_tags }} + TARGETS: ${{ inputs.targets }} run: | images=() - for tags in "$MINIMAL_TAGS" "$STANDARD_TAGS"; do + while IFS= read -r target_tags; do + [[ -z "$target_tags" ]] && continue + tags="${target_tags#*:}" IFS=',' read -ra tag_list <<< "$tags" for tag in "${tag_list[@]}"; do docker buildx imagetools create \ @@ -164,9 +197,18 @@ jobs: "$tag-arm64" images+=("$tag") done - done + done <<< "$TARGETS" { echo 'images<> "$GITHUB_OUTPUT" + + - name: Flag ignored best-effort publish failure + if: failure() && inputs.best_effort + env: + TARGETS: ${{ inputs.targets }} + run: | + msg="Best-effort multi-arch manifest publish failed (ignored, run stays green): ${TARGETS//$'\n'/ }" + echo "::warning title=Best-effort publish failed::${msg}" + echo "### :warning: ${msg}" >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/reusable-e2e.yml b/.github/workflows/reusable-e2e.yml index 9610157..4311476 100644 --- a/.github/workflows/reusable-e2e.yml +++ b/.github/workflows/reusable-e2e.yml @@ -46,7 +46,7 @@ permissions: {} jobs: e2e-local: name: Run E2E on local executors - runs-on: ${{ github.repository_owner == 'cloudnative-pg' && 'ubuntu-latest-16-cores' || 'ubuntu-24.04' }} + runs-on: ${{ github.repository_owner == 'cloudnative-pg' && 'ubuntu-latest-16-cores' || 'ubuntu-26.04' }} permissions: packages: write env: diff --git a/README.md b/README.md index 63f1567..577cf25 100644 --- a/README.md +++ b/README.md @@ -43,12 +43,21 @@ image. If you want to build these images locally, first make sure you meet the following [prerequisites](https://github.com/cloudnative-pg/postgres-containers/blob/main/BUILD.md#prerequisites). -To build and push every flavor (minimal and standard), run: +To build and push the minimal and standard flavors, run: ``` docker buildx bake --push ``` +PostGIS periodically fails to build against PostgreSQL trunk, so it's kept +as a separate `postgis` target outside the default group above (see +[#158](https://github.com/cloudnative-pg/postgres-trunk-containers/issues/158)). +Build and push it explicitly with: + +``` +docker buildx bake postgis --push +``` + By default, the build process assumes a registry server runs locally at `localhost:5000`. You can either [deploy a disposable registry at localhost:5000](https://github.com/cloudnative-pg/postgres-containers/blob/main/BUILD.md#local-testing), or [specify a different registry](https://github.com/cloudnative-pg/postgres-containers/blob/main/BUILD.md#the-distribution-registry). diff --git a/docker-bake.hcl b/docker-bake.hcl index 06119c9..080cf67 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -53,16 +53,22 @@ description = "PostgreSQL Trunk Containers for CloudNativePG operator" authors = "The CloudNativePG Contributors" url = "https://github.com/cloudnative-pg/postgres-trunk-containers" -target "default" { +// Targets built by a plain `docker buildx bake` (the "default" group). +postgresTgt = ["minimal", "standard"] +// PostGIS periodically fails to build against PostgreSQL trunk, so it's kept +// out of the "default" group: CI builds it as an independent, best-effort +// step (`docker buildx bake postgis`) so a break doesn't block +// minimal/standard or E2E -- see reusable-build.yml and +// https://github.com/cloudnative-pg/postgres-trunk-containers/issues/158 +postgisTgt = ["postgis"] + +group "default" { + targets = postgresTgt +} + +target "image" { matrix = { - // PostGIS is temporarily excluded from the default build: it - // periodically fails against PostgreSQL trunk and, being built in the - // same bake invocation, takes minimal/standard and E2E down with it. - // See https://github.com/cloudnative-pg/postgres-trunk-containers/issues/158 - tgt = [ - "minimal", - "standard" - ] + tgt = concat(postgresTgt, postgisTgt) pgMajor = ["${pgMajor}"] base = ["debian:trixie-slim"] }