Skip to content

Commit 859f8ef

Browse files
authored
fix(ci): use a colon-prefixed branch for fork tokenless Codecov uploads (#2363)
A real fork PR rebase (gittensory#2496-equivalent traffic) hit "Token required because branch is protected" on the tokenless upload. Per Codecov's own docs, only a branch string with a colon-separated prefix (e.g. forkname:branch) is recognized as unprotected/tokenless-eligible; a bare branch name looks like it could be a real branch on the base repo and gets rejected even with no token configured anywhere. codecov-cli's own auto-detection never adds this prefix (confirmed in its source), so override_branch must supply it explicitly. override_commit/override_pr are unaffected -- the protected-branch check only inspects the branch string.
1 parent ec131cf commit 859f8ef

2 files changed

Lines changed: 18 additions & 3 deletions

File tree

.github/workflows/ci.yml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,13 +159,20 @@ jobs:
159159
# actual commit, not a synthetic merge), so HEAD has one parent and that recovery can't fire. Pass
160160
# the same explicit overrides as the trusted upload above so the report attaches to the real PR head,
161161
# not a merge sha GitHub's PR checks list has no reason to display.
162+
#
163+
# override_branch is prefixed with the fork owner (owner:branch) -- per Codecov's own docs, only a
164+
# branch string containing a colon is recognized as "unprotected" and eligible for a tokenless
165+
# upload; a bare branch name looks like it could be a real (possibly protected) branch on the base
166+
# repo and gets rejected with "Token required because branch is protected" even with no token
167+
# configured at all. codecov-cli's own auto-detection never adds this prefix either (verified in its
168+
# source), so this must be supplied explicitly.
162169
- name: Upload coverage to Codecov (fork PR tokenless)
163170
if: ${{ success() && needs.changes.outputs.backend == 'true' && github.event.pull_request.head.repo.fork == true }}
164171
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
165172
with:
166173
files: ./coverage/lcov.info
167174
disable_search: true
168-
override_branch: ${{ github.event.pull_request.head.ref }}
175+
override_branch: ${{ github.event.pull_request.head.repo.owner.login }}:${{ github.event.pull_request.head.ref }}
169176
override_commit: ${{ github.event.pull_request.head.sha }}
170177
override_pr: ${{ github.event.pull_request.number }}
171178
fail_ci_if_error: true
@@ -191,7 +198,7 @@ jobs:
191198
files: ./reports/junit/vitest.xml
192199
report_type: test_results
193200
disable_search: true
194-
override_branch: ${{ github.event.pull_request.head.ref }}
201+
override_branch: ${{ github.event.pull_request.head.repo.owner.login }}:${{ github.event.pull_request.head.ref }}
195202
override_commit: ${{ github.event.pull_request.head.sha }}
196203
override_pr: ${{ github.event.pull_request.number }}
197204
fail_ci_if_error: false

test/unit/codecov-policy.test.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,16 @@ describe("Codecov policy", () => {
100100
// recover the real head sha assumes a 2-parent merge commit at HEAD -- which our checkout step (it
101101
// fetches github.event.pull_request.head.sha directly) never produces. Without an explicit override,
102102
// the report would attach to a sha GitHub's PR checks list has no reason to ever display.
103-
expect(forkCoverageWith.override_branch).toBe("${{ github.event.pull_request.head.ref }}");
104103
expect(forkCoverageWith.override_commit).toBe("${{ github.event.pull_request.head.sha }}");
105104
expect(forkCoverageWith.override_pr).toBe("${{ github.event.pull_request.number }}");
105+
// Codecov only treats a branch as "unprotected" (eligible for tokenless upload) when its name has a
106+
// colon-separated prefix; a bare branch name gets rejected with "Token required because branch is
107+
// protected" even with no token configured anywhere. codecov-cli's own auto-detection never adds
108+
// this prefix, so it must be supplied explicitly -- omitting it is exactly the regression this guards.
109+
expect(String(forkCoverageWith.override_branch)).toContain(":");
110+
expect(forkCoverageWith.override_branch).toBe(
111+
"${{ github.event.pull_request.head.repo.owner.login }}:${{ github.event.pull_request.head.ref }}",
112+
);
106113

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

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

0 commit comments

Comments
 (0)