|
| 1 | +#!/usr/bin/env bash |
| 2 | +# |
| 3 | +# Prune stale CodeQL Actions caches, keeping only the most recently created |
| 4 | +# entry per cache "group". CodeQL's default setup creates a new |
| 5 | +# overlay-base-database cache per commit and never reaps the old ones, which |
| 6 | +# pushes the repo over the 10 GiB per-repo cache budget. |
| 7 | +# |
| 8 | +# Grouping: |
| 9 | +# codeql-overlay-base-database-* -> strip trailing "-<sha>-<runid>-1" |
| 10 | +# codeql-dependencies-* -> exact key (GH allows duplicate keys) |
| 11 | +# |
| 12 | +# Requires GH_TOKEN with `actions: write`. Default is dry-run; pass --delete |
| 13 | +# to actually remove caches. Safe to run concurrently from multiple jobs: a |
| 14 | +# delete that races with another job just returns "not found", which is |
| 15 | +# treated as success. |
| 16 | + |
| 17 | +set -euo pipefail |
| 18 | + |
| 19 | +DELETE=0 |
| 20 | +if [[ "${1:-}" == "--delete" ]]; then |
| 21 | + DELETE=1 |
| 22 | +fi |
| 23 | + |
| 24 | +mapfile -t rows < <( |
| 25 | + gh cache list --key "codeql" --limit 1000 \ |
| 26 | + --json id,key,createdAt \ |
| 27 | + --jq '.[] | [.id, .key, .createdAt] | @tsv' |
| 28 | +) |
| 29 | + |
| 30 | +declare -A newest_id newest_time |
| 31 | + |
| 32 | +for row in "${rows[@]}"; do |
| 33 | + IFS=$'\t' read -r id key created <<<"$row" |
| 34 | + |
| 35 | + if [[ "$key" == codeql-overlay-base-database-* ]]; then |
| 36 | + group=$(printf '%s' "$key" | sed -E 's/-[0-9a-f]{40}-[0-9]+-1$//') |
| 37 | + else |
| 38 | + group="$key" |
| 39 | + fi |
| 40 | + |
| 41 | + # ISO8601 sorts lexicographically; keep the newest per group. |
| 42 | + if [[ -z "${newest_time[$group]:-}" || "$created" > "${newest_time[$group]}" ]]; then |
| 43 | + newest_time[$group]="$created" |
| 44 | + newest_id[$group]="$id" |
| 45 | + fi |
| 46 | +done |
| 47 | + |
| 48 | +echo "Groups found: ${#newest_id[@]}" |
| 49 | +for g in "${!newest_id[@]}"; do |
| 50 | + echo " keep id=${newest_id[$g]} @ ${newest_time[$g]} ($g)" |
| 51 | +done |
| 52 | +echo |
| 53 | + |
| 54 | +deleted=0 |
| 55 | +for row in "${rows[@]}"; do |
| 56 | + IFS=$'\t' read -r id key created <<<"$row" |
| 57 | + |
| 58 | + if [[ "$key" == codeql-overlay-base-database-* ]]; then |
| 59 | + group=$(printf '%s' "$key" | sed -E 's/-[0-9a-f]{40}-[0-9]+-1$//') |
| 60 | + else |
| 61 | + group="$key" |
| 62 | + fi |
| 63 | + |
| 64 | + [[ "$id" == "${newest_id[$group]}" ]] && continue |
| 65 | + |
| 66 | + if (( DELETE )); then |
| 67 | + # Tolerate races: another concurrent job may have already deleted it. |
| 68 | + if out=$(gh cache delete "$id" 2>&1); then |
| 69 | + echo "deleted id=$id key=$key" |
| 70 | + elif printf '%s' "$out" | grep -qi 'not found\|HTTP 404'; then |
| 71 | + echo "already gone id=$id key=$key" |
| 72 | + else |
| 73 | + echo "::warning::failed to delete id=$id: $out" |
| 74 | + continue |
| 75 | + fi |
| 76 | + else |
| 77 | + echo "would delete id=$id key=$key" |
| 78 | + fi |
| 79 | + deleted=$((deleted + 1)) |
| 80 | +done |
| 81 | + |
| 82 | +echo |
| 83 | +echo "Total $( (( DELETE )) && echo removed || echo to remove ): $deleted" |
| 84 | +(( DELETE )) || echo "(dry run — re-run with --delete to apply)" |
0 commit comments