Prepare Or Publish Packages #12
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Prepare Or Publish Packages | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| mode: | |
| description: Prepare builds before release, or publish prepared artifacts after release | |
| required: true | |
| type: choice | |
| options: | |
| - prepare | |
| - publish | |
| tag: | |
| description: Release tag in vX.Y.Z form | |
| required: true | |
| type: string | |
| permissions: | |
| contents: read | |
| env: | |
| IMAGE_NAME: ghcr.io/evokoa/pggraph | |
| jobs: | |
| validate: | |
| name: Validate release | |
| runs-on: ubuntu-latest | |
| outputs: | |
| mode: ${{ steps.release.outputs.mode }} | |
| tag: ${{ steps.release.outputs.tag }} | |
| version: ${{ steps.release.outputs.version }} | |
| short_sha: ${{ steps.commit.outputs.short_sha }} | |
| steps: | |
| - name: Resolve release input | |
| id: release | |
| run: | | |
| mode="${{ inputs.mode }}" | |
| tag="${{ inputs.tag }}" | |
| if [[ "$mode" != "prepare" && "$mode" != "publish" ]]; then | |
| echo "Mode must be prepare or publish: $mode" >&2 | |
| exit 1 | |
| fi | |
| if [[ ! "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo "Release tag must use vX.Y.Z form: $tag" >&2 | |
| exit 1 | |
| fi | |
| echo "mode=$mode" >> "$GITHUB_OUTPUT" | |
| echo "tag=$tag" >> "$GITHUB_OUTPUT" | |
| echo "version=${tag#v}" >> "$GITHUB_OUTPUT" | |
| - name: Verify GitHub Release exists | |
| if: steps.release.outputs.mode == 'publish' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GH_REPO: ${{ github.repository }} | |
| run: gh release view "${{ steps.release.outputs.tag }}" >/dev/null | |
| - name: Checkout release tag | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ steps.release.outputs.tag }} | |
| fetch-depth: 0 | |
| - name: Validate release metadata | |
| run: scripts/validate_release.py --tag "${{ steps.release.outputs.tag }}" --check-main | |
| - name: Capture release commit | |
| id: commit | |
| run: echo "short_sha=$(git rev-parse --short=12 HEAD)" >> "$GITHUB_OUTPUT" | |
| pgxn-artifact: | |
| name: Build PGXN artifact | |
| if: needs.validate.outputs.mode == 'prepare' | |
| needs: validate | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout release tag | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.validate.outputs.tag }} | |
| - name: Build PGXN source archive | |
| run: scripts/build_pgxn_dist.sh "${{ needs.validate.outputs.tag }}" dist | |
| - name: Verify PGXN source archive | |
| run: | | |
| python3 - <<'PY' | |
| import json | |
| import zipfile | |
| from pathlib import Path | |
| version = "${{ needs.validate.outputs.version }}" | |
| archive = Path(f"dist/pgGraph-{version}.zip") | |
| prefix = f"pgGraph-{version}/" | |
| with zipfile.ZipFile(archive) as package: | |
| names = set(package.namelist()) | |
| required = { | |
| f"{prefix}META.json", | |
| f"{prefix}Makefile", | |
| f"{prefix}README.md", | |
| f"{prefix}LICENSE", | |
| f"{prefix}graph/Cargo.toml", | |
| f"{prefix}graph/graph.control", | |
| } | |
| missing = sorted(required - names) | |
| if missing: | |
| raise SystemExit(f"PGXN archive is missing: {', '.join(missing)}") | |
| meta = json.loads(package.read(f"{prefix}META.json")) | |
| if meta.get("version") != version: | |
| raise SystemExit( | |
| f"PGXN archive META.json version {meta.get('version')!r} " | |
| f"does not match {version!r}" | |
| ) | |
| PY | |
| - name: Upload workflow artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: pgxn-source-${{ needs.validate.outputs.version }} | |
| path: dist/pgGraph-${{ needs.validate.outputs.version }}.zip | |
| if-no-files-found: error | |
| approve-publishing: | |
| name: Approve package publishing | |
| if: needs.validate.outputs.mode == 'publish' | |
| needs: | |
| - validate | |
| runs-on: ubuntu-latest | |
| environment: release | |
| steps: | |
| - name: Approved | |
| run: echo "Approved PGXN and Docker package publishing." | |
| publish-pgxn: | |
| name: Publish PGXN package | |
| if: needs.validate.outputs.mode == 'publish' | |
| needs: | |
| - validate | |
| - approve-publishing | |
| runs-on: ubuntu-latest | |
| container: pgxn/pgxn-tools | |
| steps: | |
| - name: Checkout release tag | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.validate.outputs.tag }} | |
| - name: Build PGXN source archive | |
| run: scripts/build_pgxn_dist.sh "${{ needs.validate.outputs.tag }}" dist | |
| - name: Publish to PGXN | |
| env: | |
| PGXN_USERNAME: ${{ secrets.PGXN_USERNAME }} | |
| PGXN_PASSWORD: ${{ secrets.PGXN_PASSWORD }} | |
| run: pgxn-release "dist/pgGraph-${{ needs.validate.outputs.version }}.zip" | |
| pgxn-verify: | |
| name: Verify PGXN package | |
| if: needs.validate.outputs.mode == 'publish' | |
| needs: | |
| - validate | |
| - publish-pgxn | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Verify PGXN metadata is visible | |
| run: | | |
| url="https://api.pgxn.org/dist/pggraph/${{ needs.validate.outputs.version }}/META.json" | |
| for _ in {1..30}; do | |
| if curl --fail --silent --show-error --location "$url" >/tmp/pgxn-meta.json; then | |
| break | |
| fi | |
| sleep 10 | |
| done | |
| test -s /tmp/pgxn-meta.json | |
| - name: Verify PGXN version metadata | |
| run: | | |
| python3 - <<'PY' | |
| import json | |
| from pathlib import Path | |
| expected = "${{ needs.validate.outputs.version }}" | |
| meta = json.loads(Path("/tmp/pgxn-meta.json").read_text(encoding="utf-8")) | |
| actual = meta.get("version") | |
| if actual != expected: | |
| raise SystemExit(f"Expected PGXN version {expected}, got {actual!r}") | |
| PY | |
| attach-pgxn-artifact: | |
| name: Attach PGXN artifact to GitHub Release | |
| if: needs.validate.outputs.mode == 'publish' | |
| needs: | |
| - validate | |
| - approve-publishing | |
| - pgxn-verify | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout release tag | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.validate.outputs.tag }} | |
| - name: Build PGXN source archive | |
| run: scripts/build_pgxn_dist.sh "${{ needs.validate.outputs.tag }}" dist | |
| - name: Attach archive to GitHub Release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GH_REPO: ${{ github.repository }} | |
| run: | | |
| gh release upload "${{ needs.validate.outputs.tag }}" \ | |
| "dist/pgGraph-${{ needs.validate.outputs.version }}.zip" \ | |
| --clobber | |
| docker: | |
| name: Prepare Docker image PG${{ matrix.pg }} ${{ matrix.platform.name }} | |
| if: needs.validate.outputs.mode == 'prepare' | |
| needs: | |
| - validate | |
| runs-on: ${{ matrix.platform.runner }} | |
| permissions: | |
| contents: read | |
| packages: write | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| pg: [14, 15, 16, 17, 18] | |
| platform: | |
| - name: linux/amd64 | |
| runner: ubuntu-24.04 | |
| pair: linux-amd64 | |
| - name: linux/arm64 | |
| runner: ubuntu-24.04-arm | |
| pair: linux-arm64 | |
| steps: | |
| - name: Checkout release tag | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.validate.outputs.tag }} | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ github.token }} | |
| - name: Extract Docker metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.IMAGE_NAME }} | |
| tags: | | |
| type=raw,value=pg${{ matrix.pg }}-sha-${{ needs.validate.outputs.short_sha }} | |
| type=raw,value=pg${{ matrix.pg }}-${{ needs.validate.outputs.tag }}-prepared | |
| type=raw,value=pg${{ matrix.pg }}-${{ needs.validate.outputs.version }}-prepared | |
| labels: | | |
| org.opencontainers.image.version=${{ needs.validate.outputs.version }} | |
| org.opencontainers.image.postgresql.major=${{ matrix.pg }} | |
| org.opencontainers.image.revision=${{ needs.validate.outputs.short_sha }} | |
| - name: Build and push | |
| id: build | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| platforms: ${{ matrix.platform.name }} | |
| build-args: | | |
| PG_MAJOR=${{ matrix.pg }} | |
| POSTGRES_IMAGE=postgres:${{ matrix.pg }}-bookworm | |
| outputs: type=image,push-by-digest=true,name-canonical=true,push=true | |
| tags: ${{ env.IMAGE_NAME }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha,scope=pggraph-pg${{ matrix.pg }}-${{ matrix.platform.pair }} | |
| cache-to: type=gha,mode=max,scope=pggraph-pg${{ matrix.pg }}-${{ matrix.platform.pair }} | |
| - name: Export digest | |
| run: | | |
| mkdir -p "${{ runner.temp }}/digests" | |
| digest="${{ steps.build.outputs.digest }}" | |
| touch "${{ runner.temp }}/digests/${digest#sha256:}" | |
| - name: Upload digest | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: digests-pg${{ matrix.pg }}-${{ matrix.platform.pair }} | |
| path: ${{ runner.temp }}/digests/* | |
| if-no-files-found: error | |
| retention-days: 1 | |
| docker-merge: | |
| name: Merge prepared Docker image PG${{ matrix.pg }} | |
| if: needs.validate.outputs.mode == 'prepare' | |
| needs: | |
| - validate | |
| - docker | |
| runs-on: ubuntu-24.04 | |
| permissions: | |
| contents: read | |
| packages: write | |
| id-token: write | |
| attestations: write | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| pg: [14, 15, 16, 17, 18] | |
| steps: | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ github.token }} | |
| - name: Extract Docker metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.IMAGE_NAME }} | |
| tags: | | |
| type=raw,value=pg${{ matrix.pg }}-sha-${{ needs.validate.outputs.short_sha }} | |
| type=raw,value=pg${{ matrix.pg }}-${{ needs.validate.outputs.tag }}-prepared | |
| type=raw,value=pg${{ matrix.pg }}-${{ needs.validate.outputs.version }}-prepared | |
| - name: Download digests | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: ${{ runner.temp }}/digests | |
| pattern: digests-pg${{ matrix.pg }}-* | |
| merge-multiple: true | |
| - name: Create manifest list and push | |
| working-directory: ${{ runner.temp }}/digests | |
| env: | |
| DOCKER_METADATA_OUTPUT_JSON: ${{ steps.meta.outputs.json }} | |
| run: | | |
| digest_count="$(find . -maxdepth 1 -type f | wc -l | tr -d ' ')" | |
| if [[ "$digest_count" != "2" ]]; then | |
| echo "Expected 2 platform digests for PG${{ matrix.pg }}, found $digest_count" >&2 | |
| find . -maxdepth 1 -type f -print >&2 | |
| exit 1 | |
| fi | |
| docker buildx imagetools create \ | |
| $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ | |
| $(printf '${{ env.IMAGE_NAME }}@sha256:%s ' *) | |
| - name: Resolve merged manifest digest | |
| id: manifest | |
| run: | | |
| image="${{ env.IMAGE_NAME }}:pg${{ matrix.pg }}-${{ needs.validate.outputs.tag }}-prepared" | |
| digest="$(docker buildx imagetools inspect "$image" | awk '/Digest:/ { print $2; exit }')" | |
| echo "digest=$digest" >> "$GITHUB_OUTPUT" | |
| - name: Attest image provenance | |
| uses: actions/attest-build-provenance@v2 | |
| with: | |
| subject-name: ${{ env.IMAGE_NAME }} | |
| subject-digest: ${{ steps.manifest.outputs.digest }} | |
| push-to-registry: true | |
| docker-verify: | |
| name: Verify prepared image PG${{ matrix.pg }} ${{ matrix.platform.name }} | |
| if: needs.validate.outputs.mode == 'prepare' | |
| needs: | |
| - validate | |
| - docker-merge | |
| runs-on: ${{ matrix.platform.runner }} | |
| permissions: | |
| packages: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| pg: [14, 15, 16, 17, 18] | |
| platform: | |
| - name: linux/amd64 | |
| runner: ubuntu-24.04 | |
| pair: linux-amd64 | |
| - name: linux/arm64 | |
| runner: ubuntu-24.04-arm | |
| pair: linux-arm64 | |
| steps: | |
| - name: Login to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ github.token }} | |
| - name: Pull and start release image | |
| run: | | |
| image="${{ env.IMAGE_NAME }}:pg${{ matrix.pg }}-${{ needs.validate.outputs.tag }}-prepared" | |
| docker pull --platform "${{ matrix.platform.name }}" "$image" | |
| docker run -d --rm \ | |
| --platform "${{ matrix.platform.name }}" \ | |
| --name pggraph-release-verify-pg${{ matrix.pg }}-${{ matrix.platform.pair }} \ | |
| -e POSTGRES_PASSWORD=postgres \ | |
| -p 55432:5432 \ | |
| "$image" | |
| - name: Wait for PostgreSQL | |
| run: | | |
| consecutive=0 | |
| for _ in {1..60}; do | |
| if docker exec pggraph-release-verify-pg${{ matrix.pg }}-${{ matrix.platform.pair }} \ | |
| psql -U postgres -d graph -v ON_ERROR_STOP=1 -tAc "SELECT 1" >/dev/null 2>&1; then | |
| consecutive=$((consecutive + 1)) | |
| if [[ "$consecutive" -ge 2 ]]; then | |
| exit 0 | |
| fi | |
| else | |
| consecutive=0 | |
| fi | |
| sleep 1 | |
| done | |
| docker logs pggraph-release-verify-pg${{ matrix.pg }}-${{ matrix.platform.pair }} | |
| exit 1 | |
| - name: Verify extensions | |
| run: | | |
| count="" | |
| for _ in {1..60}; do | |
| if count="$(docker exec pggraph-release-verify-pg${{ matrix.pg }}-${{ matrix.platform.pair }} \ | |
| psql -U postgres -d graph -v ON_ERROR_STOP=1 \ | |
| -tAc "SELECT count(*) FROM pg_extension WHERE extname IN ('graph', 'pg_cron');")"; then | |
| if [[ "$count" == "2" ]]; then | |
| break | |
| fi | |
| else | |
| count="" | |
| fi | |
| sleep 2 | |
| done | |
| if [[ "$count" != "2" ]]; then | |
| echo "Expected graph and pg_cron extensions, found $count" >&2 | |
| docker logs pggraph-release-verify-pg${{ matrix.pg }}-${{ matrix.platform.pair }} | |
| docker exec pggraph-release-verify-pg${{ matrix.pg }}-${{ matrix.platform.pair }} \ | |
| psql -U postgres -d graph -v ON_ERROR_STOP=1 \ | |
| -c "SELECT extname, extversion FROM pg_extension ORDER BY extname;" | |
| exit 1 | |
| fi | |
| server_version_num="$(docker exec pggraph-release-verify-pg${{ matrix.pg }}-${{ matrix.platform.pair }} \ | |
| psql -U postgres -d graph -v ON_ERROR_STOP=1 \ | |
| -tAc "SHOW server_version_num;")" | |
| if [[ "${server_version_num:0:2}" != "${{ matrix.pg }}" ]]; then | |
| echo "Expected PostgreSQL ${{ matrix.pg }}, got server_version_num=$server_version_num" >&2 | |
| exit 1 | |
| fi | |
| - name: Stop container | |
| if: always() | |
| run: docker rm -f pggraph-release-verify-pg${{ matrix.pg }}-${{ matrix.platform.pair }} >/dev/null 2>&1 || true | |
| publish-docker: | |
| name: Publish Docker image PG${{ matrix.pg }} | |
| if: needs.validate.outputs.mode == 'publish' | |
| needs: | |
| - validate | |
| - approve-publishing | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| pg: [14, 15, 16, 17, 18] | |
| steps: | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ github.token }} | |
| - name: Promote prepared image tags | |
| run: | | |
| source="${{ env.IMAGE_NAME }}:pg${{ matrix.pg }}-${{ needs.validate.outputs.tag }}-prepared" | |
| sha_source="${{ env.IMAGE_NAME }}:pg${{ matrix.pg }}-sha-${{ needs.validate.outputs.short_sha }}" | |
| docker buildx imagetools inspect "$source" >/dev/null | |
| docker buildx imagetools inspect "$sha_source" >/dev/null | |
| source_digest="$(docker buildx imagetools inspect "$source" | awk '/Digest:/ { print $2; exit }')" | |
| sha_digest="$(docker buildx imagetools inspect "$sha_source" | awk '/Digest:/ { print $2; exit }')" | |
| if [[ "$source_digest" != "$sha_digest" ]]; then | |
| echo "Prepared tag digest $source_digest does not match commit tag digest $sha_digest" >&2 | |
| exit 1 | |
| fi | |
| tags=( | |
| -t "${{ env.IMAGE_NAME }}:pg${{ matrix.pg }}-${{ needs.validate.outputs.tag }}" | |
| -t "${{ env.IMAGE_NAME }}:pg${{ matrix.pg }}-${{ needs.validate.outputs.version }}" | |
| -t "${{ env.IMAGE_NAME }}:pg${{ matrix.pg }}" | |
| -t "${{ env.IMAGE_NAME }}:pg${{ matrix.pg }}-sha-${{ needs.validate.outputs.short_sha }}" | |
| ) | |
| if [[ "${{ matrix.pg }}" == "17" ]]; then | |
| tags+=( | |
| -t "${{ env.IMAGE_NAME }}:${{ needs.validate.outputs.tag }}" | |
| -t "${{ env.IMAGE_NAME }}:${{ needs.validate.outputs.version }}" | |
| -t "${{ env.IMAGE_NAME }}:latest" | |
| -t "${{ env.IMAGE_NAME }}:sha-${{ needs.validate.outputs.short_sha }}" | |
| ) | |
| fi | |
| docker buildx imagetools create "${tags[@]}" "$source" | |
| publish-docker-verify: | |
| name: Verify published image PG${{ matrix.pg }} ${{ matrix.platform.name }} | |
| if: needs.validate.outputs.mode == 'publish' | |
| needs: | |
| - validate | |
| - publish-docker | |
| runs-on: ${{ matrix.platform.runner }} | |
| permissions: | |
| packages: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| pg: [14, 15, 16, 17, 18] | |
| platform: | |
| - name: linux/amd64 | |
| runner: ubuntu-24.04 | |
| pair: linux-amd64 | |
| - name: linux/arm64 | |
| runner: ubuntu-24.04-arm | |
| pair: linux-arm64 | |
| steps: | |
| - name: Login to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ github.token }} | |
| - name: Pull and start published image | |
| run: | | |
| image="${{ env.IMAGE_NAME }}:pg${{ matrix.pg }}-${{ needs.validate.outputs.tag }}" | |
| docker pull --platform "${{ matrix.platform.name }}" "$image" | |
| docker run -d --rm \ | |
| --platform "${{ matrix.platform.name }}" \ | |
| --name pggraph-release-verify-pg${{ matrix.pg }}-${{ matrix.platform.pair }} \ | |
| -e POSTGRES_PASSWORD=postgres \ | |
| -p 55432:5432 \ | |
| "$image" | |
| - name: Wait for PostgreSQL | |
| run: | | |
| consecutive=0 | |
| for _ in {1..60}; do | |
| if docker exec pggraph-release-verify-pg${{ matrix.pg }}-${{ matrix.platform.pair }} \ | |
| psql -U postgres -d graph -v ON_ERROR_STOP=1 -tAc "SELECT 1" >/dev/null 2>&1; then | |
| consecutive=$((consecutive + 1)) | |
| if [[ "$consecutive" -ge 2 ]]; then | |
| exit 0 | |
| fi | |
| else | |
| consecutive=0 | |
| fi | |
| sleep 1 | |
| done | |
| docker logs pggraph-release-verify-pg${{ matrix.pg }}-${{ matrix.platform.pair }} | |
| exit 1 | |
| - name: Verify extensions | |
| run: | | |
| count="" | |
| for _ in {1..60}; do | |
| if count="$(docker exec pggraph-release-verify-pg${{ matrix.pg }}-${{ matrix.platform.pair }} \ | |
| psql -U postgres -d graph -v ON_ERROR_STOP=1 \ | |
| -tAc "SELECT count(*) FROM pg_extension WHERE extname IN ('graph', 'pg_cron');")"; then | |
| if [[ "$count" == "2" ]]; then | |
| break | |
| fi | |
| else | |
| count="" | |
| fi | |
| sleep 2 | |
| done | |
| if [[ "$count" != "2" ]]; then | |
| echo "Expected graph and pg_cron extensions, found $count" >&2 | |
| docker logs pggraph-release-verify-pg${{ matrix.pg }}-${{ matrix.platform.pair }} | |
| docker exec pggraph-release-verify-pg${{ matrix.pg }}-${{ matrix.platform.pair }} \ | |
| psql -U postgres -d graph -v ON_ERROR_STOP=1 \ | |
| -c "SELECT extname, extversion FROM pg_extension ORDER BY extname;" | |
| exit 1 | |
| fi | |
| server_version_num="$(docker exec pggraph-release-verify-pg${{ matrix.pg }}-${{ matrix.platform.pair }} \ | |
| psql -U postgres -d graph -v ON_ERROR_STOP=1 \ | |
| -tAc "SHOW server_version_num;")" | |
| if [[ "${server_version_num:0:2}" != "${{ matrix.pg }}" ]]; then | |
| echo "Expected PostgreSQL ${{ matrix.pg }}, got server_version_num=$server_version_num" >&2 | |
| exit 1 | |
| fi | |
| - name: Stop container | |
| if: always() | |
| run: docker rm -f pggraph-release-verify-pg${{ matrix.pg }}-${{ matrix.platform.pair }} >/dev/null 2>&1 || true | |
| docker-verify-default: | |
| name: Verify default published image ${{ matrix.platform.name }} | |
| if: needs.validate.outputs.mode == 'publish' | |
| needs: | |
| - validate | |
| - publish-docker | |
| runs-on: ${{ matrix.platform.runner }} | |
| permissions: | |
| packages: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| platform: | |
| - name: linux/amd64 | |
| runner: ubuntu-24.04 | |
| pair: linux-amd64 | |
| - name: linux/arm64 | |
| runner: ubuntu-24.04-arm | |
| pair: linux-arm64 | |
| steps: | |
| - name: Login to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ github.token }} | |
| - name: Pull and start default release image | |
| run: | | |
| image="${{ env.IMAGE_NAME }}:${{ needs.validate.outputs.tag }}" | |
| docker pull --platform "${{ matrix.platform.name }}" "$image" | |
| docker run -d --rm \ | |
| --platform "${{ matrix.platform.name }}" \ | |
| --name pggraph-release-verify-default-${{ matrix.platform.pair }} \ | |
| -e POSTGRES_PASSWORD=postgres \ | |
| -p 55432:5432 \ | |
| "$image" | |
| - name: Wait for PostgreSQL | |
| run: | | |
| consecutive=0 | |
| for _ in {1..60}; do | |
| if docker exec pggraph-release-verify-default-${{ matrix.platform.pair }} \ | |
| psql -U postgres -d graph -v ON_ERROR_STOP=1 -tAc "SELECT 1" >/dev/null 2>&1; then | |
| consecutive=$((consecutive + 1)) | |
| if [[ "$consecutive" -ge 2 ]]; then | |
| exit 0 | |
| fi | |
| else | |
| consecutive=0 | |
| fi | |
| sleep 1 | |
| done | |
| docker logs pggraph-release-verify-default-${{ matrix.platform.pair }} | |
| exit 1 | |
| - name: Verify extensions and default PostgreSQL major | |
| run: | | |
| count="" | |
| for _ in {1..60}; do | |
| if count="$(docker exec pggraph-release-verify-default-${{ matrix.platform.pair }} \ | |
| psql -U postgres -d graph -v ON_ERROR_STOP=1 \ | |
| -tAc "SELECT count(*) FROM pg_extension WHERE extname IN ('graph', 'pg_cron');")"; then | |
| if [[ "$count" == "2" ]]; then | |
| break | |
| fi | |
| else | |
| count="" | |
| fi | |
| sleep 2 | |
| done | |
| if [[ "$count" != "2" ]]; then | |
| echo "Expected graph and pg_cron extensions, found $count" >&2 | |
| docker logs pggraph-release-verify-default-${{ matrix.platform.pair }} | |
| docker exec pggraph-release-verify-default-${{ matrix.platform.pair }} \ | |
| psql -U postgres -d graph -v ON_ERROR_STOP=1 \ | |
| -c "SELECT extname, extversion FROM pg_extension ORDER BY extname;" | |
| exit 1 | |
| fi | |
| server_version_num="$(docker exec pggraph-release-verify-default-${{ matrix.platform.pair }} \ | |
| psql -U postgres -d graph -v ON_ERROR_STOP=1 \ | |
| -tAc "SHOW server_version_num;")" | |
| if [[ "${server_version_num:0:2}" != "17" ]]; then | |
| echo "Expected default PostgreSQL 17 image, got server_version_num=$server_version_num" >&2 | |
| exit 1 | |
| fi | |
| - name: Stop container | |
| if: always() | |
| run: docker rm -f pggraph-release-verify-default-${{ matrix.platform.pair }} >/dev/null 2>&1 || true | |
| publish-summary: | |
| name: Publish summary | |
| if: needs.validate.outputs.mode == 'publish' | |
| needs: | |
| - validate | |
| - pgxn-verify | |
| - attach-pgxn-artifact | |
| - publish-docker-verify | |
| - docker-verify-default | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Summarize published packages | |
| run: | | |
| { | |
| echo "## Published packages" | |
| echo | |
| echo "- PGXN: pggraph ${{ needs.validate.outputs.version }}" | |
| echo "- Docker: ${{ env.IMAGE_NAME }}:${{ needs.validate.outputs.tag }}" | |
| echo "- Docker: ${{ env.IMAGE_NAME }}:pg14-${{ needs.validate.outputs.tag }}" | |
| echo "- Docker: ${{ env.IMAGE_NAME }}:pg15-${{ needs.validate.outputs.tag }}" | |
| echo "- Docker: ${{ env.IMAGE_NAME }}:pg16-${{ needs.validate.outputs.tag }}" | |
| echo "- Docker: ${{ env.IMAGE_NAME }}:pg17-${{ needs.validate.outputs.tag }}" | |
| echo "- Docker: ${{ env.IMAGE_NAME }}:pg18-${{ needs.validate.outputs.tag }}" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| prepare-summary: | |
| name: Prepare summary | |
| if: needs.validate.outputs.mode == 'prepare' | |
| needs: | |
| - validate | |
| - pgxn-artifact | |
| - docker-verify | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Summarize prepared packages | |
| run: | | |
| { | |
| echo "## Prepared packages" | |
| echo | |
| echo "- Commit: ${{ needs.validate.outputs.short_sha }}" | |
| echo "- PGXN workflow artifact: pgxn-source-${{ needs.validate.outputs.version }}" | |
| echo "- Docker: ${{ env.IMAGE_NAME }}:pg14-${{ needs.validate.outputs.tag }}-prepared" | |
| echo "- Docker: ${{ env.IMAGE_NAME }}:pg15-${{ needs.validate.outputs.tag }}-prepared" | |
| echo "- Docker: ${{ env.IMAGE_NAME }}:pg16-${{ needs.validate.outputs.tag }}-prepared" | |
| echo "- Docker: ${{ env.IMAGE_NAME }}:pg17-${{ needs.validate.outputs.tag }}-prepared" | |
| echo "- Docker: ${{ env.IMAGE_NAME }}:pg18-${{ needs.validate.outputs.tag }}-prepared" | |
| } >> "$GITHUB_STEP_SUMMARY" |