I scanned 5 popular OSS repos in 5 minutes. Here's what I found.
Earlier today I shipped scan.html: a one-page
in-browser tool that takes any public GitHub repo URL, fetches its
.github/workflows/*.yml, and returns a per-workflow report
using ci-doctor
(14 rules) and gha-budget
(per-job pricing). Runs entirely client-side via the GitHub public API.
No signup, nothing uploaded.
To make sure it actually works on real-world repos and not just on the canned examples I built it against, I picked 5 well-known npm-ecosystem repos that I had not specifically optimized for, and ran them through. All 5 are maintained by experienced engineers. None of these are random small repos; they all matter.
The 5 repos
| Repo | Workflows | Per-run $ | Modeled $/mo* | Findings (err/warn/info) |
|---|---|---|---|---|
| axios/axios | 8 | $2.62 | $2,362 | 40 (12 / 27 / 1) |
| eslint/eslint | 8 | $1.54 | $1,382 | 46 (0 / 40 / 6) |
| vitejs/vite | 12 | $1.09 | $979 | 26 (0 / 25 / 1) |
| prettier/prettier | 17 | $1.02 | $922 | 32 (6 / 23 / 3) |
| sveltejs/svelte | 5 | $0.70 | $634 | 14 (0 / 10 / 4) |
| Total | 50 | $6.97 | $6,279 | 158 (18 / 125 / 15) |
*Modeled at 30 runs/day, 8 min/job, on standard ubuntu-latest
GitHub-hosted runner pricing. Real spend depends on actual run frequency,
runner choice, and OSS rate-limit credits. The point of the column is
comparison, not accusation.
The same 3 rules show up in all 5 repos
This is the part I find genuinely interesting. These repos have nothing in common architecturally - vite is a bundler, axios is an HTTP client, eslint is a static analyzer, etc. - but the top-3 ci-doctor findings are nearly identical across all of them:
-
missing-timeout(76 hits across 5 repos). Notimeout-minutes:on jobs, so a hung step bills until GitHub's 6-hour default cap. Every repo has this. -
missing-concurrency(20 hits). Push 3 commits to a PR in 30 seconds, you get 3 stacked CI runs and GitHub bills all 3.concurrency:withcancel-in-progress: truekills the first 2 in milliseconds. Free 30-50% CI savings on PR-heavy repos. -
missing-cache(16 hits, mostly in eslint).actions/setup-nodewithoutcache: 'npm'/'pnpm'/'yarn'means every job re-downloadsnode_modules. Slow and expensive.
The interesting outlier is axios/axios with 12
error-severity findings. All 12 are
deprecated-action: workflows still pinned to
actions/checkout@v3, actions/setup-node@v3,
and actions/upload-artifact@v3. v3 of upload-artifact was
deprecated in late 2024 and the v3 endpoint is being shut off.
These are not "save 3% of your CI bill" findings; these are "your CI
will silently start failing" findings.
Why these specific 3 are everywhere
My theory: GitHub Actions doesn't push you to add any of them. The workflow files YAML-validates fine without a timeout, without concurrency, without a cache. The CI passes. The PR ships. There is no linter built in to nudge anyone toward better defaults. So the same 3 smells survive in every repo I scan, including in mine before I built ci-doctor.
This is a tooling problem, not a competence problem. The maintainers of all 5 of these repos are excellent engineers. The smells are just invisible until something points at them.
Run this scan on your own repo (free, browser-only)
Paste any public GitHub repo URL. Get the same per-workflow report above for your own code in about 10 seconds. Shareable result URL. Nothing uploaded.
What to do about it (5-minute fixes)
For each of the top 3 rules, here's the smallest possible change:
1. Add a job-level timeout
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 15 # <-- add this
steps: ...
2. Add concurrency at workflow scope
# top of every workflow that runs on PRs
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
3. Tell setup-node what package manager you use
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # or 'pnpm' or 'yarn'
Or just run npx ci-doctor --fix and let it patch
missing-concurrency, missing-timeout,
wide-trigger, and artifact-no-retention
in place. The other 10 rules need a human to look at them, but those 4 are mechanical.
Cookbook: 30 paste-ready GitHub Actions patterns
If the in-browser report flags 5 things in your workflow and you'd rather just copy a known-good template than fix one rule at a time, the Cut Your CI Bill cookbook ships 30 production patterns: monorepo dispatch, OIDC publish, security gates, matrix trims, the works. $19 one-time, MIT-licensed templates.
Get the cookbook Or read 5 free patterns first Hardened workflows pack ($9)
Methodology + raw data
- All 5 repos were pulled via the GitHub public API, no auth.
- Each
.github/workflows/*.ymlwas passed throughci-doctor0.4.1 (14 rules) andgha-budgetfor per-job pricing. - Cost = sum of all jobs at GitHub-hosted standard
ubuntu-latestrates, assuming 8 min/job. - Monthly = per-run * 30 runs/day * 30 days. Self-hosted and large-runner jobs are unpriced.
- This is the same engine that runs in the browser at
/scan.html. The 20-repo version of this analysis lives at/benchmarks.htmlwith per-repo deep dives.
What this is NOT
This isn't an attack on any of these projects. They all ship excellent software, and "modeled $/mo" is not the same as "actual $/mo" - large OSS projects get free GitHub Actions credits, run things conditionally, and use self-hosted runners for the heavy lifting. The point is that the same three workflow-YAML patterns show up in every repo I scan, including mine before I started building ci-doctor.