name: PGXN package

on:
  pull_request:
    paths:
      - ".github/workflows/pgxn.yml"
      - ".github/workflows/auto-version.yml"
      - ".gitignore"
      - "LICENSE"
      - "META.json"
      - "Makefile"
      - "PGXN.md"
      - "README.md"
      - "pg_local_cache.control"
      - "scripts/auto_version.py"
      - "scripts/build_pgxn_dist.py"
      - "scripts/validate_pgxn_meta.py"
      - "sql/**"
      - "src/pg_local_cache_worker.c"
      - "compose.yaml"
      - "compose.sql-only.yaml"
      - "tests/docker_smoke.sh"
      - "tests/pgxn_contract_test.py"
      - "tests/release_version_test.py"
  push:
    branches: ["master"]
    paths:
      - ".github/workflows/pgxn.yml"
      - ".github/workflows/auto-version.yml"
      - ".gitignore"
      - "LICENSE"
      - "META.json"
      - "Makefile"
      - "PGXN.md"
      - "README.md"
      - "pg_local_cache.control"
      - "scripts/auto_version.py"
      - "scripts/build_pgxn_dist.py"
      - "scripts/validate_pgxn_meta.py"
      - "sql/**"
      - "src/pg_local_cache_worker.c"
      - "compose.yaml"
      - "compose.sql-only.yaml"
      - "tests/docker_smoke.sh"
      - "tests/pgxn_contract_test.py"
      - "tests/release_version_test.py"
  workflow_run:
    workflows: ["Release PostgreSQL 14-18 Linux artifacts"]
    types: [completed]
  workflow_dispatch:
    inputs:
      ref:
        description: Git ref to package
        required: false
        default: master
        type: string

permissions:
  contents: read

concurrency:
  group: pgxn-package-${{ github.event_name }}-${{ github.event.workflow_run.id || github.run_id }}
  cancel-in-progress: false

jobs:
  validate:
    if: github.event_name != 'workflow_run'
    runs-on: ubuntu-24.04
    timeout-minutes: 10
    steps:
      - name: Check out source
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.sha }}
          fetch-depth: 0
          persist-credentials: false

      - name: Validate metadata and build PGXN ZIP
        run: make pgxn-check dist

      - name: Upload review artifact
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
        with:
          name: pgxn-distribution
          path: dist/pg_local_cache-*.zip
          if-no-files-found: error
          retention-days: 14

  publish:
    if: >-
      github.event_name == 'workflow_run' &&
      github.event.workflow_run.conclusion == 'success' &&
      github.event.workflow_run.head_branch == 'master' &&
      github.event.workflow_run.head_repository.full_name == github.repository
    runs-on: ubuntu-24.04
    timeout-minutes: 10
    permissions:
      contents: write
    env:
      GH_TOKEN: ${{ github.token }}
      RELEASE_SHA: ${{ github.event.workflow_run.head_sha }}
    steps:
      - name: Check out released source
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          ref: ${{ env.RELEASE_SHA }}
          fetch-depth: 0
          persist-credentials: false

      - name: Qualify the exact stable GitHub release
        id: qualify
        shell: bash
        run: |
          set -Eeuo pipefail
          version="$(python3 scripts/validate_pgxn_meta.py --print-version)"
          sha="$(git rev-parse HEAD)"
          [[ "$sha" == "$RELEASE_SHA" ]]
          stable_tag="v${version}"
          stable_sha=""
          if stable_sha="$(
            gh api "repos/${GITHUB_REPOSITORY}/commits/${stable_tag}" \
              --jq .sha 2>/dev/null
          )" && [[ "$stable_sha" == "$sha" ]] \
              && gh release view "$stable_tag" >/dev/null 2>&1; then
            echo "publish=true" >> "$GITHUB_OUTPUT"
            echo "::notice::${stable_tag} is the stable release for ${sha}"
          else
            echo "publish=false" >> "$GITHUB_OUTPUT"
            echo "::notice::Release workflow completed without a new stable release for ${sha}; skipping PGXN"
          fi

      - name: Build exact PGXN distribution
        if: steps.qualify.outputs.publish == 'true'
        run: make pgxn-check dist

      - name: Attach ZIP to immutable releases
        if: steps.qualify.outputs.publish == 'true'
        shell: bash
        run: |
          set -Eeuo pipefail

          version="$(python3 scripts/validate_pgxn_meta.py --print-version)"
          asset="dist/pg_local_cache-${version}.zip"
          test -f "$asset"

          sha="$(git rev-parse HEAD)"
          [[ "$sha" == "$RELEASE_SHA" ]]
          commit_tag="master-${sha:0:12}"
          stable_tag="v${version}"

          upload_exact_asset() {
            local tag="$1" name existing temporary
            name="$(basename "$asset")"
            gh release view "$tag" >/dev/null
            existing="$(
              gh release view "$tag" --json assets --jq '.assets[].name'
            )"
            if grep -Fxq "$name" <<<"$existing"; then
              temporary="$(mktemp -d)"
              gh release download "$tag" \
                --pattern "$name" \
                --dir "$temporary"
              cmp --silent "$asset" "$temporary/$name" || {
                echo "::error::${tag}/${name} already exists with different bytes"
                return 1
              }
              echo "::notice::${tag}/${name} already matches"
            else
              gh release upload "$tag" "$asset"
            fi
          }

          upload_exact_asset "$commit_tag"

          stable_sha=""
          if stable_sha="$(
            gh api "repos/${GITHUB_REPOSITORY}/commits/${stable_tag}" \
              --jq .sha 2>/dev/null
          )"; then
            if [[ "$stable_sha" == "$sha" ]]; then
              upload_exact_asset "$stable_tag"
            else
              echo "::notice::${stable_tag} points to ${stable_sha}; not attaching an archive built from ${sha}"
            fi
          else
            echo "::notice::${stable_tag} does not exist yet"
          fi

      - name: Publish the stable distribution to PGXN
        if: steps.qualify.outputs.publish == 'true'
        shell: bash
        env:
          PGXN_USERNAME: ${{ secrets.PGXN_USERNAME }}
          PGXN_PASSWORD: ${{ secrets.PGXN_PASSWORD }}
        run: |
          set -Eeuo pipefail

          version="$(python3 scripts/validate_pgxn_meta.py --print-version)"
          asset="dist/pg_local_cache-${version}.zip"
          test -f "$asset"

          sha="$(git rev-parse HEAD)"
          [[ "$sha" == "$RELEASE_SHA" ]]
          stable_tag="v${version}"
          stable_sha="$(
            gh api "repos/${GITHUB_REPOSITORY}/commits/${stable_tag}" --jq .sha
          )"
          [[ "$stable_sha" == "$sha" ]] || {
            echo "::error::${stable_tag} points to ${stable_sha}, not ${sha}"
            exit 1
          }

          remote_archive="https://api.pgxn.org/dist/pg_local_cache/${version}/pg_local_cache-${version}.zip"
          mirrored="$RUNNER_TEMP/pg_local_cache-${version}.zip"
          if curl --fail --silent --show-error \
              --retry 3 --retry-all-errors \
              --output "$mirrored" "$remote_archive" 2>/dev/null; then
            cmp --silent "$asset" "$mirrored" || {
              echo "::error::PGXN already contains pg_local_cache ${version} with different bytes"
              exit 1
            }
            echo "::notice::PGXN already contains the exact pg_local_cache ${version} archive"
            exit 0
          fi

          [[ -n "$PGXN_USERNAME" && -n "$PGXN_PASSWORD" ]] || {
            echo "::error::Add PGXN_USERNAME and PGXN_PASSWORD repository secrets once before publishing"
            exit 1
          }

          netrc="$RUNNER_TEMP/pgxn.netrc"
          response="$RUNNER_TEMP/pgxn-upload-response.txt"
          umask 077
          {
            printf 'machine manager.pgxn.org\n'
            printf 'login %s\n' "$PGXN_USERNAME"
            printf 'password %s\n' "$PGXN_PASSWORD"
          } > "$netrc"

          http_code="$(
            curl --silent --show-error --location \
              --retry 3 --retry-all-errors \
              --netrc-file "$netrc" \
              --header 'X-Requested-With: XMLHttpRequest' \
              --header 'Accept: text/plain' \
              --form "archive=@${asset};type=application/zip" \
              --output "$response" \
              --write-out '%{http_code}' \
              https://manager.pgxn.org/upload
          )"

          case "$http_code" in
            200)
              grep -Fxq 'Success' "$response" || {
                echo "::error::PGXN returned HTTP 200 with an unexpected response"
                cat "$response"
                exit 1
              }
              ;;
            409)
              echo "::notice::PGXN reports that ${version} already exists; verifying immutable bytes"
              ;;
            *)
              echo "::error::PGXN upload failed with HTTP ${http_code}"
              cat "$response"
              exit 1
              ;;
          esac

          for attempt in $(seq 1 18); do
            if curl --fail --silent --show-error \
                --output "$mirrored" "$remote_archive" 2>/dev/null; then
              cmp --silent "$asset" "$mirrored" || {
                echo "::error::Published PGXN archive differs from the release asset"
                exit 1
              }
              echo "::notice::Published and byte-verified pg_local_cache ${version} on PGXN"
              exit 0
            fi
            sleep 5
          done

          echo "::error::PGXN accepted the upload but its API mirror did not expose the archive"
          exit 1
