#!/usr/bin/env bash # Prune old container image versions (tags) for an image in the Gitea registry. # # Keeps the moving tag (e.g. "44") and the newest versioned tag per distro, # plus the cosign signature tag (sha256-.sig) for each kept image. # # Usage: # REGISTRY_TOKEN= REGISTRY_USERNAME= \ # ./prune-registry.sh [image] [owner] [distro...] set -euo pipefail IMAGE="${1:-fedora-cosmic}"; shift || true OWNER="${1:-Misthios}"; shift || true DISTROS=("$@"); [[ ${#DISTROS[@]} -gt 0 ]] || DISTROS=(44 rawhide) GITEA_URL="${GITEA_URL:-https://git.plabble.org}" REGISTRY_HOST="${REGISTRY_HOST:-$(printf '%s' "${GITEA_URL}" | sed -E 's#https?://##')}" TOKEN="${REGISTRY_TOKEN:?REGISTRY_TOKEN (write:package) is required}" AUTHFILE="${AUTHFILE:-${HOME}/.docker/config.json}" API="${GITEA_URL%/}/api/v1/packages/${OWNER}/container/${IMAGE}" # Registry paths are lowercase (the Gitea API owner is not). IMAGE_REF="${REGISTRY_HOST}/$(printf '%s' "${OWNER}" | tr '[:upper:]' '[:lower:]')/${IMAGE}" # Collect all versions (paginated). versions="" page=1 while :; do page_json="$(curl -fsSL -H "Authorization: token ${TOKEN}" \ "${API}?limit=50&page=${page}")" || break [[ "$(jq 'length' <<< "${page_json}")" -eq 0 ]] && break versions+="$(jq -r '.[].version' <<< "${page_json}")"$'\n' page=$((page + 1)) [[ "${page}" -gt 40 ]] && break done # Decide what to keep. declare -A keep=() for d in "${DISTROS[@]}"; do keep["${d}"]=1 # A distro that has no versioned tags yet is fine; don't let grep's non-zero # exit status abort the script under set -e/pipefail. newest="$(grep -E "^${d}\.[0-9]+\.[0-9]+$" <<< "${versions}" \ | sort -t. -k2,2n -k3,3n | tail -1 || true)" [[ -n "${newest}" ]] && keep["${newest}"]=1 done # Keep the cosign signature of each kept image tag. If no digest could be # resolved, keep every signature tag rather than risk deleting a needed one. resolved=0 for tag in "${!keep[@]}"; do [[ "${tag}" == sha256-* ]] && continue digest="$(skopeo inspect --authfile "${AUTHFILE}" --format '{{.Digest}}' \ "docker://${IMAGE_REF}:${tag}" 2>/dev/null || true)" if [[ -n "${digest}" ]]; then keep["sha256-${digest#sha256:}.sig"]=1 resolved=1 fi done if [[ "${resolved}" -eq 0 ]]; then echo "WARNING: could not resolve any image digest; keeping all signature tags" while IFS= read -r v; do [[ "${v}" == sha256-* ]] && keep["${v}"]=1 done <<< "${versions}" fi # Delete everything else. while IFS= read -r v; do [[ -z "${v}" ]] && continue if [[ -n "${keep[${v}]:-}" ]]; then echo "keep ${v}" else echo "remove ${v}" curl -fsSL -X DELETE -H "Authorization: token ${TOKEN}" \ "${API}/${v}" >/dev/null || echo " (failed to delete ${v})" fi done <<< "${versions}" echo "Pruned ${IMAGE} registry versions."