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
10 changes: 7 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1111,8 +1111,12 @@ jobs:
--lcov-directory .coverage-artifacts
--target 90

# Public fork PRs use the pinned action's tokenless mode. GitHub does not
# expose repository secrets to these pull_request runs; no privileged
# trigger or OIDC/write permission is needed. Their required codecov/patch
# status must be produced just like same-repository PRs.
- name: Upload coverage to Codecov
if: steps.cov.outputs.has-coverage == 'true' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
if: steps.cov.outputs.has-coverage == 'true'
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with:
token: ${{ secrets.CODECOV_TOKEN }}
Expand All @@ -1129,7 +1133,7 @@ jobs:
fail_ci_if_error: false

- name: Wait for Codecov to merge the uploaded report
if: steps.cov.outputs.has-coverage == 'true' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
if: steps.cov.outputs.has-coverage == 'true'
env:
CODECOV_COMMIT: ${{ github.event.pull_request.head.sha || github.sha }}
CODECOV_BUILD_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
Expand All @@ -1155,7 +1159,7 @@ jobs:
done

- name: Publish finalized Codecov notifications
if: steps.cov.outputs.has-coverage == 'true' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
if: steps.cov.outputs.has-coverage == 'true'
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with:
token: ${{ secrets.CODECOV_TOKEN }}
Expand Down
11 changes: 11 additions & 0 deletions docs/reference/ci-performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,14 @@ gate independently checks scope outputs against each job result: selected jobs
must succeed; missing, cancelled or skipped selected jobs fail the merge gate.
Only genuinely unselected lanes and main's PR-only checks may be skipped.
Do not infer full release acceptance from a green documentation-only push.

## Contributor-fork coverage

Public fork PRs upload the merged LCOV evidence through the pinned Codecov action's
[tokenless fork mode](https://github.com/codecov/codecov-action#v4-release).
GitHub withholds repository secrets from these ordinary `pull_request` jobs;
no privileged trigger or additional write/OIDC permission is granted. The uploader,
processing check and final notifications run for every nonempty report, so the
required `codecov/patch` status is available for contributors as well as maintainers.
The repository-owned 90% patch-coverage check remains the first blocking check
on the exact diff.
17 changes: 17 additions & 0 deletions scripts/ci-orchestration.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,20 @@ test('all selected execution jobs survive skipped ancestors and expose a strict
assert.match(gate, /CI_NEEDS: \$\{\{ toJSON\(needs\) \}\}/)
assert.match(gate, /run: node scripts\/ci-result-gate\.mjs/)
})

test('fork PRs can produce the required Codecov status without a privileged workflow', () => {
const workflow = readFileSync(CI_PATH, 'utf8')
for (const step of [
'Upload coverage to Codecov',
'Wait for Codecov to merge the uploaded report',
'Publish finalized Codecov notifications'
]) {
assert.ok(
workflow.includes(
` - name: ${step}\n if: steps.cov.outputs.has-coverage == 'true'\n`
)
)
}
assert.doesNotMatch(workflow, /pull_request_target|id-token:\s*write/)
assert.match(workflow, /run: >-\n node scripts\/patch-coverage\.mjs/)
})
Loading