diff --git a/.github/workflows/acceptance-test.yml b/.github/workflows/acceptance-test.yml index 5573daaef68..a6526049e80 100644 --- a/.github/workflows/acceptance-test.yml +++ b/.github/workflows/acceptance-test.yml @@ -12,9 +12,10 @@ # wrong or the architecture is missing from Release, the install fails, which # is the point. # -# It runs against a release candidate on the development server, which serves -# its tree the moment it is written. The download server sits behind an hourly -# sync to the mirrors, so testing there would mostly measure the sync. +# It runs against each release candidate on the development server, which +# serves its tree the moment it is written, and against each published nightly +# snapshot on ftp.postgresql.org, where snapshot.yml first waits for the hourly +# sync to the mirrors to deliver everything this test fetches. name: Acceptance test diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 71a6d33b10b..edf5e288f8c 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -463,3 +463,101 @@ jobs: else publish snapshot-purge fi + + # The published snapshot reaches ftp.postgresql.org on the hourly sync from + # the download server, not when the publish step finishes, so the acceptance + # test cannot simply follow it. This waits until everything that test will + # fetch is being served, the signatures and repository indexes included, + # since those are written last on the server. + # + # Each request carries a throwaway query string. The mirrors sit behind + # Varnish, and a plain request for a file that has not arrived yet could + # leave a cached 404 for exactly the URL the acceptance test asks for next. + mirrored: + needs: [location, windows, assemble] + if: >- + always() + && needs.assemble.result == 'success' + && (github.event_name == 'schedule' || inputs.publish) + runs-on: ubuntu-24.04 + timeout-minutes: 180 + steps: + - name: Wait for the snapshot to reach the mirrors + env: + BASE_URL: https://ftp.postgresql.org/pub/pgadmin/pgadmin4/snapshots/${{ needs.location.outputs.name }} + # The Windows runner is allowed to be offline, in which case the + # snapshot has no installer and waiting for one would only time out. + # The acceptance test still fails for it, which is right: the run + # is already red from the Windows job. + WINDOWS: ${{ needs.windows.result }} + run: | + set -euo pipefail + + # Bounded, so that one stalled connection to a mirror cannot use up + # the job's time limit and leave no polls after it. + fetch() { + curl -fsS --connect-timeout 10 --max-time 60 \ + "${BASE_URL}/$1?mirror-check=$(date +%s)" + } + + complete() { + local listing name + listing=$(fetch '') || return 1 + local wanted=( + 'pgadmin4-[0-9.]+\.tar\.gz\.asc' + 'pgadmin4-[0-9.]+-py3-none-any\.whl\.asc' + 'pgadmin4-[0-9.]+-docs\.tar\.gz\.asc' + 'pgadmin4-[0-9.]+\.pdf\.asc' + 'pgadmin4-[0-9.]+\.epub\.asc' + 'pgadmin4-[0-9.]+-arm64\.dmg' + 'pgadmin4-[0-9.]+-x64\.dmg' + ) + [ "${WINDOWS}" = "success" ] && wanted+=('pgadmin4-[0-9.]+-x64\.exe') + for name in "${wanted[@]}"; do + grep -Eq "\"${name}\"" <<< "${listing}" || { + echo " not yet: ${name}"; return 1; } + done + + for name in bookworm trixie jammy noble resolute; do + fetch "apt/${name}/dists/pgadmin4/InRelease" >/dev/null || { + echo " not yet: apt/${name}"; return 1; } + done + for name in redhat/rhel-9 redhat/rhel-10 fedora/fedora-43 fedora/fedora-44; do + fetch "yum/${name}-x86_64/repodata/repomd.xml.asc" >/dev/null || { + echo " not yet: yum/${name}"; return 1; } + done + } + + # Up to two and a half hours: two sync intervals and some slack. The + # limit is on elapsed time rather than on attempts, since a check can + # make ten requests of up to a minute each against a stalled mirror, + # and counting attempts would then run into the job's own timeout. + deadline=$((SECONDS + 150 * 60)) + attempt=0 + while :; do + attempt=$((attempt + 1)) + echo "Check ${attempt} of ${BASE_URL}" + if complete; then + echo "The snapshot is being served." + exit 0 + fi + [ "${SECONDS}" -lt "${deadline}" ] || break + # The last wait is cut short, so that a final check still runs + # at the deadline rather than being skipped. + wait=$((deadline - SECONDS)) + if [ "${wait}" -gt 300 ]; then + wait=300 + fi + sleep "${wait}" + done + echo "The snapshot had not reached the mirrors after 150 minutes." >&2 + exit 1 + + # Installing from the published snapshot, as the release candidate does, + # so that a defect only visible in what a user downloads shows up the next + # morning rather than when a release is being cut. + acceptance: + needs: [location, mirrored] + uses: ./.github/workflows/acceptance-test.yml + with: + base-url: https://ftp.postgresql.org/pub/pgadmin/pgadmin4/snapshots/${{ needs.location.outputs.name }}