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 install

It 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@v1

ci 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:

ReachableCompares against
Merge baseThe merge base. What you want
Base branch tip onlyThe tip
NeitherNothing, so it reports every finding in the changed files

The comment says which happened, and silence-missing-baseline-warning hides that notice.

Inputs

InputDefaultDescription
directory.Project directory to scan
scopechangedWhat a pull request reports. See Scope
blockingnoneSeverity that fails the workflow: none, warning, error
min-scoreunsetFail when the project's health score drops below this
configunsetPath to a config file
commenttrueSticky pull request summary comment
review-commentstrueInline review comments on changed lines
commit-statustrueCommit status with the score
sariffalseAlso write a SARIF report
sarif-filenestjs-doctor.sarifWhere the SARIF report is written
silence-missing-baseline-warningfalseHide the degraded-scope warning
github-token${{ github.token }}Token used to comment and set the commit status
node-version22Node.js version
versionlatestnestjs-doctor version to run

Outputs

OutputDescription
scoreHealth score (0-100) for the whole project. Empty when the scan could not produce one
labelExcellent, Good, Fair, Poor, or Critical
total-issuesFindings in the reported scope
fixed-issuesFindings the change resolved. Only meaningful with scope: changed
error-countError-severity findings in the reported scope
warning-countWarning-severity findings in the reported scope
affected-filesFiles carrying at least one reported finding
report-filePath to the raw JSON report
sarif-filePath 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.

FeaturePermission
Sticky comment, inline review commentspull-requests: write
Commit statusstatuses: write
Changed-file API fallbackpull-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.

ScopeReports
fullEvery finding in the project (the CLI default)
filesFindings in files the change touched
linesFindings on the lines the change touched. Schema findings carry an entity rather than a line, so they never appear here
changedFindings 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.