From 1957fe151523a5d56b0cd5b4e73add5acddc98ec Mon Sep 17 00:00:00 2001 From: Kasra Bigdeli Date: Fri, 18 Sep 2026 17:39:58 -0700 Subject: [PATCH] Add edge tag cleanup script --- release/cleanup-edge-tags.sh | 95 ++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 release/cleanup-edge-tags.sh diff --git a/release/cleanup-edge-tags.sh b/release/cleanup-edge-tags.sh new file mode 100644 index 00000000..90cb48a6 --- /dev/null +++ b/release/cleanup-edge-tags.sh @@ -0,0 +1,95 @@ +#!/bin/bash + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +. "$SCRIPT_DIR/release.conf" + +KEEP_EDGE_COMMIT_TAGS="${KEEP_EDGE_COMMIT_TAGS:-100}" + +if ! [[ "$KEEP_EDGE_COMMIT_TAGS" =~ ^[0-9]+$ ]] || [ "$KEEP_EDGE_COMMIT_TAGS" -lt 1 ]; then + echo "KEEP_EDGE_COMMIT_TAGS must be a positive integer." >&2 + exit 1 +fi + +if [ -z "${REGISTRY_USERNAME:-}" ] || [ -z "${REGISTRY_PASSWORD:-}" ]; then + echo "REGISTRY_USERNAME and REGISTRY_PASSWORD are required." >&2 + exit 1 +fi + +if ! command -v curl >/dev/null 2>&1 || ! command -v jq >/dev/null 2>&1; then + echo "curl and jq are required." >&2 + exit 1 +fi + +if [[ "$EDGE_IMAGE_NAME" != */* ]]; then + echo "EDGE_IMAGE_NAME must be in namespace/repository format." >&2 + exit 1 +fi + +NAMESPACE="${EDGE_IMAGE_NAME%%/*}" +REPOSITORY="${EDGE_IMAGE_NAME#*/}" +API_BASE="https://hub.docker.com/v2/namespaces/$NAMESPACE/repositories/$REPOSITORY" + +AUTH_RESPONSE="$( + jq -nc '{identifier: env.REGISTRY_USERNAME, secret: env.REGISTRY_PASSWORD}' | + curl --fail-with-body --silent --show-error \ + --request POST \ + --header 'Content-Type: application/json' \ + --data-binary @- \ + 'https://hub.docker.com/v2/auth/token' +)" +HUB_TOKEN="$(jq -r '.access_token // empty' <<<"$AUTH_RESPONSE")" + +if [ -z "$HUB_TOKEN" ]; then + echo "Docker Hub authentication did not return an access token." >&2 + exit 1 +fi + +TAG_FILE="$(mktemp)" +trap 'rm -f "$TAG_FILE"' EXIT + +NEXT_URL="$API_BASE/tags?page_size=100&page=1" +while [ -n "$NEXT_URL" ]; do + TAG_RESPONSE="$( + curl --fail-with-body --silent --show-error \ + --header "Authorization: Bearer $HUB_TOKEN" \ + "$NEXT_URL" + )" + + jq -c ' + .results[] + | select((.name // "") | test("^[0-9a-fA-F]{40}$")) + | {name, last_updated} + ' <<<"$TAG_RESPONSE" >>"$TAG_FILE" + + NEXT_URL="$(jq -r '.next // empty' <<<"$TAG_RESPONSE")" +done + +COMMIT_TAG_COUNT="$(wc -l <"$TAG_FILE" | tr -d ' ')" +if [ "$COMMIT_TAG_COUNT" -le "$KEEP_EDGE_COMMIT_TAGS" ]; then + echo "Found $COMMIT_TAG_COUNT edge commit tags; nothing to delete." + exit 0 +fi + +TAGS_TO_DELETE="$( + jq -s -r --argjson keep "$KEEP_EDGE_COMMIT_TAGS" ' + sort_by([.last_updated // "", .name]) + | reverse + | .[$keep:][] + | .name + ' "$TAG_FILE" +)" + +DELETE_COUNT=$((COMMIT_TAG_COUNT - KEEP_EDGE_COMMIT_TAGS)) +echo "Keeping the newest $KEEP_EDGE_COMMIT_TAGS of $COMMIT_TAG_COUNT edge commit tags; deleting $DELETE_COUNT." + +while IFS= read -r TAG; do + [ -n "$TAG" ] || continue + echo "Deleting old edge commit tag: $TAG" + curl --fail-with-body --silent --show-error \ + --request DELETE \ + --header "Authorization: Bearer $HUB_TOKEN" \ + "$API_BASE/tags/$TAG" \ + >/dev/null +done <<<"$TAGS_TO_DELETE"