Find actions/cache keys that never hit, hashFiles() patterns that match nothing, missing restore-keys and uncached dependency installs. CachePilot then prints the optimized cache configuration.
A cache key with ${{ github.sha }} never gets an exact hit. hashFiles('package-lock.json') returns an empty string when the lockfile lives in web/, so the key never changes. Without restore-keys, every dependency bump starts from zero. A matrix with Linux, Windows and macOS keeps overwriting the same cache.
These mistakes don't fail the build. They just make it slow, so nobody notices. CachePilot finds them statically and tells you exactly what to change.
npx github:6t9xstar/github-actions-cache-optimizerCACHEPILOT REPORT
1 workflows · 2 jobs · 4 cache steps
[ERROR] job "test" actions/setup-node has cache: npm but package-lock.json, npm-shrinkwrap.json match
no file at the repository root; the step will fail.
CP010 .github/workflows/ci.yml:15 Fix: cache-dependency-path: web/package-lock.json
[ERROR] job "test" Cache key uses github.sha: the key is new on every run, so the cache is never restored.
CP001 .github/workflows/ci.yml:21 Fix: key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
[WARNING] job "test" Job runs on a multi-OS matrix but the key has no runner.os, so Linux, Windows and
macOS overwrite each other's caches.
[ERROR] job "test" hashFiles('package-lock.json') matches no files, so it returns '' and the key never changes.
CP003 .github/workflows/ci.yml:25 Fix: hashFiles('**/package-lock.json')
[WARNING] job "test" No restore-keys configured: after any lockfile change the job starts from an empty cache.
[ERROR] job "python" actions/cache@v2 no longer works: GitHub shut down the legacy cache service it uses in 2025.
[WARNING] job "python" Cache is restored at step 4, after "pip install -r requirements.txt" already ran at step 3.
[WARNING] job "python" Missing lockfile hash: key "pip-cache" never changes, so the cache keeps stale pip
dependencies forever.
Run `cachepilot optimize` to see the recommended cache configuration.
Recommended: include runner OS and lockfile hash in every dependency cache key.
Abridged output for fixtures/repo/.github/workflows/ci.yml.
cachepilot optimize prints a drop-in replacement for each problem:
- uses: actions/cache@v6
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-CachePilot parses every workflow, follows each job step by step, and resolves every hashFiles() pattern against the files in your repository using the same glob rules as @actions/glob. It knows the cache paths, lockfiles, install commands and built-in caching of 15 ecosystems: npm, Yarn, pnpm, Bun, pip, Poetry, Pipenv, uv, Maven, Gradle, Go, Cargo, Bundler, Composer and NuGet.
It understands actions/cache, actions/cache/restore, actions/cache/save, and the built-in caches of actions/setup-node, setup-python, setup-java, setup-go, setup-dotnet, ruby/setup-ruby, astral-sh/setup-uv and Swatinem/rust-cache.
No GitHub token and no network access are needed.
Requirements
- Node.js 18.17 or newer
- git (to install straight from GitHub)
- Windows, macOS or Linux
- No GitHub token or network access needed.
npx github:6t9xstar/github-actions-cache-optimizer # run once
npm install -g github:6t9xstar/github-actions-cache-optimizer # installs the cachepilot commandcachepilot # scan .github/workflows
cachepilot scan .github/workflows/ci.yml # one workflow
cachepilot optimize # print optimized cache steps
cachepilot --format sarif -o cachepilot.sarif
cachepilot rules| Option | Description |
|---|---|
--root <dir> |
Repository root used for hashFiles() checks (default .) |
-f, --format |
text, json, sarif, markdown, github |
-o, --output <file> |
Write the report to a file |
--fail-on <level> |
error (default), warning, info, none |
--ignore <ids> |
Skip rules, e.g. --ignore CP007,CP012 |
name: Workflow lint
on:
pull_request:
paths: ['.github/workflows/**']
jobs:
cachepilot:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: 6t9xstar/github-actions-cache-optimizer@v1
with:
fail-on: warning| Rule | Severity | Description |
|---|---|---|
| CP000 | error | Workflow is not valid YAML |
| CP001 | error / warning | Key uses github.sha, run_id, run_number, ... (never an exact hit; churns the 10 GB cache limit) |
| CP002 | warning | Dependency cache key without a lockfile hashFiles() (stale forever) |
| CP003 | error | hashFiles() pattern matches no file in the repository |
| CP004 | warning | No restore-keys |
| CP005 | warning / info | Restore key equal to the key, or not a prefix of it |
| CP006 | warning | Multi-OS matrix but no runner.os in the key |
| CP007 | info | Caching node_modules instead of the package-manager store |
| CP008 | warning | Same dependencies cached twice in one job |
| CP009 | error / warning / info | Outdated action: actions/cache v1–v2 no longer work; Node 16-era majors are deprecated |
| CP010 | error | setup-* cache enabled but the required lockfile is missing (with the right cache-dependency-path) |
| CP011 | warning | Cache restored after the install step |
| CP012 | info | Dependencies installed without any cache (with a ready-made snippet) |
| CP013 | error | hashFiles() with an absolute path or .. |
| CP014 | error | actions/cache missing path or key |
The most common causes are a key that changes on every run (github.sha), a hashFiles() pattern that matches no file (it returns an empty string), a cache restored after npm ci already ran, or actions/cache v1/v2, which stopped working when GitHub retired the legacy cache service. CachePilot checks all of these.
${{ runner.os }}-<tool>-${{ hashFiles('<lockfile glob>') }} with restore-keys: ${{ runner.os }}-<tool>-. It produces an exact hit when dependencies are unchanged and a partial hit after a lockfile change.
Usually not. node_modules depends on the OS and Node.js version and skips install scripts when restored. Cache ~/.npm or use actions/setup-node with cache: npm, and keep running npm ci.
No. It is a static analyzer and runs completely offline.
- WorkflowTriggerDoctor: why a GitHub Actions workflow did not run
- PackageProof: detect hallucinated and non-existent npm and PyPI packages
- PaginationProbe: REST API pagination tester
- GitHookDoctor: Git hooks, Husky, Lefthook and pre-commit troubleshooter
- NpmTarballAudit: npm package contents and secrets auditor
- CachePilot: GitHub Actions cache analyzer and optimizer
- ComposeDoctor: Docker Compose validator and troubleshooter
- DevContainer Doctor: devcontainer.json validator
- OwnerScope: CODEOWNERS validator and coverage checker
- ScriptPort: cross-platform npm scripts checker