#!/usr/bin/env bash # # Purge the entire BunnyCDN pull zone. # # `purge_cdn_cache.sh` purges the five index files by URL, which is right after # a normal update: new packages arrive at new URLs, so only the index is stale. # # A rebuild is different. It replaces an object *in place*: a package whose # build failed was published as its CRAN source, and the rebuilt binary takes # exactly the same URL. The zone caches tarballs for ~370 days # (`cache_expiration_time` in cdn.tf), so without a purge every client keeps # receiving the source tarball for up to a year, and nothing about it looks # wrong from the outside. # # Purging per URL would mean one API call per replaced package -- ~13.5k per # arch against a rate-limited endpoint, where a single missed call leaves a # silently stale package. One zone purge is a single call regardless of how many # objects were replaced. The cost is a cold cache for everything else, which is # why this is not used by the daily update path. # # The public hostnames currently use separate pull zones, so callers must pass # every zone that serves the repository. A zone can be identified by its # numeric ID or by one of its hostnames; hostname lookup avoids persisting IDs # that change when a zone is recreated. # # Usage: # purge_cdn_zone.sh [...] # set -euo pipefail if (($# < 2)); then echo "usage: $0 [...]" >&2 exit 2 fi api_key="$1" shift resolve_zone_id() { local zone="$1" local response_file local zone_id if [[ "${zone}" =~ ^[0-9]+$ ]]; then echo "${zone}" return fi response_file=$(mktemp) curl -sS -o "${response_file}" \ -H "AccessKey: ${api_key}" \ "https://api.bunny.net/pullzone" zone_id=$( jq -r --arg hostname "${zone}" \ '(.Items // .)[] | select(any(.Hostnames[]?; .Value == $hostname)) | .Id' \ "${response_file}" ) rm -f "${response_file}" if [[ -z "${zone_id}" ]]; then echo "Could not find BunnyCDN pull zone for hostname ${zone}" >&2 exit 1 fi echo "${zone_id}" } for zone in "$@"; do zone_id=$(resolve_zone_id "${zone}") echo "Purging BunnyCDN pull zone ${zone_id}" response_file="/tmp/purge_zone_response_${zone_id}.txt" status=$( curl -sS -o "${response_file}" -w '%{http_code}' -X POST \ -H "AccessKey: ${api_key}" \ -H "Content-Length: 0" \ "https://api.bunny.net/pullzone/${zone_id}/purgeCache" ) if [[ "${status}" != "200" && "${status}" != "204" ]]; then echo "Purge of pull zone ${zone_id} failed with HTTP ${status}:" >&2 cat "${response_file}" >&2 exit 1 fi echo "Purged pull zone ${zone_id} (HTTP ${status})" done