Audit GitHub Actions workflows for waste, cost, and security gaps. CLI and a GitHub Action. Posts a comment on every PR with a table of findings and fix-it suggestions.
Try it without installing: paste a workflow into the in-browser audit. Same rules engine, runs entirely client-side, nothing leaves your tab. Want the dollar number? budget.html prices the same workflow.
$ npx ci-doctor examples/bad-workflow
Found 7 finding(s) [error 1 warn 4 info 2]
.github/workflows/ci.yml
ERROR 7:15 deprecated-action
actions/checkout@v3 is on a deprecated major. Latest stable: v4.
| actions/checkout@v4
WARN 8:15 missing-cache
actions/setup-node has no cache option. Add 'with: cache: <ecosystem>' to skip dep re-downloads. Saves 30-90 seconds per run.
| with:
| cache: npm # or pip, gradle, maven, go, etc.
WARN 2:5 missing-concurrency
No top-level concurrency block. New pushes will not cancel in-flight runs of stale commits, doubling spend on rapid-push branches.
| concurrency:
| group: ${{ github.workflow }}-${{ github.ref }}
| cancel-in-progress: true
WARN 5:5 missing-timeout
Job 'build' has no timeout-minutes. Default is 360 (6h). A hung job can drain your CI budget.
| timeout-minutes: 15 # tune to your job; cap below the default 360.
WARN 1:7 missing-permissions
No top-level permissions block. GITHUB_TOKEN inherits the repo default, often write-all. Set least-privilege explicitly.
| permissions:
| contents: read # add other scopes only as jobs need them.
INFO 2:5 wide-trigger
on: push fires on every branch. Restrict to main or release branches unless you need every-branch runs.
INFO 14:15 artifact-no-retention
upload-artifact has no retention-days. CI artifacts pile up at the repo default (usually 90d). Set 7-14d unless you need long-term retention.
Above is real output from examples/bad-workflow/. Try it: git clone https://github.com/depmedicdev-byte/ci-doctor && cd ci-doctor && npx ci-doctor examples/bad-workflow.
act runs your workflow locally. actionlint checks syntax. Neither tells
you the workflow is burning money. ci-doctor focuses on the cost and security
defaults you actually control.
npm install -g ci-doctor
# or one-shot
npx ci-doctorNode 18+.
name: ci-doctor
on:
pull_request:
permissions:
contents: read
pull-requests: write
jobs:
audit:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- uses: depmedicdev-byte/ci-doctor@v1
with:
fail-on: error
comment: 'true'It runs against every workflow under .github/workflows/, posts a single
sticky comment on the PR, and updates that comment on subsequent runs.
ci-doctor # scan .github/workflows in cwd
ci-doctor path/to/repo # scan another repo
ci-doctor --file ci.yml # one file
ci-doctor --json # machine output for CI
ci-doctor --markdown # PR-comment table
ci-doctor --severity=warn # only warn + error
ci-doctor --only=missing-cache # one rule
ci-doctor --disable=fetch-depth-zero
ci-doctor --rules # list rules
ci-doctor --fix # auto-apply safe fixes in place (new in 0.2)
ci-doctor --fix --dry-run # preview the patched yaml on stdout
ci-doctor --sarif # SARIF 2.1.0 for GitHub Code Scanning (new in 0.3)Exit codes: 0 no error-level findings, 1 one or more errors, 2 internal
error.
ci-doctor --fix rewrites your workflows in place to fix the issues that
have a single safe answer. It uses the document model (preserves comments
and ordering of existing keys) and only adds:
| Rule | What --fix adds |
|---|---|
missing-permissions |
top-level permissions: { contents: read } |
missing-concurrency |
concurrency: { group: ..., cancel-in-progress: true } |
missing-timeout |
timeout-minutes: 15 per job missing it |
artifact-no-retention |
retention-days: 7 on each actions/upload-artifact |
Rules with judgment calls (cache ecosystem, action major-version bumps,
SHA pinning, runner cost) keep their warning so you decide. SHA pinning
specifically is delegated to
pin-actions.
Use --fix --dry-run first if you want to see the diff before writing.
--sarif emits SARIF 2.1.0 you can upload with the official
codeql-action/upload-sarif action. Findings appear as inline PR
annotations and in the repo's Security tab.
name: ci-doctor
on:
pull_request:
permissions:
contents: read
security-events: write
jobs:
audit:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- run: npx ci-doctor --sarif > ci-doctor.sarif || true
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: ci-doctor.sarifSeverity maps error -> error, warn -> warning, info -> note.
| Rule | Severity | Category |
|---|---|---|
deprecated-action |
error | maintenance |
pinned-action-sha |
warn | security |
missing-cache |
warn | cost |
missing-concurrency |
warn | cost |
missing-timeout |
warn | cost |
expensive-runner |
warn | cost |
missing-permissions |
warn | security |
matrix-overcommit |
warn | cost |
stale-cache-key |
warn | cost |
wide-trigger |
info | cost |
artifact-no-retention |
info | cost |
fetch-depth-zero |
info | cost |
fail-fast-true |
info | cost |
always-run-on-pr |
info | cost |
ci-doctor --rules prints them with descriptions.
| Input | Default | What it does |
|---|---|---|
directory |
. |
Repo root. Action looks for .github/workflows under it. |
fail-on |
error |
Threshold to fail the job: error, warn, info, never. |
comment |
true |
Post a single sticky PR comment with the findings. |
only |
(empty) | Comma-separated rule ids to run exclusively. |
disable |
(empty) | Comma-separated rule ids to skip. |
github-token |
${{ github.token }} |
Token used to post the PR comment. |
A paid Pro tier is in development:
- Org-wide policy file: enforce a baseline across every repo.
- Cost projection: estimate $ saved per finding using your runner mix.
- Audit history page on GitHub Pages.
- Private-repo support via license key.
License via Polar: $9/month or $39/year. Free CLI and Action stay free, MIT.
depmedic- surgical npm vulnerability triage from the same author.
Built with AI assistance. Every change reviewed. Open an issue if anything breaks.
MIT.