build-cran-binaries/scripts/purge_cdn_zone.sh
pat-s 9bded261ee
All checks were successful
ci/crow/cron/process-updates/13 Pipeline was successful
ci/crow/cron/process-updates/10 Pipeline was successful
ci/crow/cron/process-updates/15 Pipeline was successful
ci/crow/cron/process-updates/14 Pipeline was successful
ci/crow/cron/process-updates/4 Pipeline was successful
ci/crow/cron/process-updates/16 Pipeline was successful
ci/crow/cron/process-updates/18 Pipeline was successful
ci/crow/cron/process-updates/11 Pipeline was successful
ci/crow/cron/process-updates/17 Pipeline was successful
ci/crow/cron/process-updates/12 Pipeline was successful
ci/crow/cron/process-updates/6 Pipeline was successful
ci/crow/cron/process-updates/5 Pipeline was successful
ci/crow/cron/process-updates/1 Pipeline was successful
ci/crow/cron/process-updates/2 Pipeline was successful
ci/crow/cron/process-updates/7 Pipeline was successful
ci/crow/cron/process-updates/8 Pipeline was successful
ci/crow/cron/process-updates/9 Pipeline was successful
ci/crow/cron/process-updates/3 Pipeline was successful
fix(cdn): restore Alliance pull-zone hostname (#166)
## Motivation

Applying #165 recreated the Alliance SwissPass pull zone without its custom hostname because the hostname association was not represented in OpenTofu.
The recreated zone also received a new numeric ID, making the weekly purge configuration stale.

## Changes

- Manage `cran.allianceswisspass.devxy.io` as a pull-zone hostname with TLS and forced HTTPS.
- Resolve the Alliance pull-zone ID from its hostname before purging instead of persisting a replaceable numeric ID.
- Install `jq` in the purge step for the Bunny API lookup.

## Verification

- Targeted `prek` hooks pass.
- `tofu validate` passes.
- `crow lint .crow/` passes.
- `just edge-test` passes all 14 routing steps.
- `bash -n scripts/purge_cdn_zone.sh` passes.

## Deployment

Run `tofu apply` to restore the Alliance hostname on the recreated pull zone.

Reviewed-on: #166
2026-08-13 14:13:04 +00:00

87 lines
2.6 KiB
Shell
Executable file

#!/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 <BUNNYNET_API_KEY> <pull_zone> [<pull_zone>...]
#
set -euo pipefail
if (($# < 2)); then
echo "usage: $0 <api_key> <pull_zone> [<pull_zone>...]" >&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