GitHub Actions setup
The action reports only what a pull request introduced, so an existing backlog stays out of the review.
Scaffolding the workflow
Run this from anywhere inside the repository:
npx nestjs-doctor@latest ci installIt writes .github/workflows/nestjs-doctor.yml at the git repository root, so
running it from apps/api in a monorepo still lands the file in the right
place. The push trigger uses the branch origin/HEAD points at, verified to
still exist, then origin/main, origin/master, and finally the branch you
have checked out. A --single-branch clone or a remote added by hand has no
origin/HEAD. The command prints its pick.
It refuses in three cases: outside a git repository, an existing workflow
(replace it with --force), and a symlink anywhere on the path. The refusals
keep the write from escaping the repository.
A minimal .github/workflows/nestjs-doctor.yml:
name: nestjs-doctor
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
pull-requests: write
statuses: write
jobs:
doctor:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # required for `scope: changed`
- uses: RoloBits/nestjs-doctor@v1ci install writes a fuller version of that file: ready_for_review among the
pull request types, a concurrency guard that cancels superseded runs, and the
common inputs commented out.
The check never fails until you uncomment blocking or min-score.
What a pull request gets
The action reports in three places. A sticky summary comment is rewritten in place on every push, and inline review comments land on the changed lines. A commit status carries the score, and the job summary mirrors the full report.
fetch-depth: 0 matters for the default scope. The action compares against the
merge base of your branch and its target, which a shallow checkout cannot
resolve. It then degrades in steps:
| Reachable | Compares against |
|---|---|
| Merge base | The merge base. What you want |
| Base branch tip only | The tip |
| Neither | Nothing, so it reports every finding in the changed files |
The comment says which happened, and silence-missing-baseline-warning hides
that notice.
Inputs
| Input | Default | Description |
|---|---|---|
directory | . | Project directory to scan |
scope | changed | What a pull request reports. See Scope |
blocking | none | Severity that fails the workflow: none, warning, error |
min-score | unset | Fail when the project's health score drops below this |
config | unset | Path to a config file |
comment | true | Sticky pull request summary comment |
review-comments | true | Inline review comments on changed lines |
commit-status | true | Commit status with the score |
sarif | false | Also write a SARIF report |
sarif-file | nestjs-doctor.sarif | Where the SARIF report is written |
silence-missing-baseline-warning | false | Hide the degraded-scope warning |
github-token | ${{ github.token }} | Token used to comment and set the commit status |
node-version | 22 | Node.js version |
version | latest | nestjs-doctor version to run |
Outputs
| Output | Description |
|---|---|
score | Health score (0-100) for the whole project. Empty when the scan could not produce one |
label | Excellent, Good, Fair, Poor, or Critical |
total-issues | Findings in the reported scope |
fixed-issues | Findings the change resolved. Only meaningful with scope: changed |
error-count | Error-severity findings in the reported scope |
warning-count | Warning-severity findings in the reported scope |
affected-files | Files carrying at least one reported finding |
report-file | Path to the raw JSON report |
sarif-file | Path to the SARIF report, when sarif is enabled |
Give the step an id and read one through it, as ${{ steps.doctor.outputs.score }}.
Permissions
The action degrades rather than failing when a permission is missing, and logs a warning naming the one it needs.
| Feature | Permission |
|---|---|
| Sticky comment, inline review comments | pull-requests: write |
| Commit status | statuses: write |
| Changed-file API fallback | pull-requests: read |
SARIF upload (via github/codeql-action/upload-sarif) | security-events: write |
Runs outside a pull request
Only a pull_request event is gated. Every other event forces scope: full and
exits 0, whatever blocking says. That covers push on any branch,
workflow_dispatch, and schedule alike.
Such a run is a health snapshot, surfaced through the job summary and the commit
status. main does not go red on findings that were already there.
Scope
The whole project is always analyzed, so --scope narrows only what gets
reported. See Scope for the CLI flags.
| Scope | Reports |
|---|---|
full | Every finding in the project (the CLI default) |
files | Findings in files the change touched |
lines | Findings on the lines the change touched. Schema findings carry an entity rather than a line, so they never appear here |
changed | Findings the change introduced, measured against the base (the action's default) |
changed scans the base revision with the same rules and config, then subtracts
what was already there. Findings match on rule, file, message, and source text,
never on line number. An edit higher up a file does not make an old finding look
new.