Skip to content
Back to skills

Finops Caches

ASecurity

Analyze cache usage - size, breakdown by prefix/branch, stale cache detection

  • 58 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added February 8, 2026
toolsgobashnodegitapi

Works with

  • api

Security analysis

A100/100

Scanned February 12, 2026

npx -y skills add laurigates/claude-plugins --skill finops-caches --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Finops Caches?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Finops Caches
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/laurigates-finops-caches/badge)](https://www.skillsdirectory.com/skills/laurigates-finops-caches)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
model: haiku
description: Analyze cache usage - size, breakdown by prefix/branch, stale cache detection
args: "[repo|org:orgname]"
allowed-tools: Bash(gh api *), Bash(gh repo *), Read, TodoWrite
argument-hint: Repo (owner/name), org:orgname for org-wide, or empty for current repo
created: 2025-01-30
modified: 2025-01-30
reviewed: 2025-01-30
name: finops-caches
---

# /finops:caches

Analyze GitHub Actions cache usage - size breakdown, cache key patterns, branch distribution, and stale cache detection.

## Context

- Current repo: !`gh repo view --json nameWithOwner --jq '.nameWithOwner' 2>/dev/null`
- Repo owner: !`gh repo view --json owner --jq '.owner.login' 2>/dev/null`

## Parameters

| Parameter | Description | Default |
|-----------|-------------|---------|
| `repo` | Repository in owner/name format | Current repository |
| `org:orgname` | Analyze org-wide cache usage | - |

## Execution

### Org-wide Analysis (if arg starts with "org:")

```bash
ORG="${1#org:}"
echo "=== Org Cache Usage: $ORG ==="

# Org total
gh api /orgs/$ORG/actions/cache/usage \
  --jq '"Total: \(.total_active_caches_count) caches, \(.total_active_caches_size_in_bytes / 1024 / 1024 | floor)MB"'

echo ""
echo "=== Top Repos by Cache Size ==="

# Iterate repos and get cache usage
gh repo list $ORG --json nameWithOwner --limit 100 --jq '.[].nameWithOwner' | while read repo; do
  result=$(gh api "/repos/$repo/actions/cache/usage" 2>/dev/null)
  if [ -n "$result" ]; then
    size=$(echo "$result" | jq -r '.active_caches_size_in_bytes // 0')
    count=$(echo "$result" | jq -r '.active_caches_count // 0')
    if [ "$size" -gt 0 ]; then
      echo "$repo|$count|$size"
    fi
  fi
done | sort -t'|' -k3 -n -r | head -15 | while IFS='|' read repo count size; do
  mb=$((size / 1024 / 1024))
  echo "  $repo: $count caches, ${mb}MB"
done
```

### Per-Repo Analysis (default)

**1. Determine repository:**

```bash
REPO="${1:-$(gh repo view --json nameWithOwner --jq '.nameWithOwner')}"
echo "=== Cache Analysis: $REPO ==="
```

**2. Cache summary:**

```bash
echo ""
echo "Summary:"
gh api "/repos/$REPO/actions/cache/usage" \
  --jq '"  \(.active_caches_count) caches, \(.active_caches_size_in_bytes / 1024 / 1024 | floor)MB used"'

# Check against limit
SIZE=$(gh api "/repos/$REPO/actions/cache/usage" --jq '.active_caches_size_in_bytes')
LIMIT=$((10 * 1024 * 1024 * 1024))  # 10GB
PCT=$((SIZE * 100 / LIMIT))
echo "  $PCT% of 10GB limit"
```

**3. Breakdown by cache key prefix:**

```bash
echo ""
echo "=== By Key Prefix ==="
gh api "/repos/$REPO/actions/caches?per_page=100" \
  --jq '.actions_caches | group_by(.key | split("-") | .[0:2] | join("-")) |
        map({
          prefix: .[0].key | split("-") | .[0:2] | join("-"),
          count: length,
          size_mb: (map(.size_in_bytes) | add / 1024 / 1024 | floor)
        }) |
        sort_by(-.size_mb)[] |
        "  \(.prefix): \(.count) caches, \(.size_mb)MB"'
```

**4. Breakdown by branch:**

```bash
echo ""
echo "=== By Branch ==="
gh api "/repos/$REPO/actions/caches?per_page=100" \
  --jq '.actions_caches | group_by(.ref) |
        map({
          branch: (.[0].ref | sub("refs/heads/"; "") | sub("refs/pull/"; "PR ")),
          count: length,
          size_mb: (map(.size_in_bytes) | add / 1024 / 1024 | floor)
        }) |
        sort_by(-.size_mb)[] |
        "  \(.branch): \(.count) caches, \(.size_mb)MB"'
```

**5. Largest individual caches:**

```bash
echo ""
echo "=== Largest Caches ==="
gh api "/repos/$REPO/actions/caches?per_page=100" \
  --jq '.actions_caches | sort_by(-.size_in_bytes) | .[0:10][] |
        "  \(.key | .[0:60]): \(.size_in_bytes / 1024 / 1024 | floor)MB"'
```

**6. Stale cache detection (not accessed in 7+ days):**

```bash
echo ""
echo "=== Stale Caches (>7 days old) ==="
CUTOFF=$(date -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -v-7d +%Y-%m-%dT%H:%M:%SZ)
gh api "/repos/$REPO/actions/caches?per_page=100" \
  --jq --arg cutoff "$CUTOFF" '
    [.actions_caches[] | select(.last_accessed_at < $cutoff)] |
    {
      count: length,
      size_mb: (map(.size_in_bytes) | add / 1024 / 1024 | floor // 0),
      oldest: (sort_by(.last_accessed_at) | .[0].last_accessed_at // "none")
    } |
    "  \(.count) stale caches, \(.size_mb)MB reclaimable\n  Oldest: \(.oldest)"'
```

**7. PR branch caches (potential cleanup):**

```bash
echo ""
echo "=== PR Branch Caches ==="
gh api "/repos/$REPO/actions/caches?per_page=100" \
  --jq '[.actions_caches[] | select(.ref | startswith("refs/pull/"))] |
        {
          count: length,
          size_mb: (map(.size_in_bytes) | add / 1024 / 1024 | floor // 0)
        } |
        "  \(.count) PR caches, \(.size_mb)MB (check if PRs are merged/closed)"'
```

## Output Format

```
=== Cache Analysis: org/repo ===

Summary:
  45 caches, 2340MB used
  23% of 10GB limit

=== By Key Prefix ===
  node-modules: 12 caches, 1200MB
  playwright: 8 caches, 800MB
  turbo: 15 caches, 300MB

=== By Branch ===
  main: 10 caches, 500MB
  develop: 8 caches, 400MB
  PR 123: 3 caches, 150MB
  PR 118: 3 caches, 140MB

=== Largest Caches ===
  node-modules-linux-abc123: 180MB
  playwright-linux-def456: 150MB

=== Stale Caches (>7 days old) ===
  8 stale caches, 450MB reclaimable
  Oldest: 2025-01-15T10:30:00Z

=== PR Branch Caches ===
  12 PR caches, 580MB (check if PRs are merged/closed)
```

## Post-actions

Based on findings, suggest:
- High % of limit → Clean up stale caches, review key strategies
- Many PR caches → Add cache cleanup workflow for closed PRs
- Large single caches → Consider splitting or compressing
- Stale caches → Provide cleanup command:
  ```bash
  # Delete stale caches
  gh api "/repos/$REPO/actions/caches?per_page=100" \
    --jq '.actions_caches[] | select(.last_accessed_at < "CUTOFF") | .id' | \
    while read id; do gh api -X DELETE "/repos/$REPO/actions/caches/$id"; done
  ```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.