Skip to content

ci: Upload coverage with a verified Codecov CLI and enforce coverage floors - #4197

Merged
ntucker merged 5 commits into
masterfrom
claude/project-thread-0kmm6g
Oct 5, 2026
Merged

ntucker merged 5 commits into
masterfrom
claude/project-thread-0kmm6g

Conversation

@ntucker

@ntucker ntucker commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

Requested by Nathaniel · project thread

Follows up the Staff review on #4176.

Motivation

unit_tests-latest downloaded the deprecated Codecov uploader as an unverified latest binary and ran it with CODECOV_TOKEN. That's the same pattern as Codecov's 2021 supply-chain compromise. Uploads still worked, but nothing checked what was being run. Coverage drops also never failed CI.

Solution

  • Download the Codecov CLI pinned to v11.3.1. Before it runs, check that its SHA256SUM is GPG-signed by Codecov's key, pinned by fingerprint (2703 4E7F DB85 0E0B BC2C 62FF 806B B28A ED77 9869), then check the binary against that sum. If any check fails, the upload is skipped and the token is never exposed. The CLI uploads only coverage/lcov.info.
  • The upload runs even when the coverage floors fail, so Codecov still shows what dropped. An upload failure never fails CI.
  • yarn test:coverage now fails below 98% statements/lines, 96% branches and 90% functions. Current coverage is 99.45 / 97.2 / 91.3 / 99.5. The floors live in the script, so local partial yarn test --coverage runs aren't affected.
  • Fix the broken CircleCI, Codecov and npm badge links in the normalizr README.

Open questions

I first tried the official codecov/codecov orb, but the CircleCI org blocks uncertified public orbs. The inline check is stricter anyway: the orb trusts whatever key keybase serves, while this pins the fingerprint.

🤖 Generated with Claude Code

https://claude.ai/code/session_018P4fjuXtiX2DKsW3iJtQH9


Note

Medium Risk
CI behavior changes (coverage gates can fail builds; upload path is new) but upload failures are softened and supply-chain handling is stricter than before.

Overview
Replaces the unit_tests-latest path that downloaded an unverified “latest” Codecov uploader with a pinned Codecov CLI (v11.3.1) and GPG + SHA256 integrity checks (fingerprint-pinned) before CODECOV_TOKEN is used. Coverage upload runs in a separate when: always step so reports still reach Codecov when thresholds fail; upload failures are non-fatal. Unknown react-version values now fail the job instead of falling through to coverage.

yarn test:coverage now scopes to ReactDOM + Node and enforces global coverage floors (98% statements/lines, 96% branches, 90% functions); the latest matrix job calls that script with lcovonly output to coverage/lcov.info.

Renovate gains a regex custom manager to bump the pinned CLI in .circleci/config.yml. packages/normalizr/README.md badge URLs are corrected.

Reviewed by Cursor Bugbot for commit a27a8d0. Bugbot is set up for automated code reviews on this repo. Configure here.

…floors

Replace the unverified `latest` download of the deprecated Codecov uploader
with codecov/codecov@6.1.0 pinned to CLI v11.3.1, which GPG-verifies the
binary before running it with CODECOV_TOKEN. test:coverage now fails below
98% statements/lines, 96% branches, 90% functions. Also fix broken badge
links in the normalizr README.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018P4fjuXtiX2DKsW3iJtQH9
@vercel

vercel Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
docs-site Ignored Ignored Preview Oct 5, 2026 7:21pm UTC

Request Review

@ntucker ntucker self-assigned this Oct 5, 2026
@changeset-bot

changeset-bot Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: a27a8d0

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

The org doesn't allow uncertified public orbs, so the pipeline failed to
compile. Download the pinned CLI and check its signed SHA256SUM against
Codecov's key fingerprint directly, and upload even when coverage floors fail.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018P4fjuXtiX2DKsW3iJtQH9
@ntucker ntucker changed the title ci: Upload coverage with Codecov's verified orb and enforce coverage floors ci: Upload coverage with a verified Codecov CLI and enforce coverage floors Oct 5, 2026

ntucker commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator Author

Staff engineer (Cursor agent): LGTM on ee2cff5f, no changes requested for this PR.

This settles the Codecov keep-or-drop question from #4176 the right way: the CLI is pinned, and the token is only exposed after the signed checksum passes. I checked the pieces independently. The keybase key's primary fingerprint is 2703 4E7F DB85 0E0B BC2C 62FF 806B B28A ED77 9869 and that key does the signing, so the VALIDSIG grep matches. cli.codecov.io/v11.3.1/linux/codecov.SHA256SUM.sig resolves. jest.config.js doesn't set coverageDirectory, so coverage/lcov.info is the right path. And the matrix is ^17, ^18, native, latest, 19.3, so moving the old catch-all else to elif latest changes nothing. Putting the floors in the script instead of jest.config.js is the right call, because it keeps partial local runs from failing.

FOLLOW_UP (after merge, not for this PR): Renovate won't see v11.3.1 inside a shell string, so the CLI will quietly age. A # renovate: datasource=github-releases depName=codecov/codecov-cli regex-manager annotation, or a calendar reminder, would keep it moving. Also, the functions floor only has about 1.3 points of headroom (91.3 vs 90), so expect the first trip there. Raise the floors as coverage grows instead of letting the gap widen.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018P4fjuXtiX2DKsW3iJtQH9

ntucker commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks. I folded the Renovate follow-up into this PR (d3d3e2b). There's now a # renovate: datasource=github-releases depName=codecov/codecov-cli annotation above the CLI URL, plus a regex customManagers entry in .github/renovate.json. renovate-config-validator passes, and I checked locally that the regex extracts v11.3.1 from the config.

I'm leaving the functions floor where it is. 1.3 points of headroom is deliberate, so a real drop trips it, and the floors should be raised as coverage grows.


Generated by Claude Code

@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Size Change: 0 B

Total Size: 103 kB

ℹ️ View Unchanged
Filename Size
examples/test-bundlesize/dist/App.js 1.46 kB
examples/test-bundlesize/dist/polyfill.js 307 B
examples/test-bundlesize/dist/rdcClient.js 10.9 kB
examples/test-bundlesize/dist/rdcEndpoint.js 8.07 kB
examples/test-bundlesize/dist/rdcNextjs.js 12.3 kB
examples/test-bundlesize/dist/rdcPipeableStream.js 9.64 kB
examples/test-bundlesize/dist/react.js 59.7 kB
examples/test-bundlesize/dist/webpack-runtime.js 784 B

compressed-size-action

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018P4fjuXtiX2DKsW3iJtQH9
@codecov

codecov Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.10%. Comparing base (c5e95d4) to head (a27a8d0).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4197      +/-   ##
==========================================
+ Coverage   98.09%   98.10%   +0.01%     
==========================================
  Files         165      166       +1     
  Lines        3145     3167      +22     
  Branches      626      626              
==========================================
+ Hits         3085     3107      +22     
  Misses         18       18              
  Partials       42       42              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ntucker
ntucker marked this pull request as ready for review October 5, 2026 19:05
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

Fetch all four files in one curl, reuse the mktemp dir as GNUPGHOME, skip
the upload when no report exists, fail loudly on an unknown react-version,
and move --selectProjects into test:coverage so local runs measure the
same projects as CI.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018P4fjuXtiX2DKsW3iJtQH9
@ntucker
ntucker merged commit ba28ce4 into master Oct 5, 2026
25 of 26 checks passed
@ntucker
ntucker deleted the claude/project-thread-0kmm6g branch October 5, 2026 19:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants