Skip to content

test: validate changed-line coverage gate#23

Merged
bladehan1 merged 1 commit intobladehan1:developfrom
hzj-edu-nju:test/changed-line-coverage-gate
Apr 23, 2026
Merged

test: validate changed-line coverage gate#23
bladehan1 merged 1 commit intobladehan1:developfrom
hzj-edu-nju:test/changed-line-coverage-gate

Conversation

@hzj-edu-nju
Copy link
Copy Markdown

@hzj-edu-nju hzj-edu-nju commented Apr 23, 2026

Test PR for validating that PR Build uses diff-cover changed-line coverage as the changed-code gate.\n\nExpected workflow signals:\n- Changed-line coverage (diff-cover) step runs\n- diff-cover JSON output is parsed into changed_line_coverage\n- Coverage Gate summary shows Changed-line Gate\n- old changed-files gate is not used

Summary by CodeRabbit

  • Chores
    • Enhanced pull request coverage reporting with improved line-by-line analysis and detailed coverage summaries displayed in PR pages.
    • Strengthened coverage validation by implementing more accurate coverage measurements with stricter enforcement and improved failure detection.

Summary by cubic

Switch PR Build to use diff-cover changed-line coverage as the changed-code gate. Keeps the overall coverage delta gate (-0.1%) and posts JaCoCo reports as summary-only.

  • Refactors

    • Run diff-cover against PR JaCoCo XMLs and compare to the base branch; add a "Changed-line Coverage" section to the step summary.
    • Parse JSON output to expose changed_line_coverage for gating and logs.
    • Enforce the new "Changed-line Gate" (>60%) and keep the "Overall Delta Gate"; stop using the old changed-files gate from madrapps/jacoco-report.
  • Dependencies

    • Add diff-cover@9.2.0 (via actions/setup-python@v5 with Python 3.11).

Written for commit 28f1819. Summary will update on new commits.

Use diff-cover changed-line coverage as the changed-code gate, keep the overall coverage delta gate at -0.1%, and keep Jacoco reports as summary-only snapshots.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2026

📝 Walkthrough

Walkthrough

The pull request enhances the PR coverage validation by introducing a diff-cover-based changed-line coverage metric. The workflow now fetches the base branch, runs diff-cover against Jacoco XML reports, extracts the coverage percentage, and enforces the new CHANGED_LINE metric in the coverage gate with proper sanitization and validation.

Changes

Cohort / File(s) Summary
Coverage Gate Enhancement
.github/workflows/pr-build.yml
Adds diff-cover job to compute changed-line coverage from PR Jacoco reports, updates coverage gate to enforce CHANGED_LINE metric with sanitization and numeric validation, sets comment-type to summary for jacoco-report aggregation steps, and replaces prior jacoco-pr changed-files metric.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 Hop, skip, and bounds of code so grand,
We now measure changes line by line so bright,
Diff-cover hops through branches with care,
Each coverage metric caught with rabbit's delight,
Coverage gates now stronger, our PR-s more fair! 🚀

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'test: validate changed-line coverage gate' directly and clearly describes the main change: introducing validation for the new changed-line coverage gate mechanism using diff-cover, which aligns with the PR's core objective.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
.github/workflows/pr-build.yml (2)

376-409: Redundant guards around CHANGED_LINE validation.

is_number's regex already rejects empty strings and NaN, so the [ -z … ] and = "NaN" tests on line 399 are redundant. Collapsing to a single is_number check keeps the validation consistent with the base/pr overall checks on line 390.

♻️ Minor simplification
-          if [ -z "$CHANGED_LINE" ] || [ "$CHANGED_LINE" = "NaN" ] || ! is_number "$CHANGED_LINE"; then
+          if ! is_number "$CHANGED_LINE"; then
             echo "Failed to parse changed-line coverage: changed-line='${CHANGED_LINE}'."
             exit 1
           fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/pr-build.yml around lines 376 - 409, The CHANGED_LINE
validation contains redundant checks ([ -z "$CHANGED_LINE" ] and = "NaN")
because is_number() already rejects empty strings and NaN; update the validation
to mirror the BASE_OVERALL/PR_OVERALL block by removing those extra guards and
only calling is_number "$CHANGED_LINE" (and keep the same error message/exit
behavior), so change the block that sets CHANGED_LINE_OK to first ensure
is_number returns true for CHANGED_LINE and then call compare_float and set
CHANGED_LINE_STATUS accordingly.

287-315: Recommended cleanup: reuse prior outputs and quote expansions.

Small hygiene improvements that reduce duplication and make the step robust to unusual path characters:

  • PR_XMLS is already computed by collect-xml — reuse steps.collect-xml.outputs.pr_xmls (comma-separated) rather than re-running find. If you keep find, switch to -print0 / xargs -0 or mapfile to handle whitespace in paths safely.
  • SRC_ROOTS is similarly expanded unquoted; safer to build an array.
  • The explicit git fetch is fine as defense-in-depth even with fetch-depth: 0, but a brief comment noting that would help future readers.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/pr-build.yml around lines 287 - 315, Replace the ad-hoc
re-find of PR XMLs and unquoted path expansions with the previously computed
step output and safe array handling: use the workflow output
steps.collect-xml.outputs.pr_xmls (split on commas into a shell array) instead
of re-running find for PR_XMLS, build SRC_ROOTS as a quoted array (avoid
word-splitting) or use find -print0 / mapfile to populate it safely, and pass
those quoted/array values into the diff-cover invocation to handle whitespace in
paths; keep the existing git fetch but add a short comment noting it complements
fetch-depth: 0 for robustness.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/pr-build.yml:
- Around line 298-302: The branch that checks PR_XMLS and exits 0 is misleading
or unreachable because the earlier collect-xml step already exits 1 when no
reports exist; remove this block or change it to set a sentinel output (e.g.,
set the changed_line_coverage output to "N/A" or "skipped") instead of exiting,
so the enforcement step can detect the sentinel and honestly skip parsing;
update the PR_XMLS check in the workflow to either be deleted or to assign
changed_line_coverage="N/A" (and document that enforcement should treat that
sentinel as skip) — refer to the PR_XMLS variable, the collect-xml step
behavior, and the changed_line_coverage/enforcement parsing logic to ensure
consistency.
- Around line 298-321: The diff-cover run can produce a report with no changed
lines (total_num_lines == 0) which makes jq return empty for
.total_percent_covered and causes an erroneous exit; update the workflow to
parse both .total_num_lines and .total_percent_covered from diff-cover.json (use
CHANGED_LINE_COUNT=$(jq -r '.total_num_lines // 0' diff-cover.json) and
CHANGED_LINE_COVERAGE=$(jq -r '.total_percent_covered // empty'
diff-cover.json)), and if CHANGED_LINE_COUNT is 0 set CHANGED_LINE_COVERAGE="NA"
(or another sentinel) instead of exiting; also update the gate-enforcement logic
that consumes CHANGED_LINE_COVERAGE to treat "NA" as a pass rather than a
failure so workflow-only/non-Java PRs do not fail the gate.

---

Nitpick comments:
In @.github/workflows/pr-build.yml:
- Around line 376-409: The CHANGED_LINE validation contains redundant checks ([
-z "$CHANGED_LINE" ] and = "NaN") because is_number() already rejects empty
strings and NaN; update the validation to mirror the BASE_OVERALL/PR_OVERALL
block by removing those extra guards and only calling is_number "$CHANGED_LINE"
(and keep the same error message/exit behavior), so change the block that sets
CHANGED_LINE_OK to first ensure is_number returns true for CHANGED_LINE and then
call compare_float and set CHANGED_LINE_STATUS accordingly.
- Around line 287-315: Replace the ad-hoc re-find of PR XMLs and unquoted path
expansions with the previously computed step output and safe array handling: use
the workflow output steps.collect-xml.outputs.pr_xmls (split on commas into a
shell array) instead of re-running find for PR_XMLS, build SRC_ROOTS as a quoted
array (avoid word-splitting) or use find -print0 / mapfile to populate it
safely, and pass those quoted/array values into the diff-cover invocation to
handle whitespace in paths; keep the existing git fetch but add a short comment
noting it complements fetch-depth: 0 for robustness.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3839bedf-7612-47f8-81f6-43ed4715e552

📥 Commits

Reviewing files that changed from the base of the PR and between 2de63bb and 28f1819.

📒 Files selected for processing (1)
  • .github/workflows/pr-build.yml

Comment on lines +298 to +302
PR_XMLS=$(find coverage/pr -name "jacocoTestReport.xml" | sort)
if [ -z "$PR_XMLS" ]; then
echo "No PR jacoco XML reports found, skipping diff-cover."
exit 0
fi
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Misleading "skip" branch — either dead or causes a later hard failure.

The earlier collect-xml step already exit 1s when PR XMLs are missing (lines 275-278), so this branch is unreachable on the normal path. Worse, if it ever did execute, exit 0 bypasses setting changed_line_coverage, and the enforcement step will then fail at lines 399-402 with a confusing "Failed to parse changed-line coverage" message rather than the "skipping diff-cover" intent suggested here.

Consider removing the block, or making it honestly skip by setting a sentinel output that the enforcement step treats as N/A.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/pr-build.yml around lines 298 - 302, The branch that
checks PR_XMLS and exits 0 is misleading or unreachable because the earlier
collect-xml step already exits 1 when no reports exist; remove this block or
change it to set a sentinel output (e.g., set the changed_line_coverage output
to "N/A" or "skipped") instead of exiting, so the enforcement step can detect
the sentinel and honestly skip parsing; update the PR_XMLS check in the workflow
to either be deleted or to assign changed_line_coverage="N/A" (and document that
enforcement should treat that sentinel as skip) — refer to the PR_XMLS variable,
the collect-xml step behavior, and the changed_line_coverage/enforcement parsing
logic to ensure consistency.

Comment on lines +298 to +321
PR_XMLS=$(find coverage/pr -name "jacocoTestReport.xml" | sort)
if [ -z "$PR_XMLS" ]; then
echo "No PR jacoco XML reports found, skipping diff-cover."
exit 0
fi

SRC_ROOTS=$(find . -type d -path '*/src/main/java' \
-not -path './coverage/*' -not -path './.git/*' | sort)

set +e
diff-cover $PR_XMLS \
--compare-branch="origin/${BASE_REF}" \
--src-roots $SRC_ROOTS \
--fail-under=0 \
--json-report=diff-cover.json \
--markdown-report=diff-cover.md
DIFF_RC=$?
set -e

CHANGED_LINE_COVERAGE=$(jq -r '.total_percent_covered // empty' diff-cover.json)
if [ -z "$CHANGED_LINE_COVERAGE" ]; then
echo "Unable to parse changed-line coverage from diff-cover.json."
exit 1
fi
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

diff-cover 9.2.0 json report total_percent_covered total_num_lines when no changed lines in src roots

💡 Result:

When there are no changed lines in the source roots specified by --src-roots in diff-cover 9.2.0 (released 2024-09-08), the tool detects no relevant diff lines within those roots. The JSON report will have "total_num_lines": 0 and "total_percent_covered": null (or omitted, as percentage is undefined for zero lines). The "src_stats" object will be empty {}, "total_num_violations": 0, with metadata like "report_name": "XML" and "diff_name" describing the comparison (e.g., "origin/master...HEAD, staged and unstaged changes"). This follows from diff-cover's design to report only on changed lines in the git diff that match coverage data via src-roots for JaCoCo XML reports. If no diff lines match, totals reflect zero lines analyzed. Console output would show "No lines with coverage information in this diff" or similar, but JSON captures the empty stats.

Citations:


Gate fails for PRs with no changed Java source lines.

When a PR touches only non-Java files (or no src/main/java source at all, like a workflow-only change), diff-cover finds no covered/uncovered lines in the configured source roots. The resulting JSON report has total_percent_covered: null (or omitted) and total_num_lines: 0.

Current code (line 320) treats this as a parsing failure:

CHANGED_LINE_COVERAGE=$(jq -r '.total_percent_covered // empty' diff-cover.json)
if [ -z "$CHANGED_LINE_COVERAGE" ]; then
  echo "Unable to parse changed-line coverage from diff-cover.json."
  exit 1
fi

Since total_percent_covered is null, jq yields empty, causing exit 1. This blocks legitimate PRs that don't modify production Java code (e.g., this PR itself, which only edits the workflow file).

Fix: Detect the "no changed lines" case and short-circuit to pass the changed-line gate:

Suggested change
-          CHANGED_LINE_COVERAGE=$(jq -r '.total_percent_covered // empty' diff-cover.json)
-          if [ -z "$CHANGED_LINE_COVERAGE" ]; then
-            echo "Unable to parse changed-line coverage from diff-cover.json."
-            exit 1
-          fi
-          echo "changed_line_coverage=${CHANGED_LINE_COVERAGE}" >> "$GITHUB_OUTPUT"
+          TOTAL_NUM_LINES=$(jq -r '.total_num_lines // 0' diff-cover.json)
+          if [ "${TOTAL_NUM_LINES}" = "0" ]; then
+            echo "No changed Java source lines detected; skipping changed-line gate."
+            echo "changed_line_coverage=NA" >> "$GITHUB_OUTPUT"
+          else
+            CHANGED_LINE_COVERAGE=$(jq -r '.total_percent_covered // empty' diff-cover.json)
+            if [ -z "$CHANGED_LINE_COVERAGE" ]; then
+              echo "Unable to parse changed-line coverage from diff-cover.json."
+              exit 1
+            fi
+            echo "changed_line_coverage=${CHANGED_LINE_COVERAGE}" >> "$GITHUB_OUTPUT"
+          fi

The gate-enforcement step (line 404) would also need to treat NA as a pass rather than fail.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/pr-build.yml around lines 298 - 321, The diff-cover run
can produce a report with no changed lines (total_num_lines == 0) which makes jq
return empty for .total_percent_covered and causes an erroneous exit; update the
workflow to parse both .total_num_lines and .total_percent_covered from
diff-cover.json (use CHANGED_LINE_COUNT=$(jq -r '.total_num_lines // 0'
diff-cover.json) and CHANGED_LINE_COVERAGE=$(jq -r '.total_percent_covered //
empty' diff-cover.json)), and if CHANGED_LINE_COUNT is 0 set
CHANGED_LINE_COVERAGE="NA" (or another sentinel) instead of exiting; also update
the gate-enforcement logic that consumes CHANGED_LINE_COVERAGE to treat "NA" as
a pass rather than a failure so workflow-only/non-Java PRs do not fail the gate.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".github/workflows/pr-build.yml">

<violation number="1" location=".github/workflows/pr-build.yml:301">
P2: The `exit 0` here skips setting the `changed_line_coverage` output. If this branch ever executes, the downstream enforcement step will see an empty `CHANGED_LINE_RAW` and fail with "Failed to parse changed-line coverage" — a confusing error that masks the real cause. Either remove this unreachable block or set a sentinel output (e.g., `changed_line_coverage=NA`) and handle it in the enforcement step.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

PR_XMLS=$(find coverage/pr -name "jacocoTestReport.xml" | sort)
if [ -z "$PR_XMLS" ]; then
echo "No PR jacoco XML reports found, skipping diff-cover."
exit 0
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The exit 0 here skips setting the changed_line_coverage output. If this branch ever executes, the downstream enforcement step will see an empty CHANGED_LINE_RAW and fail with "Failed to parse changed-line coverage" — a confusing error that masks the real cause. Either remove this unreachable block or set a sentinel output (e.g., changed_line_coverage=NA) and handle it in the enforcement step.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/pr-build.yml, line 301:

<comment>The `exit 0` here skips setting the `changed_line_coverage` output. If this branch ever executes, the downstream enforcement step will see an empty `CHANGED_LINE_RAW` and fail with "Failed to parse changed-line coverage" — a confusing error that masks the real cause. Either remove this unreachable block or set a sentinel output (e.g., `changed_line_coverage=NA`) and handle it in the enforcement step.</comment>

<file context>
@@ -279,6 +279,60 @@ jobs:
+          PR_XMLS=$(find coverage/pr -name "jacocoTestReport.xml" | sort)
+          if [ -z "$PR_XMLS" ]; then
+            echo "No PR jacoco XML reports found, skipping diff-cover."
+            exit 0
+          fi
+
</file context>
Fix with Cubic

@bladehan1 bladehan1 merged commit cbfc520 into bladehan1:develop Apr 23, 2026
27 of 29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants