Skip to content

🔧 Infra · 🧹 Cleanup Caches #2

🔧 Infra · 🧹 Cleanup Caches

🔧 Infra · 🧹 Cleanup Caches #2

name: '🔧 Infra · 🧹 Cleanup Caches'
# =============================================================================
# 🧹 Cleanup Caches — Purge stale GitHub Actions caches to free storage
# =============================================================================
#
# Lists all GHA caches, deletes those older than a configurable threshold
# (default 14 days), and reports remaining cache usage.
#
# Flow:
# 1. ENUMERATE — Fetch all caches via GitHub API
# 2. FILTER — Identify caches older than threshold
# 3. DELETE — Remove stale caches (or dry-run preview)
# 4. REPORT — Show remaining cache count and total size
#
# Triggers:
# - schedule: Weekly (Sunday 3am UTC, after container rebuilds)
# - workflow_dispatch: Manual with days threshold and dry_run toggle
#
# Secrets: GITHUB_TOKEN (automatic)
#
# Related:
# - infra-container-linux.yml — Produces caches cleaned by this workflow
# - infra-container-windows.yml — Produces caches cleaned by this workflow
# - infra-health-check.yml — Complementary infra maintenance
#
# =============================================================================
permissions:
actions: write
contents: read
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: true
on:
schedule:
- cron: '0 3 * * 0' # Weekly cleanup (Sunday at 3am UTC, after container rebuilds)
workflow_dispatch:
inputs:
days:
description: 'Delete caches older than this many days'
required: false
default: '14'
type: string
dry_run:
description: 'Show what would be deleted without actually deleting'
required: false
default: false
type: boolean
jobs:
cleanup:
name: '🔧 Infra · 🧹 Cleanup Caches'
runs-on: ubuntu-latest
if: github.repository_owner == 'harvard-edge'
steps:
- name: '📥 Checkout repository'
uses: actions/checkout@v6
- name: '🐍 Setup Python'
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: '📦 Install requests'
run: pip install requests
- name: '🗂️ Clean up old caches'
env:
# Use input values if available (manual trigger), otherwise use defaults (scheduled trigger)
DAYS_TO_KEEP: ${{ github.event.inputs.days || '14' }}
DRY_RUN: ${{ github.event.inputs.dry_run || 'false' }}
run: |
echo "🧹 Cleaning up caches older than ${DAYS_TO_KEEP} days..."
# Get caches
CACHES=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
"https://api.github.com/repos/${{ github.repository }}/actions/caches")
# Parse and filter caches
echo "$CACHES" | python3 -c "
import json
import sys
import requests
import os
from datetime import datetime, timedelta
data = json.load(sys.stdin)
days_to_keep = int(os.environ.get('DAYS_TO_KEEP', '7'))
cutoff_date = datetime.now() - timedelta(days=days_to_keep)
old_caches = []
for cache in data.get('actions_caches', []):
cache_date = datetime.fromisoformat(cache['created_at'].replace('Z', '+00:00'))
if cache_date < cutoff_date.replace(tzinfo=cache_date.tzinfo):
old_caches.append(cache)
print(f'Found {len(old_caches)} caches older than {days_to_keep} days:')
for cache in old_caches:
size_mb = cache['size_in_bytes'] / (1024 * 1024)
print(f' - {cache[\"key\"]} ({size_mb:.1f} MB) from {cache[\"ref\"]}')
dry_run = os.environ.get('DRY_RUN', 'false').lower() == 'true'
if dry_run:
print('\\n🔍 Dry run mode - no caches will be deleted')
else:
print('\\n🗑️ Deleting old caches...')
for cache in old_caches:
response = requests.delete(
f'https://api.github.com/repos/${{ github.repository }}/actions/caches/{cache[\"id\"]}',
headers={'Authorization': f'token ${{ secrets.GITHUB_TOKEN }}'}
)
if response.status_code == 204:
print(f' ✅ Deleted {cache[\"key\"]}')
else:
print(f' ❌ Failed to delete {cache[\"key\"]}: {response.status_code}')
"
- name: '📊 Show current cache usage'
run: |
echo "📊 Current cache usage:"
curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
"https://api.github.com/repos/${{ github.repository }}/actions/caches" | \
python3 -c "
import json
import sys
data = json.load(sys.stdin)
total_size = sum(cache['size_in_bytes'] for cache in data.get('actions_caches', []))
total_size_mb = total_size / (1024 * 1024)
print(f'Total caches: {len(data.get(\"actions_caches\", []))}')
print(f'Total size: {total_size_mb:.1f} MB')
"