Cache Hygiene #8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| name: Cache Hygiene | |
| on: | |
| schedule: | |
| - cron: "35 2 * * 1" # Weekly on Monday (UTC) | |
| workflow_dispatch: | |
| inputs: | |
| dry_run: | |
| description: "Preview deletions without removing caches" | |
| required: false | |
| default: true | |
| type: boolean | |
| permissions: | |
| actions: write | |
| contents: read | |
| concurrency: | |
| group: cache-hygiene | |
| cancel-in-progress: true | |
| jobs: | |
| cleanup: | |
| name: Cleanup stale caches | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Prune old workflow caches | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| INPUT_DRY_RUN: ${{ inputs.dry_run || 'false' }} | |
| run: | | |
| set -euo pipefail | |
| dry_run="false" | |
| if [ "$EVENT_NAME" = "workflow_dispatch" ] && [ "$INPUT_DRY_RUN" = "true" ]; then | |
| dry_run="true" | |
| fi | |
| api="https://api.github.com/repos/${GITHUB_REPOSITORY}/actions/caches?per_page=100" | |
| payload="$(curl -fsSL \ | |
| -H "Authorization: Bearer ${GH_TOKEN}" \ | |
| -H "Accept: application/vnd.github+json" \ | |
| "$api")" | |
| total="$(jq -r '.total_count' <<<"$payload")" | |
| echo "Found ${total} cache entries. dry_run=${dry_run}" | |
| delete_for_prefix() { | |
| local prefix="$1" | |
| local keep="$2" | |
| mapfile -t rows < <(jq -r --arg p "$prefix" --argjson keep "$keep" ' | |
| [ .actions_caches[] | |
| | select(.key | startswith($p)) | |
| | { id, key, last_accessed_at } | |
| ] | |
| | sort_by(.last_accessed_at) | |
| | reverse | |
| | .[$keep:][]? | |
| | "\(.id)\t\(.key)\t\(.last_accessed_at)" | |
| ' <<<"$payload") | |
| if [ "${#rows[@]}" -eq 0 ]; then | |
| echo "No stale caches for prefix=${prefix}" | |
| return | |
| fi | |
| for row in "${rows[@]}"; do | |
| id="${row%%$'\t'*}" | |
| rest="${row#*$'\t'}" | |
| key="${rest%%$'\t'*}" | |
| last="${rest#*$'\t'}" | |
| if [ "$dry_run" = "true" ]; then | |
| echo "[dry-run] delete id=${id} key=${key} last_accessed=${last}" | |
| else | |
| echo "Deleting id=${id} key=${key} last_accessed=${last}" | |
| curl -fsSL -X DELETE \ | |
| -H "Authorization: Bearer ${GH_TOKEN}" \ | |
| -H "Accept: application/vnd.github+json" \ | |
| "https://api.github.com/repos/${GITHUB_REPOSITORY}/actions/caches/${id}" \ | |
| >/dev/null | |
| fi | |
| done | |
| } | |
| # Keep a small hot set per cache family. | |
| delete_for_prefix "codeql-overlay-base-database-" 4 | |
| delete_for_prefix "css-build-" 2 | |
| delete_for_prefix "zola-" 2 |