Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ inputs:
comment:
description: "Create or update a sticky pull request summary comment. Automatically skipped when the pull request changes no React-eligible files (`.tsx`/`.jsx` or framework entry files)."
default: "true"
comment-on-clean:
description: "When comment is enabled, whether to post a comment when the scan finds no issues. Set to false to only comment when there are issues to fix, reducing PR noise. Existing comments are still updated."
default: "true"
review-comments:
description: "Post inline review comments on the changed lines that triggered diagnostics (pull requests only)."
default: "true"
Expand Down Expand Up @@ -386,6 +389,8 @@ runs:
env:
REACT_DOCTOR_COMMENT_FILE: ${{ steps.render.outputs.comment-file }}
REACT_DOCTOR_SKIPPED: ${{ steps.render.outputs.skipped }}
REACT_DOCTOR_CLEAN: ${{ steps.render.outputs.clean }}
REACT_DOCTOR_COMMENT_ON_CLEAN: ${{ inputs.comment-on-clean }}
with:
script: |
const fs = require("fs");
Expand All @@ -401,6 +406,12 @@ runs:
// changes were reverted out of the PR.
const skipped = process.env.REACT_DOCTOR_SKIPPED === "true";

// A clean scan (no issues found) can optionally skip creating new
// comments via the `comment-on-clean` input, reducing PR noise. Existing
// comments are still updated so stale findings don't linger.
const clean = process.env.REACT_DOCTOR_CLEAN === "true";
const commentOnClean = process.env.REACT_DOCTOR_COMMENT_ON_CLEAN !== "false";

const marker = "<!-- react-doctor:summary -->";
try {
const { data: comments } = await github.rest.issues.listComments({
Expand All @@ -410,6 +421,7 @@ runs:
});
const previous = comments.find((comment) => comment.body?.startsWith(marker));
if (skipped && !previous) return;
if (clean && !commentOnClean && !previous) return;
if (previous) {
await github.rest.issues.updateComment({
...context.repo,
Expand Down
10 changes: 10 additions & 0 deletions packages/react-doctor/tests/github-action-comment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ describe("render-github-action-comment", () => {
expect(comment).not.toContain("No React Doctor issues found");
expect(comment).not.toContain("found no issues");
expect(outputs).toContain("skipped=true");
expect(outputs).toContain("clean=false");
});

it("marks a diff scan whose changed files examined zero eligible files as skipped", () => {
Expand All @@ -378,6 +379,7 @@ describe("render-github-action-comment", () => {

expect(comment).toContain("React Doctor skipped this pull request");
expect(outputs).toContain("skipped=true");
expect(outputs).toContain("clean=false");
});

it("does NOT skip a clean scan of real React changes (eligible files examined)", () => {
Expand All @@ -401,6 +403,14 @@ describe("render-github-action-comment", () => {
expect(comment).toContain("**React Doctor** found no issues. 馃帀");
expect(comment).not.toContain("skipped this pull request");
expect(outputs).toContain("skipped=false");
expect(outputs).toContain("clean=true");
});

it("marks a scan with issues as not clean", () => {
const { outputs } = runRenderer(buildReport());

expect(outputs).toContain("skipped=false");
expect(outputs).toContain("clean=false");
});

it("does NOT skip a full-scope scan with no projects (clean success, not a no-op)", () => {
Expand Down
10 changes: 10 additions & 0 deletions scripts/render-github-action-comment.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,14 @@ const isSkippedScan = (report) => {
return (report.projects ?? []).every((project) => project.scannedFileCount === 0);
};

const isCleanScan = (report) => {
if (!report.ok) return false;
if (isSkippedScan(report)) return false;
if ((report.summary?.totalDiagnosticCount ?? 0) > 0) return false;
if (hasIncompleteChecks(report)) return false;
return true;
};

// Unified body for every successful scan (baseline, diff, full). The lead line
// adapts to the mode; the error/warning lists are shared.
const buildIssuesBody = (report) => {
Expand Down Expand Up @@ -434,6 +442,7 @@ if (!report) {
process.exit(0);
}
const skipped = isSkippedScan(report);
const clean = isCleanScan(report);
const body = skipped ? buildSingleLineBody(COPY.skipped) : buildCommentBody(report);

if (commentPath) {
Expand All @@ -445,6 +454,7 @@ if (commentPath) {
// The Action reads this to suppress the sticky PR comment (it still mirrors the
// body into the job summary + commit status, which read "skipped").
appendOutput("skipped", skipped ? "true" : "false");
appendOutput("clean", clean ? "true" : "false");

appendOutput(
"score",
Expand Down
Loading