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
11 changes: 9 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -159,13 +159,20 @@ jobs:
# actual commit, not a synthetic merge), so HEAD has one parent and that recovery can't fire. Pass
# the same explicit overrides as the trusted upload above so the report attaches to the real PR head,
# not a merge sha GitHub's PR checks list has no reason to display.
#
# override_branch is prefixed with the fork owner (owner:branch) -- per Codecov's own docs, only a
# branch string containing a colon is recognized as "unprotected" and eligible for a tokenless
# upload; a bare branch name looks like it could be a real (possibly protected) branch on the base
# repo and gets rejected with "Token required because branch is protected" even with no token
# configured at all. codecov-cli's own auto-detection never adds this prefix either (verified in its
# source), so this must be supplied explicitly.
- name: Upload coverage to Codecov (fork PR tokenless)
if: ${{ success() && needs.changes.outputs.backend == 'true' && github.event.pull_request.head.repo.fork == true }}
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with:
files: ./coverage/lcov.info
disable_search: true
override_branch: ${{ github.event.pull_request.head.ref }}
override_branch: ${{ github.event.pull_request.head.repo.owner.login }}:${{ github.event.pull_request.head.ref }}
override_commit: ${{ github.event.pull_request.head.sha }}
override_pr: ${{ github.event.pull_request.number }}
fail_ci_if_error: true
Expand All @@ -191,7 +198,7 @@ jobs:
files: ./reports/junit/vitest.xml
report_type: test_results
disable_search: true
override_branch: ${{ github.event.pull_request.head.ref }}
override_branch: ${{ github.event.pull_request.head.repo.owner.login }}:${{ github.event.pull_request.head.ref }}
override_commit: ${{ github.event.pull_request.head.sha }}
override_pr: ${{ github.event.pull_request.number }}
fail_ci_if_error: false
Expand Down
10 changes: 9 additions & 1 deletion test/unit/codecov-policy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,16 @@ describe("Codecov policy", () => {
// recover the real head sha assumes a 2-parent merge commit at HEAD -- which our checkout step (it
// fetches github.event.pull_request.head.sha directly) never produces. Without an explicit override,
// the report would attach to a sha GitHub's PR checks list has no reason to ever display.
expect(forkCoverageWith.override_branch).toBe("${{ github.event.pull_request.head.ref }}");
expect(forkCoverageWith.override_commit).toBe("${{ github.event.pull_request.head.sha }}");
expect(forkCoverageWith.override_pr).toBe("${{ github.event.pull_request.number }}");
// Codecov only treats a branch as "unprotected" (eligible for tokenless upload) when its name has a
// colon-separated prefix; a bare branch name gets rejected with "Token required because branch is
// protected" even with no token configured anywhere. codecov-cli's own auto-detection never adds
// this prefix, so it must be supplied explicitly -- omitting it is exactly the regression this guards.
expect(String(forkCoverageWith.override_branch)).toContain(":");
expect(forkCoverageWith.override_branch).toBe(
"${{ github.event.pull_request.head.repo.owner.login }}:${{ github.event.pull_request.head.ref }}",
);

const forkTestResultsUpload = steps.find(
(step) => step.name === "Upload Vitest results to Codecov (fork PR tokenless)",
Expand All @@ -113,6 +120,7 @@ describe("Codecov policy", () => {
expect(forkTestResultsWith.report_type).toBe("test_results");
expect(forkTestResultsWith.fail_ci_if_error).toBe(false);
expect(forkTestResultsWith.override_commit).toBe("${{ github.event.pull_request.head.sha }}");
expect(String(forkTestResultsWith.override_branch)).toContain(":");

// The trusted (token) path must still explicitly exclude forks -- it must never see the token env
// used, and the two paths must be mutually exclusive so a fork PR never double-uploads.
Expand Down