🔧 Infra · 🧹 Cleanup Caches #2
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: '🔧 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') | |
| " |