https://github.com/didrod205/actionbudget
Lint GitHub Actions workflows for CI-minute waste โ missing caching, no concurrency cancel, no timeouts, oversized matrices, duplicate push+PR runs, costly runners. Each finding has a one-line fix. Deterministic CLI, JSON/MD reports.
https://github.com/didrod205/actionbudget
actionlint actions cache ci ci-cd ci-minutes cli concurrency cost-optimization developer-tools devops github-actions linter typescript workflow yaml
Last synced: about 13 hours ago
JSON representation
Lint GitHub Actions workflows for CI-minute waste โ missing caching, no concurrency cancel, no timeouts, oversized matrices, duplicate push+PR runs, costly runners. Each finding has a one-line fix. Deterministic CLI, JSON/MD reports.
- Host: GitHub
- URL: https://github.com/didrod205/actionbudget
- Owner: didrod205
- License: mit
- Created: 2026-06-04T06:25:19.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-06-29T07:23:22.000Z (6 days ago)
- Last Synced: 2026-06-29T09:16:48.498Z (6 days ago)
- Topics: actionlint, actions, cache, ci, ci-cd, ci-minutes, cli, concurrency, cost-optimization, developer-tools, devops, github-actions, linter, typescript, workflow, yaml
- Language: TypeScript
- Size: 49.8 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# ๐งพ actionbudget
### Find what's burning your GitHub Actions minutes โ before the bill does.
[](https://www.npmjs.com/package/actionbudget)
[](https://github.com/didrod205/actionbudget/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/actionbudget)
[](./LICENSE)
Your GitHub Actions usage creeps up and you find out from the **billing page** โ or
when the free minutes run out mid-sprint. The waste is hiding in plain sight in your
YAML: a workflow with **no `concurrency`** keeps three superseded runs alive on a
busy PR, `setup-node` **reinstalls deps every run** because caching is off, a
**push + pull_request** pair runs CI *twice* per commit, a **12-job matrix** pays
full setup nine extra times, and a job with **no `timeout-minutes`** can bill six
hours when it hangs.
**actionbudget reads your workflow files and lists exactly what's wasting
minutes โ locally, deterministically, with the one-line fix.** No app to install on
your org, no API token, no waiting for the invoice.
```bash
npx actionbudget scan
```
```
.github/workflows/ci.yml (CI) 64/100 (D)
โ No concurrency group โ superseded runs keep running L4 no-concurrency
โ setup-node without dependency caching job:test L20 missing-cache
โ push + pull_request run CI twice per PR commit L4 duplicate-triggers
โ Matrix expands to 12 jobs job:test L10 large-matrix
โ Schedule runs every ~5 min L6 frequent-schedule
โน macOS runner bills at 10ร Linux job:mac-build L26 costly-runner
```
---
## Why actionbudget?
- ๐ธ **It's about cost, not syntax.** `actionlint` checks your YAML is *valid*;
`zizmor` checks it's *secure*. Neither tells you it's **wasteful**. actionbudget
owns that gap โ caching, concurrency, matrices, timeouts, triggers, runners.
- ๐ฏ **Every finding has a fix.** Not "this could be better" โ the exact
`concurrency:` block, the `cache: npm` line, the `paths:` filter to add.
- ๐ **Local & deterministic.** Parses the YAML with a real parser (no guessing),
runs offline, gives the same result every time. Drop it in CI and the build fails
when a PR adds a 9ร matrix.
- ๐งฎ **Honest about cost.** It reports structural waste and multipliers (matrix
size, macOS 10ร / Windows 2ร) โ deterministic facts from your YAML, not a
made-up dollar figure.
Why not just ask an LLM "is my CI wasteful"? It can't see all your workflow files at
once, can't run on every PR, and "does push+pull_request double-run here" depends on
your exact branch filters โ a structural check, not a vibe.
## Install
```bash
# run it now, from your repo root
npx actionbudget scan
# or add it
npm install -g actionbudget # global CLI
npm install -D actionbudget # CI dependency
```
Node โฅ 18. With no arguments it scans `.github/workflows`.
## Quick start
```bash
actionbudget scan # scan .github/workflows
actionbudget scan path/to/ci.yml # a single file
actionbudget scan --min-score 80 # CI gate
actionbudget scan --md budget.md # Markdown report for a PR comment
actionbudget scan --disable no-timeout,no-path-filter
actionbudget init # write actionbudget.config.json
```
See [`examples/sample-report.md`](./examples/sample-report.md) for a full report and
[`examples/.github/workflows/lean.yml`](./examples/.github/workflows/lean.yml) for a
workflow that scores 100/100.
## What it checks
| Rule | What it catches |
| ---- | --------------- |
| `no-concurrency` / `no-cancel-in-progress` | push/PR workflows that don't cancel superseded runs |
| `missing-cache` | `setup-node`/`-python`/`-java`/`-dotnet`/`-ruby` with caching off |
| `duplicate-triggers` | `push` + `pull_request` with an unfiltered `push` โ double runs |
| `no-path-filter` | heavy triggers with no `paths:` filter (docs-only changes run full CI) |
| `large-matrix` | a build matrix that fans out past your threshold |
| `frequent-schedule` | `schedule:` crons that fire more often than your floor |
| `costly-runner` | macOS (10ร) / Windows (2ร) runner cost multipliers |
| `no-timeout` | jobs with no `timeout-minutes` (360-min default) |
| `full-history-checkout` | `actions/checkout` with `fetch-depth: 0` |
Each finding is a weighted error / warning / info; workflows roll up to a 0โ100
score and an AโF grade you can gate in CI.
## Real scenarios
**1. Gate CI cost in CI.** Add actionbudget to your pipeline so a PR that adds a
`macos-latest` matrix or drops caching fails review:
```yaml
# .github/workflows/budget.yml
- run: npx actionbudget scan --min-score 85 --md budget.md
```
**2. Audit a repo you just inherited.** `npx actionbudget scan` from the root
prints every workflow ranked by score โ see the worst offenders in one screen.
**3. Trim an org's biggest spender.** Point it at a monorepo's `.github/workflows`
and the `large-matrix` + `costly-runner` + `missing-cache` findings show where the
minutes actually go.
## Configuration
`actionbudget init` writes `actionbudget.config.json`:
```jsonc
{
"disable": [], // rule ids to skip, e.g. ["no-path-filter"]
"maxMatrixJobs": 6, // warn when a matrix fans out past this
"requireTimeout": true, // flag jobs without timeout-minutes
"minScheduleMinutes": 15, // flag schedules tighter than this
"minScore": 0 // CI gate threshold
}
```
## Library API
```ts
import { analyzeWorkflow, DEFAULT_CONFIG } from "actionbudget";
const report = analyzeWorkflow("ci.yml", yamlText, DEFAULT_CONFIG);
for (const f of report.findings) console.log(f.severity, f.rule, f.fix);
```
Also exported: `buildReport`, `parseWorkflow`, the `RULES` registry, and types.
## Roadmap
- ๐ค **Optional `--ai` layer (bring-your-own key)** to draft the refactored
workflow YAML for a finding. The core stays 100% offline and deterministic.
- Reusable-workflow (`workflow_call`) and composite-action awareness.
- An estimated minutes/month figure when you supply average job durations.
- More rules: artifact retention, redundant `needs`, always-uploading artifacts.
- A `--fix` mode that applies the safe, mechanical fixes.
## ๐ Sponsor
actionbudget is free and MIT-licensed, built and maintained in spare time. If it
trimmed your CI bill, please consider supporting it:
- โญ **Star this repo** โ the simplest free way to help others find it.
- ๐ **[Sponsor via Lemon Squeezy](https://elab-studio.lemonsqueezy.com/checkout/buy/5d059b89-51d0-456b-b33a-ed56994f7010)** โ one-time or recurring.
## License
[MIT](./LICENSE) ยฉ actionbudget contributors