Skip to content

Resolve patch coverage from Codecov first and name the source of every verdict - #4397

Merged
DonOmalVindula merged 1 commit into
thunder-id:mainfrom
DonOmalVindula:ci/gate-parity
Jul 30, 2026
Merged

Resolve patch coverage from Codecov first and name the source of every verdict#4397
DonOmalVindula merged 1 commit into
thunder-id:mainfrom
DonOmalVindula:ci/gate-parity

Conversation

@DonOmalVindula

@DonOmalVindula DonOmalVindula commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Purpose

On #4404 the gate failed at 76.74% while codecov/patch passed with "Coverage not affected". Two verdicts for one diff is not acceptable, and the cause was that the gate read Codecov's compare API while codecov/patch reports from a check run. Those are different measurements:

PR codecov/patch check run compare API (what the gate read)
4404 success, "Coverage not affected" 76.74% → gate failed
4003 failure, 67.32% 87.75% → gate passed
4371 success, 91.70% 93.58%
4318 success, 94.11% 97.05%

Refs #4268.

Approach

Patch coverage is now resolved from the most authoritative source available, in order, and every message names the source it came from.

Source 1: the codecov/patch check run. mirror-codecov-check.sh polls the check run for the commit and returns Codecov's own conclusion, echoing its summary line ("91.70% of diff hit (target 80.00%)"). When Codecov reports, the gate is identical to Codecov by construction, because it is Codecov's verdict.

A verdict is final. A failure here fails the gate rather than continuing to a more lenient source, otherwise a pull request Codecov rejected could pass on a fallback. Only the absence of a verdict after ten 30-second polls, or a cancelled or stale check, continues to source 2.

Source 2: the Codecov compare API. check-codecov-api.sh waits for a complete report (six sessions, matching notify.after_n_builds) and enforces its patch total. It is still Codecov's data, so it is preferred over computing locally, but it is a different measurement from the check run, so it is only consulted when there is no verdict to mirror. When its comparison base is not this branch's merge base, the range difference is reported in the summary rather than hidden.

Source 3: local computation. compute-local-patch-coverage.sh runs diff-cover over the archived coverage artifacts, pinned to this diff's base. Deterministic and always correctly scoped, but not identical to Codecov, so it is the last resort. It exists because Codecov does not always report at all: fork pull requests, merge queue commits and processing stalls leave the check pending indefinitely, which is why this gate was written.

Before any source runs, list-coverable-changes.sh checks whether the diff can affect coverage at all. If no file that reports coverage changed, the gate passes immediately without polling Codecov, and says so.

Rules come from codecov.yml. The threshold was hardcoded as fail-under: "79" in the workflow and the ignored paths were hardcoded again in the gate script. read-codecov-config.sh now derives FAIL_UNDER from coverage.status.patch.default.target minus .threshold, which is when Codecov passes a patch status, and feeds ignore to diff-cover --exclude. Globs travel via a file so they are never word split or filename expanded. It prefers yq and falls back to PyYAML, mirroring how build.sh reads YAML, and fails loudly on a non-numeric target such as auto.

Merge queue runs read the verdict from the head of the pull request the queue entry came from, taken out of the queue ref, because the queue commit is not what Codecov reported on. That is the premise codecov-merge-queue-status already relies on.

How #4404 behaves now

Its codecov/patch check run is success with "Coverage not affected when comparing 54a4882...045dbe1". Source 1 mirrors that and the gate passes, matching Codecov instead of contradicting it.

Worth being explicit about the consequence: parity means inheriting Codecov's leniency. #4404 adds 578 lines and Codecov considers coverage unaffected, so the gate now passes it where the old compare-API number would have failed it. If untested new code should be blocked, that is a stricter policy than parity with codecov/patch and needs a separate decision.

Verification

The source chain was simulated against the composite's real if expressions: no coverable files runs nothing; a source 1 verdict runs source 1 only; a silent source 1 continues to source 2; a silent source 2 downloads artifacts and computes locally. A failing verdict from source 1 or 2 exits non-zero and stops the composite.

The query source 1 issues was run against #4404's head commit and returns success with Codecov's title, so the gate would pass that pull request. Against the real codecov.yml the reader derives FAIL_UNDER=79 (80 − 1) and the same six globs that were hardcoded, so the fallback's rules are unchanged; target: auto and a missing file exit 1 with a clear message, and a missing threshold defaults to 0. The chain was exercised against diff-cover==10.4.0 in a scratch repository: the globs arrive as one --exclude flag with the patterns intact, the derived threshold is enforced, and a change under docs/ is ignored.

On the accuracy of source 3: replaying PR 4371's six real coverage artifacts through the gate's own conversion and diff-cover invocation, against that pull request's actual merge base, gives 93.08% (242 of 260 lines) where codecov/patch reports 91.70%. The uncovered count matches Codecov exactly (35 lines = 25 misses + 10 partials) when scoped to Codecov's own base, so the coverage math agrees; the residual gap is in which lines reach the denominator, most likely partial-line handling. That gap is the reason source 3 is last, and the reason the summary always names the source.

Related Issues

Checklist

  • Followed the contribution guidelines.
  • Manual test round performed and verified.
  • Documentation provided. (Add links if there are any)
    • Ran Vale and fixed all errors and warnings
  • Tests provided. (Add links if there are any)
    • Unit Tests
    • Integration Tests
  • Breaking changes. (Fill if applicable)
    • Breaking changes section filled.
    • breaking change label added.

Security checks

  • Followed secure coding standards in WSO2 Secure Coding Guidelines
  • Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets.

@DonOmalVindula DonOmalVindula added Type/Improvement trigger-pr-builder Add when the PR is ready for CI; starts the PR Builder for this and all later pushes labels Jul 28, 2026
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@DonOmalVindula, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 42 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: eff67a04-5072-48d3-ba88-7e4ab0cf7e28

📥 Commits

Reviewing files that changed from the base of the PR and between ef980e6 and a581bfc.

📒 Files selected for processing (7)
  • .github/actions/patch-coverage-gate/action.yml
  • .github/actions/patch-coverage-gate/check-codecov-api.sh
  • .github/actions/patch-coverage-gate/compute-local-patch-coverage.sh
  • .github/actions/patch-coverage-gate/list-coverable-changes.sh
  • .github/actions/patch-coverage-gate/mirror-codecov-check.sh
  • .github/actions/patch-coverage-gate/read-codecov-config.sh
  • .github/workflows/pr-builder.yml
📝 Walkthrough

Walkthrough

The patch coverage gate now reads thresholds and ignore paths from codecov.yml, resolves pull request head SHAs, skips non-coverable diffs, mirrors completed Codecov check results, and falls back to local coverage computation using configured exclusions.

Changes

Codecov patch coverage gate

Layer / File(s) Summary
Workflow pull request resolution
.github/workflows/pr-builder.yml
The patch coverage job gains read permissions, resolves head SHAs for pull request and merge-group events, and passes the SHA and GitHub token to the action.
Codecov configuration loading
.github/actions/patch-coverage-gate/action.yml, .github/actions/patch-coverage-gate/read-codecov-config.sh
The action accepts a configurable Codecov file, validates patch target and threshold values, computes FAIL_UNDER, and exports configured ignore globs.
Coverable diff and gate enforcement
.github/actions/patch-coverage-gate/list-coverable-changes.sh, .github/actions/patch-coverage-gate/mirror-codecov-check.sh, .github/actions/patch-coverage-gate/compute-local-patch-coverage.sh
Coverability controls Codecov and local checks; completed Codecov verdicts are mirrored, while unresolved checks fall back to local diff-cover with configured exclusions.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Workflow
  participant CodecovTarget
  participant GitHubAPI
  participant GateAction
  participant ConfigReader
  participant CoverableCheck
  participant CodecovCheck
  participant LocalCoverage

  Workflow->>CodecovTarget: Resolve applicable head SHA
  CodecovTarget->>GitHubAPI: Fetch merge-group pull request SHA
  GitHubAPI-->>CodecovTarget: Return head SHA
  CodecovTarget->>GateAction: Pass SHA and token
  GateAction->>ConfigReader: Read Codecov configuration
  ConfigReader-->>GateAction: Export FAIL_UNDER and ignore globs
  GateAction->>CoverableCheck: List coverable changes
  CoverableCheck-->>GateAction: Return coverable status
  GateAction->>CodecovCheck: Poll codecov/patch check
  CodecovCheck-->>GateAction: Return verdict or unresolved state
  GateAction->>LocalCoverage: Compute local coverage if unresolved
Loading

Possibly related PRs

Suggested reviewers: brionmario

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
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.
Title check ✅ Passed Title clearly reflects the main change: preferring Codecov check runs and naming verdict sources.
Description check ✅ Passed Description follows the template with Purpose, Approach, Related Issues, Checklist, and Security checks.
✨ 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/actions/patch-coverage-gate/compute-local-patch-coverage.sh:
- Around line 60-68: Update the no-artifact fallback that determines COVERABLE
so it loads and applies the patterns from CODECOV_IGNORE_FILE before classifying
changed files, rather than relying only on hard-coded exclusions. Reuse the
existing EXCLUDES population and matching logic, ensuring ignored source-only
changes are treated as non-coverable when coverage artifacts are unavailable.

In @.github/actions/patch-coverage-gate/read-codecov-config.sh:
- Around line 38-41: Update the PyYAML fallback output in the `patch`
configuration parsing so `target` is replaced with an empty string only when it
is `None`, preserving numeric zero consistently with the `yq` path and
validator.
- Around line 35-46: Replace the eval-based configuration loading around the
Python heredoc with data-only parsing of its two output lines, so target and
threshold values from codecov.yml are assigned without shell re-evaluation.
Preserve the existing default handling for missing target and threshold, and
continue writing ignore globs through the Python block.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 111b909c-c808-4cd0-98bf-d620183e1a26

📥 Commits

Reviewing files that changed from the base of the PR and between 586dc49 and a657a51.

📒 Files selected for processing (5)
  • .github/actions/patch-coverage-gate/action.yml
  • .github/actions/patch-coverage-gate/check-codecov-api.sh
  • .github/actions/patch-coverage-gate/compute-local-patch-coverage.sh
  • .github/actions/patch-coverage-gate/read-codecov-config.sh
  • .github/workflows/pr-builder.yml

Comment thread .github/actions/patch-coverage-gate/compute-local-patch-coverage.sh
Comment thread .github/actions/patch-coverage-gate/read-codecov-config.sh Outdated
Comment thread .github/actions/patch-coverage-gate/read-codecov-config.sh
@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@DonOmalVindula
DonOmalVindula force-pushed the ci/gate-parity branch 2 times, most recently from c05fbbf to 2233fa6 Compare July 28, 2026 09:18
@DonOmalVindula DonOmalVindula changed the title Read patch coverage rules from codecov.yml and align merge queue gating Read patch coverage rules from codecov.yml and verify Codecov's comparison base Jul 28, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/actions/patch-coverage-gate/check-codecov-api.sh:
- Around line 61-70: Update the Codecov comparison validation near CODECOV_BASE
and MERGE_BASE to also extract and validate the response’s .head_commit against
HEAD_SHA. If the head commit is missing or differs, emit the existing fallback
warning and exit successfully so local patch coverage is used; retain the
current base and merge-base checks for valid matching comparisons.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: cbdf633b-f957-47ea-85c1-77c9289c7fc6

📥 Commits

Reviewing files that changed from the base of the PR and between 7e9cb07 and 2233fa6.

📒 Files selected for processing (6)
  • .github/actions/patch-coverage-gate/action.yml
  • .github/actions/patch-coverage-gate/check-codecov-api.sh
  • .github/actions/patch-coverage-gate/compute-local-patch-coverage.sh
  • .github/actions/patch-coverage-gate/list-coverable-changes.sh
  • .github/actions/patch-coverage-gate/read-codecov-config.sh
  • .github/workflows/pr-builder.yml
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/actions/patch-coverage-gate/action.yml
  • .github/workflows/pr-builder.yml

Comment thread .github/actions/patch-coverage-gate/check-codecov-api.sh Outdated
@DonOmalVindula DonOmalVindula changed the title Read patch coverage rules from codecov.yml and verify Codecov's comparison base Mirror the codecov/patch verdict in the patch coverage gate Jul 30, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/actions/patch-coverage-gate/action.yml:
- Around line 60-67: Pass the gate’s expected base reference into the mirror
helper from the codecov-api step in
.github/actions/patch-coverage-gate/action.yml (lines 60-67), then update
mirror-codecov-check.sh (lines 32-53) to validate the Codecov comparison’s base
or base_commit against BASE_REF before mirroring; skip the verdict when the
bases do not match.

In @.github/actions/patch-coverage-gate/read-codecov-config.sh:
- Around line 30-46: Normalize Codecov ignore rules before local filtering:
update read-codecov-config.sh to distinguish directory-prefix, glob/fnmatch,
regex, and unsupported entries; update list-coverable-changes.sh to apply the
normalized compatible exclusions when selecting files; and update
compute-local-patch-coverage.sh to pass only compatible fnmatch/glob exclusions
to diff-cover, guarding or excluding unsupported regex syntax. Apply these
changes at .github/actions/patch-coverage-gate/read-codecov-config.sh:30-46,
.github/actions/patch-coverage-gate/list-coverable-changes.sh:18-20, and
.github/actions/patch-coverage-gate/compute-local-patch-coverage.sh:63-78.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 44eec152-530e-4521-99c8-18e7e8ca400e

📥 Commits

Reviewing files that changed from the base of the PR and between 2233fa6 and ef980e6.

📒 Files selected for processing (7)
  • .github/actions/patch-coverage-gate/action.yml
  • .github/actions/patch-coverage-gate/check-codecov-api.sh
  • .github/actions/patch-coverage-gate/compute-local-patch-coverage.sh
  • .github/actions/patch-coverage-gate/list-coverable-changes.sh
  • .github/actions/patch-coverage-gate/mirror-codecov-check.sh
  • .github/actions/patch-coverage-gate/read-codecov-config.sh
  • .github/workflows/pr-builder.yml
💤 Files with no reviewable changes (1)
  • .github/actions/patch-coverage-gate/check-codecov-api.sh

Comment thread .github/actions/patch-coverage-gate/action.yml Outdated
Comment thread .github/actions/patch-coverage-gate/read-codecov-config.sh
@DonOmalVindula DonOmalVindula changed the title Mirror the codecov/patch verdict in the patch coverage gate Resolve patch coverage from Codecov first and name the source of every verdict Jul 30, 2026
@DonOmalVindula DonOmalVindula added trigger-pr-builder Add when the PR is ready for CI; starts the PR Builder for this and all later pushes and removed trigger-pr-builder Add when the PR is ready for CI; starts the PR Builder for this and all later pushes labels Jul 30, 2026
@DonOmalVindula
DonOmalVindula force-pushed the ci/gate-parity branch 2 times, most recently from eae3ada to 2bf7565 Compare July 30, 2026 04:53
@DonOmalVindula
DonOmalVindula added this pull request to the merge queue Jul 30, 2026
Merged via the queue into thunder-id:main with commit 54e5d4e Jul 30, 2026
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

trigger-pr-builder Add when the PR is ready for CI; starts the PR Builder for this and all later pushes Type/Improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants