Skip to content
Merged
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: 7 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1088,7 +1088,7 @@ jobs:
# Consumed by validate-tests-merge to re-check the global coverage threshold against all shards
# combined -- see this job's own header comment.
- name: Upload coverage blob report
if: ${{ success() }}
if: ${{ success() && steps.coverage.outputs.no_tests_matched != 'true' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-blob-shard-${{ matrix.shard }}
Expand All @@ -1097,7 +1097,9 @@ jobs:
# Direct upload for trusted contexts (push + same-repo PRs). Multiple shards' uploads for the same
# commit/PR are additive in Codecov -- see this job's own header comment.
- name: Upload coverage to Codecov
if: ${{ success() && github.event.pull_request.head.repo.fork != true }}
# Same no_tests_matched skip as the verify step above (#8167 follow-up): with zero matched tests
# there is no lcov to upload, and fail_ci_if_error would otherwise turn that non-event red.
if: ${{ success() && steps.coverage.outputs.no_tests_matched != 'true' && github.event.pull_request.head.repo.fork != true }}
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with:
token: ${{ secrets.CODECOV_TOKEN }}
Expand All @@ -1115,7 +1117,7 @@ jobs:
# override, etc). Condition dropped the push-or-backend check since this job's own `if:` already
# covers it.
- name: Upload coverage to Codecov (fork PR tokenless)
if: ${{ success() && github.event.pull_request.head.repo.fork == true }}
if: ${{ success() && steps.coverage.outputs.no_tests_matched != 'true' && github.event.pull_request.head.repo.fork == true }}
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with:
files: ./coverage/lcov.info
Expand All @@ -1129,7 +1131,7 @@ jobs:
# their upload non-blocking so a JUnit ingestion hiccup does not fail CI
# after the tests and hard coverage upload have already passed.
- name: Upload Vitest results to Codecov
if: ${{ !cancelled() && github.event.pull_request.head.repo.fork != true }}
if: ${{ !cancelled() && steps.coverage.outputs.no_tests_matched != 'true' && github.event.pull_request.head.repo.fork != true }}
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with:
token: ${{ secrets.CODECOV_TOKEN }}
Expand All @@ -1142,7 +1144,7 @@ jobs:
override_pr: ${{ github.event_name == 'pull_request' && github.event.pull_request.number || '' }}
fail_ci_if_error: false
- name: Upload Vitest results to Codecov (fork PR tokenless)
if: ${{ !cancelled() && github.event.pull_request.head.repo.fork == true }}
if: ${{ !cancelled() && steps.coverage.outputs.no_tests_matched != 'true' && github.event.pull_request.head.repo.fork == true }}
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with:
files: ./reports/junit/vitest.xml
Expand Down
16 changes: 11 additions & 5 deletions test/unit/codecov-policy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,17 @@ describe("Codecov policy", () => {
const testResultsUpload = steps[testResultsUploadIndex]!;

// Verify must run whenever coverage was generated at all -- the job's own top-level `if:` (push or
// backend==true) already gates the whole matrix, so the step itself only needs `success()`. It
// deliberately does NOT exclude forks, since both the trusted and the tokenless fork upload path
// below it need the report to exist first.
expect(String(verifyStep.if)).toBe("${{ success() }}");
expect(String(coverageUpload.if)).toContain(String(verifyStep.if).replace(/^\$\{\{\s*|\s*\}\}$/g, ""));
// backend==true) already gates the whole matrix. #8167 added the ONE legitimate escape: a scoped
// selection that matched zero test files writes no report at all (no_tests_matched), so verify — and
// every upload below it — must skip on that output rather than fail closed on a missing lcov. The
// policy stays: absent that explicit escape hatch, a missing report still fails the build, and the
// uploads can never run without the verify guard's own condition.
expect(String(verifyStep.if)).toBe("${{ success() && steps.coverage.outputs.no_tests_matched != 'true' }}");
// EVERY upload below the verify step must carry the same escape: with zero matched tests there is no
// lcov/junit at all, and the coverage uploads' fail_ci_if_error would turn that non-event red.
expect(String(coverageUpload.if)).toContain("success()");
expect(String(coverageUpload.if)).toContain("no_tests_matched != 'true'");
expect(String(testResultsUpload.if)).toContain("no_tests_matched != 'true'");
expect(String(verifyStep.run)).toContain("coverage/lcov.info is missing or empty");
expect(String(verifyStep.run)).toContain("exit 1");

Expand Down
Loading