|
1 | 1 | #!/usr/bin/env bash |
2 | 2 | # Delete e2e-* image tags older than 7 days from capcs-staging on quay.io. |
3 | | -# Requires QUAY_E2E_TOKEN environment variable (OAuth token with repo:admin scope). |
| 3 | +# Requires regctl to be installed and authenticated (via docker login or regctl registry login). |
| 4 | +# If QUAY_E2E_USERNAME and QUAY_E2E_PASSWORD are set, the script logs in automatically. |
4 | 5 | # Set DRY_RUN=true to list tags without deleting them. |
5 | 6 |
|
6 | 7 | set -euo pipefail |
7 | 8 |
|
8 | | -REPO="cloudscalech/capcs-staging" |
9 | | -API="https://quay.io/api/v1/repository/${REPO}/tag/" |
| 9 | +REPO="quay.io/cloudscalech/capcs-staging" |
10 | 10 | MAX_AGE_DAYS="${MAX_AGE_DAYS:-7}" |
11 | 11 | DRY_RUN="${DRY_RUN:-false}" |
12 | 12 |
|
13 | | -if [[ -z "${QUAY_E2E_TOKEN:-}" ]]; then |
14 | | - echo "Error: QUAY_E2E_TOKEN environment variable is required" >&2 |
| 13 | +if ! command -v regctl &>/dev/null; then |
| 14 | + echo "Error: regctl is not installed" >&2 |
15 | 15 | exit 1 |
16 | 16 | fi |
17 | 17 |
|
| 18 | +# Login if credentials are provided |
| 19 | +if [[ -n "${QUAY_E2E_USERNAME:-}" && -n "${QUAY_E2E_PASSWORD:-}" ]]; then |
| 20 | + echo "${QUAY_E2E_PASSWORD}" | regctl registry login quay.io --user "${QUAY_E2E_USERNAME}" --pass-stdin |
| 21 | +fi |
| 22 | + |
| 23 | +# Calculate cutoff timestamp |
18 | 24 | cutoff=$(date -u -v-${MAX_AGE_DAYS}d +%s 2>/dev/null || date -u -d "${MAX_AGE_DAYS} days ago" +%s) |
19 | 25 |
|
20 | 26 | if [[ "${DRY_RUN}" == "true" ]]; then |
21 | 27 | echo "DRY RUN: will list tags without deleting them" |
22 | 28 | fi |
23 | 29 | echo "Listing e2e-* tags older than ${MAX_AGE_DAYS} days..." |
24 | 30 |
|
25 | | -page=1 |
| 31 | +# List all tags and filter for e2e-* prefix |
| 32 | +tags=$(regctl tag ls "${REPO}" | grep '^e2e-' || true) |
| 33 | + |
| 34 | +if [[ -z "${tags}" ]]; then |
| 35 | + echo "No e2e-* tags found." |
| 36 | + exit 0 |
| 37 | +fi |
| 38 | + |
26 | 39 | deleted=0 |
27 | | -while true; do |
28 | | - response=$(curl -s -H "Authorization: Bearer ${QUAY_E2E_TOKEN}" \ |
29 | | - "${API}?filter_tag_name=like:e2e-%25&limit=100&page=${page}") |
| 40 | +while IFS= read -r name; do |
| 41 | + # Get the image creation timestamp (RFC3339 format) |
| 42 | + created=$(regctl image config "${REPO}:${name}" --format '{{.Created}}' 2>/dev/null || true) |
| 43 | + |
| 44 | + if [[ -z "${created}" ]]; then |
| 45 | + echo "Warning: could not get creation time for tag ${name}, skipping" |
| 46 | + continue |
| 47 | + fi |
30 | 48 |
|
31 | | - tags=$(echo "${response}" | jq -r '.tags // [] | .[] | select(.end_ts == null) | "\(.name) \(.start_ts)"') |
| 49 | + # Convert RFC3339 to epoch (strip fractional seconds and Z suffix for macOS compatibility) |
| 50 | + stripped=$(echo "${created}" | sed 's/\.[0-9]*Z$//' | sed 's/Z$//') |
| 51 | + created_ts=$(date -u -jf "%Y-%m-%dT%H:%M:%S" "${stripped}" +%s 2>/dev/null \ |
| 52 | + || date -u -d "${created}" +%s 2>/dev/null \ |
| 53 | + || true) |
32 | 54 |
|
33 | | - if [[ -z "${tags}" ]]; then |
34 | | - break |
| 55 | + if [[ -z "${created_ts}" ]]; then |
| 56 | + echo "Warning: could not parse creation time '${created}' for tag ${name}, skipping" |
| 57 | + continue |
35 | 58 | fi |
36 | 59 |
|
37 | | - while IFS=' ' read -r name start_ts; do |
38 | | - if [[ "${start_ts}" -lt "${cutoff}" ]]; then |
39 | | - created=$(date -u -r "${start_ts}" 2>/dev/null || date -u -d "@${start_ts}") |
40 | | - if [[ "${DRY_RUN}" == "true" ]]; then |
41 | | - echo "Would delete tag: ${name} (created ${created})" |
42 | | - else |
43 | | - echo "Deleting tag: ${name} (created ${created})" |
44 | | - curl -s -X DELETE -H "Authorization: Bearer ${QUAY_E2E_TOKEN}" "${API}${name}" > /dev/null |
45 | | - fi |
46 | | - deleted=$((deleted + 1)) |
| 60 | + if [[ "${created_ts}" -lt "${cutoff}" ]]; then |
| 61 | + created_human=$(date -u -r "${created_ts}" 2>/dev/null || date -u -d "@${created_ts}") |
| 62 | + if [[ "${DRY_RUN}" == "true" ]]; then |
| 63 | + echo "Would delete tag: ${name} (created ${created_human})" |
| 64 | + else |
| 65 | + echo "Deleting tag: ${name} (created ${created_human})" |
| 66 | + regctl tag delete "${REPO}:${name}" |
47 | 67 | fi |
48 | | - done <<< "${tags}" |
49 | | - |
50 | | - has_more=$(echo "${response}" | jq -r '.has_additional') |
51 | | - if [[ "${has_more}" != "true" ]]; then |
52 | | - break |
| 68 | + deleted=$((deleted + 1)) |
53 | 69 | fi |
54 | | - page=$((page + 1)) |
55 | | -done |
| 70 | +done <<< "${tags}" |
56 | 71 |
|
57 | 72 | if [[ "${DRY_RUN}" == "true" ]]; then |
58 | 73 | echo "Would delete ${deleted} e2e tag(s)." |
|
0 commit comments