{"id":51417790,"url":"https://github.com/didrod205/actionbudget","last_synced_at":"2026-07-04T21:30:40.863Z","repository":{"id":368145407,"uuid":"1259039053","full_name":"didrod205/actionbudget","owner":"didrod205","description":"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.","archived":false,"fork":false,"pushed_at":"2026-06-29T07:23:22.000Z","size":51,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-29T09:16:48.498Z","etag":null,"topics":["actionlint","actions","cache","ci","ci-cd","ci-minutes","cli","concurrency","cost-optimization","developer-tools","devops","github-actions","linter","typescript","workflow","yaml"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/didrod205.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"didrod205"}},"created_at":"2026-06-04T06:25:19.000Z","updated_at":"2026-06-29T07:23:25.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/didrod205/actionbudget","commit_stats":null,"previous_names":["didrod205/actionbudget"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/didrod205/actionbudget","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didrod205%2Factionbudget","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didrod205%2Factionbudget/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didrod205%2Factionbudget/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didrod205%2Factionbudget/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/didrod205","download_url":"https://codeload.github.com/didrod205/actionbudget/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didrod205%2Factionbudget/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35136712,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-04T02:00:05.987Z","response_time":113,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["actionlint","actions","cache","ci","ci-cd","ci-minutes","cli","concurrency","cost-optimization","developer-tools","devops","github-actions","linter","typescript","workflow","yaml"],"created_at":"2026-07-04T21:30:39.899Z","updated_at":"2026-07-04T21:30:40.757Z","avatar_url":"https://github.com/didrod205.png","language":"TypeScript","funding_links":["https://github.com/sponsors/didrod205"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# 🧾 actionbudget\n\n### Find what's burning your GitHub Actions minutes — before the bill does.\n\n[![npm version](https://img.shields.io/npm/v/actionbudget.svg?color=success)](https://www.npmjs.com/package/actionbudget)\n[![CI](https://github.com/didrod205/actionbudget/actions/workflows/ci.yml/badge.svg)](https://github.com/didrod205/actionbudget/actions/workflows/ci.yml)\n[![types](https://img.shields.io/npm/types/actionbudget.svg)](https://www.npmjs.com/package/actionbudget)\n[![license](https://img.shields.io/npm/l/actionbudget.svg)](./LICENSE)\n\n\u003c/div\u003e\n\nYour GitHub Actions usage creeps up and you find out from the **billing page** — or\nwhen the free minutes run out mid-sprint. The waste is hiding in plain sight in your\nYAML: a workflow with **no `concurrency`** keeps three superseded runs alive on a\nbusy PR, `setup-node` **reinstalls deps every run** because caching is off, a\n**push + pull_request** pair runs CI *twice* per commit, a **12-job matrix** pays\nfull setup nine extra times, and a job with **no `timeout-minutes`** can bill six\nhours when it hangs.\n\n**actionbudget reads your workflow files and lists exactly what's wasting\nminutes — locally, deterministically, with the one-line fix.** No app to install on\nyour org, no API token, no waiting for the invoice.\n\n```bash\nnpx actionbudget scan\n```\n\n```\n.github/workflows/ci.yml (CI)  64/100 (D)\n  ⚠ No concurrency group — superseded runs keep running   L4   no-concurrency\n  ⚠ setup-node without dependency caching   job:test L20   missing-cache\n  ⚠ push + pull_request run CI twice per PR commit   L4   duplicate-triggers\n  ⚠ Matrix expands to 12 jobs   job:test L10   large-matrix\n  ⚠ Schedule runs every ~5 min   L6   frequent-schedule\n  ℹ macOS runner bills at 10× Linux   job:mac-build L26   costly-runner\n```\n\n---\n\n## Why actionbudget?\n\n- 💸 **It's about cost, not syntax.** `actionlint` checks your YAML is *valid*;\n  `zizmor` checks it's *secure*. Neither tells you it's **wasteful**. actionbudget\n  owns that gap — caching, concurrency, matrices, timeouts, triggers, runners.\n- 🎯 **Every finding has a fix.** Not \"this could be better\" — the exact\n  `concurrency:` block, the `cache: npm` line, the `paths:` filter to add.\n- 🔒 **Local \u0026 deterministic.** Parses the YAML with a real parser (no guessing),\n  runs offline, gives the same result every time. Drop it in CI and the build fails\n  when a PR adds a 9× matrix.\n- 🧮 **Honest about cost.** It reports structural waste and multipliers (matrix\n  size, macOS 10× / Windows 2×) — deterministic facts from your YAML, not a\n  made-up dollar figure.\n\nWhy not just ask an LLM \"is my CI wasteful\"? It can't see all your workflow files at\nonce, can't run on every PR, and \"does push+pull_request double-run here\" depends on\nyour exact branch filters — a structural check, not a vibe.\n\n## Install\n\n```bash\n# run it now, from your repo root\nnpx actionbudget scan\n\n# or add it\nnpm install -g actionbudget      # global CLI\nnpm install -D actionbudget      # CI dependency\n```\n\nNode ≥ 18. With no arguments it scans `.github/workflows`.\n\n## Quick start\n\n```bash\nactionbudget scan                          # scan .github/workflows\nactionbudget scan path/to/ci.yml           # a single file\nactionbudget scan --min-score 80           # CI gate\nactionbudget scan --md budget.md           # Markdown report for a PR comment\nactionbudget scan --disable no-timeout,no-path-filter\nactionbudget init                          # write actionbudget.config.json\n```\n\nSee [`examples/sample-report.md`](./examples/sample-report.md) for a full report and\n[`examples/.github/workflows/lean.yml`](./examples/.github/workflows/lean.yml) for a\nworkflow that scores 100/100.\n\n## What it checks\n\n| Rule | What it catches |\n| ---- | --------------- |\n| `no-concurrency` / `no-cancel-in-progress` | push/PR workflows that don't cancel superseded runs |\n| `missing-cache` | `setup-node`/`-python`/`-java`/`-dotnet`/`-ruby` with caching off |\n| `duplicate-triggers` | `push` + `pull_request` with an unfiltered `push` → double runs |\n| `no-path-filter` | heavy triggers with no `paths:` filter (docs-only changes run full CI) |\n| `large-matrix` | a build matrix that fans out past your threshold |\n| `frequent-schedule` | `schedule:` crons that fire more often than your floor |\n| `costly-runner` | macOS (10×) / Windows (2×) runner cost multipliers |\n| `no-timeout` | jobs with no `timeout-minutes` (360-min default) |\n| `full-history-checkout` | `actions/checkout` with `fetch-depth: 0` |\n\nEach finding is a weighted error / warning / info; workflows roll up to a 0–100\nscore and an A–F grade you can gate in CI.\n\n## Real scenarios\n\n**1. Gate CI cost in CI.** Add actionbudget to your pipeline so a PR that adds a\n`macos-latest` matrix or drops caching fails review:\n\n```yaml\n# .github/workflows/budget.yml\n- run: npx actionbudget scan --min-score 85 --md budget.md\n```\n\n**2. Audit a repo you just inherited.** `npx actionbudget scan` from the root\nprints every workflow ranked by score — see the worst offenders in one screen.\n\n**3. Trim an org's biggest spender.** Point it at a monorepo's `.github/workflows`\nand the `large-matrix` + `costly-runner` + `missing-cache` findings show where the\nminutes actually go.\n\n## Configuration\n\n`actionbudget init` writes `actionbudget.config.json`:\n\n```jsonc\n{\n  \"disable\": [],              // rule ids to skip, e.g. [\"no-path-filter\"]\n  \"maxMatrixJobs\": 6,         // warn when a matrix fans out past this\n  \"requireTimeout\": true,     // flag jobs without timeout-minutes\n  \"minScheduleMinutes\": 15,   // flag schedules tighter than this\n  \"minScore\": 0               // CI gate threshold\n}\n```\n\n## Library API\n\n```ts\nimport { analyzeWorkflow, DEFAULT_CONFIG } from \"actionbudget\";\n\nconst report = analyzeWorkflow(\"ci.yml\", yamlText, DEFAULT_CONFIG);\nfor (const f of report.findings) console.log(f.severity, f.rule, f.fix);\n```\n\nAlso exported: `buildReport`, `parseWorkflow`, the `RULES` registry, and types.\n\n## Roadmap\n\n- 🤖 **Optional `--ai` layer (bring-your-own key)** to draft the refactored\n  workflow YAML for a finding. The core stays 100% offline and deterministic.\n- Reusable-workflow (`workflow_call`) and composite-action awareness.\n- An estimated minutes/month figure when you supply average job durations.\n- More rules: artifact retention, redundant `needs`, always-uploading artifacts.\n- A `--fix` mode that applies the safe, mechanical fixes.\n\n## 💖 Sponsor\n\nactionbudget is free and MIT-licensed, built and maintained in spare time. If it\ntrimmed your CI bill, please consider supporting it:\n\n- ⭐ **Star this repo** — the simplest free way to help others find it.\n- 🍋 **[Sponsor via Lemon Squeezy](https://elab-studio.lemonsqueezy.com/checkout/buy/5d059b89-51d0-456b-b33a-ed56994f7010)** — one-time or recurring.\n\n## License\n\n[MIT](./LICENSE) © actionbudget contributors\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdidrod205%2Factionbudget","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdidrod205%2Factionbudget","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdidrod205%2Factionbudget/lists"}