Skip to content

feat(scorecard): add codecov backend module with 7 coverage metrics#3477

Open
fullsend-ai-coder[bot] wants to merge 4 commits into
mainfrom
fs/3473-scorecard-codecov-module
Open

feat(scorecard): add codecov backend module with 7 coverage metrics#3477
fullsend-ai-coder[bot] wants to merge 4 commits into
mainfrom
fs/3473-scorecard-codecov-module

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

Add a new scorecard-backend-module-codecov plugin that integrates with the Codecov API to provide 7 code coverage metrics: coverage percentage, coverage trend, tracked files, tracked lines, covered lines, partial lines, and missed lines. The module supports multi-account configuration with optional auth tokens and resolves service/owner/repo from entity annotations with fallback logic. All 7 metrics share a single API call via the batch provider pattern.

Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com


Closes #3473

Post-script verification

  • Branch is not main/master (fs/3473-scorecard-codecov-module)
  • Secret scan passed (gitleaks — 7ccaff17753df64c7ab288cdcba34cee5a657254..HEAD)
  • Pre-commit hooks passed (authoritative run on runner)
  • Tests ran inside sandbox

@rhdh-gh-app

rhdh-gh-app Bot commented Jun 19, 2026

Copy link
Copy Markdown

Important

This PR includes changes that affect public-facing API. Please ensure you are adding/updating documentation for new features or behavior.

Changed Packages

Package Name Package Path Changeset Bump Current Version
backend workspaces/scorecard/packages/backend none v0.0.0
@red-hat-developer-hub/backstage-plugin-scorecard-backend-module-codecov workspaces/scorecard/plugins/scorecard-backend-module-codecov minor v0.0.0

@codecov

codecov Bot commented Jun 19, 2026

Copy link
Copy Markdown

⚠️ JUnit XML file not found

The CLI was unable to find any JUnit XML files to upload.
For more help, visit our troubleshooting guide.

@fullsend-ai-review

fullsend-ai-review Bot commented Jun 19, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 1:30 PM UTC · Completed 1:42 PM UTC
Commit: 7ccaff1 · View workflow run →

@fullsend-ai-review

fullsend-ai-review Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review

Findings

High

  • [nil/null handling] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/clients/types.ts:48 — The totals field in CodecovRepoResponse is typed as non-nullable CodecovTotals, but the Codecov repos_retrieve API can return totals: null for repositories that exist but have never uploaded coverage data. When totals is null, both calculateMetric and calculateMetrics in CodecovMetricProvider will crash with Cannot read properties of null when accessing repoInfo.totals[field].
    Remediation: Change the type to totals: CodecovTotals | null and add a null check in CodecovMetricProvider.calculateMetric and calculateMetrics before accessing totals fields. Return a sensible default (e.g., 0) or throw a descriptive error when totals is null.

Medium

  • [API behavior claims] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/metricProviders/CodecovConfig.ts:121 — The coverage_trend metric is described as "Code coverage trend for the last 7 days" and mapped to totals.diff. However, the Codecov repos_retrieve API's totals.diff field represents the diff coverage of the latest commit/comparison, not a 7-day rolling trend. The README and the metric description both make this incorrect claim.
    Remediation: Update the description to accurately reflect what totals.diff represents (e.g., "Diff coverage for the latest comparison"), or use a different Codecov API endpoint that actually provides a time-based trend.

Low

  • [error handling gaps] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/metricProviders/CodecovMetricProvider.ts:99 — If the Codecov API returns a response where specific totals fields are null, the returned value will be null cast as number, silently passing invalid data downstream. This is largely addressed by the null-handling fix above.

  • [edge cases] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/metricProviders/CodecovConfig.ts:155 — The threshold rules for partial_lines and missed_lines use absolute numeric boundaries (<10, 10-50, >50) that are not meaningful as universal defaults. A large project would reasonably have more than 50 missed lines even with excellent coverage.

  • [logic errors] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/metricProviders/CodecovConfig.ts:130 — The type of CODECOV_TOTALS_FIELD_MAP includes | 'diff' but diff is already a member of keyof CodecovTotals. The redundant union can be simplified.

  • [test adequacy] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/metricProviders/CodecovMetricProvider.test.ts — Tests do not cover the case where the Codecov API returns totals: null or where individual fields within totals are null.

  • [Fail-Open Authentication] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/clients/CodecovClient.ts:33 — When a specific account name is requested via annotation but not found in config, resolveAuthToken() returns undefined and the API call proceeds without authentication. A warning is logged, but the silent fallback could mask configuration errors for private repos.

Previous run

Review

Findings

High

  • [nil-deref] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/metricProviders/CodecovMetricProvider.ts:97 — Both calculateMetric and calculateMetrics access repoInfo.totals[field] without null-checking totals. The Codecov repos_retrieve API returns totals: null for repositories that have been registered but have no coverage uploads yet. The TypeScript interface CodecovRepoResponse types totals as CodecovTotals (non-nullable), masking this at compile time, but at runtime repoInfo.totals will be null and property access will throw TypeError: Cannot read properties of null.
    Remediation: Change the totals field in CodecovRepoResponse to totals: CodecovTotals | null, and add a null guard in both calculateMetric and calculateMetrics that either throws a descriptive error (e.g., "No coverage data available for {repo}") or returns a sensible default (e.g., 0).

Medium

  • [api-contract] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/metricProviders/CodecovConfig.ts:148 — The coverage_trend metric is described as "Code coverage trend for the last 7 days" (in both the README and CODECOV_METRIC_CONFIG) but maps to totals.diff from the Codecov repos_retrieve API. The totals.diff field represents the coverage diff of the most recent commit, not a 7-day rolling trend. The description misleads users about what the metric actually measures.
    Remediation: Update the description to accurately reflect what totals.diff represents (e.g., "Coverage diff from the latest commit") or, if a 7-day trend is genuinely desired, use a different Codecov API endpoint that provides time-series data.

Low

  • [error-handling] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/clients/CodecovClient.ts:87getRepoInfo calls response.json() without a try-catch. If the Codecov API returns a 200 with a non-JSON body (e.g., HTML from a proxy), the error message will be confusing. Consider wrapping in try-catch for a more descriptive error.

  • [test-inadequate] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/metricProviders/CodecovMetricProvider.test.ts — The test suite does not cover the case where totals is null or where resolveCodecovEntityInfo throws. Both calculateMetric and calculateMetrics will fail at runtime for these scenarios but the tests only exercise the happy path.

Info

  • [edge-case] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/metricProviders/CodecovConfig.ts:117 — The CODECOV_TOTALS_FIELD_MAP type includes | 'diff' but diff is already a key of CodecovTotals, making the union redundant.

  • [sub-agent-failure] N/A — The style-conventions, intent-coherence, and docs-currency sub-agents did not return findings: model claude-sonnet-4-5@20250929 not available on deployment. These are non-critical review dimensions; correctness and security reviews completed successfully.

Previous run (2)

Review

Findings

High

  • [null-handling] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/clients/types.ts:81 — The totals field in CodecovRepoResponse is typed as non-nullable CodecovTotals, but the Codecov API (repos_retrieve) returns totals: null for repositories with no coverage data uploaded. When totals is null, repoInfo.totals[field] in both calculateMetric and calculateMetrics will throw a TypeError at runtime.
    Remediation: Change the type to totals: CodecovTotals | null and add a null guard in calculateMetric and calculateMetrics that throws a descriptive error or returns a sentinel value.

Medium

  • [efficiency] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/metricProviders/CodecovMetricProviderFactory.ts:28 — The factory creates 7 separate CodecovMetricProvider instances, each constructing its own CodecovClient. If the framework calls calculateMetric on individual providers (non-batch path), 7 separate HTTP requests will be made for the same repository data.
    Remediation: Create a single CodecovClient instance in the factory and share it across all 7 providers.

  • [fail-open-auth-degradation] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/clients/CodecovClient.ts:48 — When an entity's codecov.io/account annotation references an account name not present in the config, resolveAuthToken() silently returns undefined and the request proceeds without authentication. A misconfigured account name silently degrades from authenticated to unauthenticated access, making the root cause hard to diagnose for private repos.
    Remediation: When an explicit accountName is provided but no matching account is found, throw an error rather than silently falling back to unauthenticated requests.

  • [scope-gap] Missing changeset entry — the PR does not include a changeset file. Without one, the new package will not appear in the next release changelog and its version will not be bumped by the automated release pipeline.
    Remediation: Add a changeset file via yarn changeset.

  • [scope-gap] Missing README.md — every other scorecard backend module (dependabot, filecheck, github, jira, openssf, sonarqube) ships a README documenting installation, required annotations, and usage. The new codecov module does not.
    Remediation: Add a README.md following the pattern of existing modules.

Low

  • [API-contract-accuracy] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/metricProviders/CodecovConfig.ts:130 — The coverage_trend metric description says "Code coverage trend for the last 7 days" but maps to the diff field, which represents coverage diff from the most recent comparison, not a 7-day window.

  • [null-handling] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/clients/types.ts:49 — The coverage field in CodecovTotals is typed as number, but the Codecov API may return coverage: null when there are tracked files but no actual coverage measurements.

  • [test-adequacy] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/metricProviders/CodecovMetricProvider.test.ts — No test covers the error path when totals is null. Tests only use a happy-path sample response.

  • [insufficient-input-validation] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/metricProviders/CodecovConfig.ts:56 — The service value from the codecov.io/service annotation is used in the API URL without validation against known Codecov services. While encodeURIComponent prevents URL manipulation, allowlist validation would be a defense-in-depth improvement.

  • [scope-alignment] workspaces/scorecard/plugins/scorecard-backend-module-codecov/config.d.ts:24 — The defaultAccount field is marked @visibility frontend, but this is a backend-only module with no frontend counterpart.

  • [package-json-consistency] workspaces/scorecard/plugins/scorecard-backend-module-codecov/package.json — Missing keywords, homepage, bugs, and author fields that some peer modules include (e.g., github module).

Previous run (3)

Review

Findings

High

  • [nil/null handling] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/clients/types.ts:55 — The CodecovRepoResponse.totals field is typed as non-optional CodecovTotals, but the Codecov API returns totals: null for repositories that have no coverage data uploaded yet. Both calculateMetric and calculateMetrics access repoInfo.totals[field] without any null check, which will throw a TypeError at runtime for such repos.
    Remediation: Change the type to totals: CodecovTotals | null and add a null guard in both calculateMetric and calculateMetrics that either throws a descriptive error or returns a sensible default (e.g., 0 or NaN) when totals is null.

Medium

  • [error-handling-idiom] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/clients/CodecovClient.ts:62 — When a specific account name is requested but not found in configuration, the CodecovClient only logs a warning and returns undefined (falling through silently to an unauthenticated request). In the established SonarQube module, the analogous situation throws an Error. This means the codecov module will silently make unauthenticated requests when a user misconfigures an account name.
    Remediation: Throw an Error when a specific accountName is provided but not found in configuration, matching the SonarQube pattern.

  • [missing-artifact] workspaces/scorecard/plugins/scorecard-backend-module-codecov — The new plugin is missing a README.md file. All 6 other scorecard backend modules ship a README documenting their metrics, annotations, and configuration. This is a gap for a publishable package.
    Remediation: Add a README.md following the pattern in scorecard-backend-module-sonarqube/README.md.

Low

  • [api-contract] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/metricProviders/CodecovConfig.ts:109 — The coverage_trend metric is described as "Code coverage trend for the last 7 days" but maps to the totals.diff field, which represents diff coverage (coverage of changed lines), not a time-based trend. The metric label and description are misaligned with the underlying data.

  • [edge-case] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/metricProviders/CodecovConfig.ts:127 — Threshold rules for partial_lines and missed_lines use absolute counts (<10, 10-50, >50) which may not scale well across repos of different sizes.

  • [error-handling-gaps] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/metricProviders/CodecovMetricProvider.ts:89calculateMetric and calculateMetrics have no additional error handling around the API call. While CodecovClient throws descriptive errors on non-OK responses (matching the SonarQube pattern), adding the entity ref and metric ID to error context would improve debuggability.

  • [missing-artifact] workspaces/scorecard/.changeset — No changeset entry exists for the new package.

  • [test-inadequate] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/metricProviders/CodecovMetricProvider.test.ts — Tests do not cover error/edge-case paths: API failure during calculateMetric, totals being null in the API response, or entity with codecov.io/account pointing to a non-existent account. (Note: annotation resolution errors are covered in CodecovConfig.test.ts.)

Info

  • [logic-error] workspaces/scorecard/plugins/scorecard-backend-module-codecov/src/metricProviders/CodecovConfig.ts:103 — The type Record<CodecovMetricId, keyof CodecovTotals | 'diff'> has a redundant union — 'diff' is already a member of keyof CodecovTotals.

  • [scope-alignment] The PR scope aligns well with issue Create a new codecov module for scorecard #3473. All 7 required metrics, annotation-based entity resolution, multi-account configuration, and single API call reuse are implemented as specified. No scope creep detected.

@fullsend-ai-review fullsend-ai-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the review comment for full details.

updatestamp: string;
author: CodecovAuthor;
language: string;
branch: string;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[high] nil/null handling

The CodecovRepoResponse.totals field is typed as non-optional CodecovTotals, but the Codecov API returns totals: null for repositories that have no coverage data uploaded yet. Both calculateMetric and calculateMetrics access repoInfo.totals[field] without any null check, which will throw a TypeError at runtime.

Suggested fix: Change the type to totals: CodecovTotals | null and add a null guard in both calculateMetric and calculateMetrics.

async getRepoInfo(
service: string,
owner: string,
repo: string,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[medium] error-handling-idiom

When a specific account name is requested but not found in configuration, the CodecovClient only logs a warning and returns undefined, silently falling through to an unauthenticated request. The SonarQube module throws an Error in the same scenario.

Suggested fix: Throw an Error when a specific accountName is provided but not found in configuration, matching the SonarQube pattern.

{ id: string; title: string; description: string }
> = {
coverage: {
id: 'codecov.coverage',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] api-contract

The coverage_trend metric is described as Code coverage trend for the last 7 days but maps to the totals.diff field which represents diff coverage, not a time-based trend.

id: 'codecov.tracked_lines',
title: 'Codecov Tracked Lines',
description: 'Total lines of code tracked by Codecov.',
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] edge-case

Threshold rules for partial_lines and missed_lines use absolute counts which may not scale well across repos of different sizes.

getCatalogFilter(): Record<string, string | symbol | (string | symbol)[]> {
return {
[`metadata.annotations.${CODECOV_REPO_ANNOTATION}`]:
CATALOG_FILTER_EXISTS,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] error-handling-gaps

calculateMetric and calculateMetrics have no additional error context (entity ref, metric ID) around the API call.

@christoph-jerolimov

Copy link
Copy Markdown
Member

/fs-fix regenerate the api reports and commit the changes (or new files)

@fullsend-ai-coder

fullsend-ai-coder Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 6:45 AM UTC · Completed 6:58 AM UTC
Commit: c0783eb · View workflow run →

@fullsend-ai-coder fullsend-ai-coder Bot force-pushed the fs/3473-scorecard-codecov-module branch from 3cbc239 to 7669647 Compare June 20, 2026 06:45
fullsend-ai-coder Bot added a commit that referenced this pull request Jun 20, 2026
Add the missing report.api.md for the new
scorecard-backend-module-codecov plugin.

Addresses review feedback on #3477
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 1 (human-triggered)

Generated the missing API report file (report.api.md) for the new codecov backend module plugin.

Fixed (1):

  1. missing API report for codecov module: Generated report.api.md for the new scorecard-backend-module-codecov plugin by running 'yarn build:api-reports' in the scorecard workspace

Tests: passed

Updated by fullsend fix agent

@fullsend-ai-review

fullsend-ai-review Bot commented Jun 20, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 7:01 AM UTC · Completed 7:14 AM UTC
Commit: c0783eb · View workflow run →

@fullsend-ai-review fullsend-ai-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the review comment for full details.

private constructor() {}

static fromConfig(config: Config, logger: LoggerService): MetricProvider[] {
return CODECOV_METRICS.map(metricId =>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[medium] efficiency

The factory creates 7 separate CodecovMetricProvider instances, each constructing its own CodecovClient. If the framework calls calculateMetric on individual providers (non-batch path), 7 separate HTTP requests will be made for the same repository data.

Suggested fix: Create a single CodecovClient instance in the factory and share it across all 7 providers.

if (account) {
return account.getOptionalString('authToken');
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[medium] fail-open

When an entity codecov.io/account annotation references an account name not present in the config, resolveAuthToken() silently returns undefined and the request proceeds without authentication, silently degrading from authenticated to unauthenticated access.

Suggested fix: When an explicit accountName is provided but no matching account is found, throw an error rather than silently falling back to unauthenticated requests.

},
covered_lines: {
id: 'codecov.covered_lines',
title: 'Codecov Covered Lines',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] API-contract-accuracy

The coverage_trend metric description says Code coverage trend for the last 7 days but maps to the diff field, which represents coverage diff from the most recent comparison, not a 7-day window.

* Represents the full response from the Codecov repos_retrieve API.
* @see https://docs.codecov.com/reference/repos_retrieve
*/
export interface CodecovRepoResponse {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] null-handling

The coverage field in CodecovTotals is typed as number, but the Codecov API may return coverage: null when there are tracked files but no actual coverage measurements.

if (serviceAnnotation) {
service = serviceAnnotation;
} else if (entity.metadata.annotations?.[GITHUB_PROJECT_SLUG_ANNOTATION]) {
service = 'github';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] insufficient-input-validation

The service value from the codecov.io/service annotation is used in the API URL without validation against known Codecov services. While encodeURIComponent prevents URL manipulation, allowlist validation would be defense-in-depth.

* The default account name to use when the codecov.io/account annotation is not set.
* Defaults to "default".
* @visibility frontend
*/

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] scope-alignment

The defaultAccount field is marked @visibility frontend, but this is a backend-only module with no frontend counterpart.

@christoph-jerolimov

Copy link
Copy Markdown
Member

/fs-fix

Add a readme, with installation instructions, notes on the necessary annotation and table with all metrics
change the package.json version to 0.0.0
add a changeset

@fullsend-ai-coder

fullsend-ai-coder Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 8:03 AM UTC · Completed 8:07 AM UTC
Commit: c3c2966 · View workflow run →

fullsend-ai-coder Bot and others added 2 commits June 20, 2026 08:03
Add a new scorecard-backend-module-codecov plugin that integrates with the
Codecov API to provide 7 code coverage metrics: coverage percentage,
coverage trend, tracked files, tracked lines, covered lines, partial lines,
and missed lines. The module supports multi-account configuration with
optional auth tokens and resolves service/owner/repo from entity annotations
with fallback logic. All 7 metrics share a single API call via the batch
provider pattern.

Closes #3473

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add the missing report.api.md for the new
scorecard-backend-module-codecov plugin.

Addresses review feedback on #3477
@fullsend-ai-coder fullsend-ai-coder Bot force-pushed the fs/3473-scorecard-codecov-module branch from baa2e8b to 331e7e5 Compare June 20, 2026 08:03
…odule

Add README.md with installation instructions, entity annotation
documentation, metrics table, and multi-account configuration guide.
Set package.json version to 0.0.0 for initial release.
Add changeset for the new codecov module.

Addresses review feedback on #3477
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 2 (human-triggered)

Addressed all 3 human-requested changes: added comprehensive README with metrics table, installation instructions, and annotation docs; set package version to 0.0.0; added changeset.

Fixed (3):

  1. Add a README with installation instructions, annotation notes, and metrics table (workspaces/scorecard/plugins/scorecard-backend-module-codecov/README.md): Created README.md with installation instructions, entity annotation documentation (codecov.io/repo, codecov.io/service, codecov.io/owner, codecov.io/account), metrics table for all 7 metrics, and multi-account configuration guide.
  2. Change package.json version to 0.0.0 (workspaces/scorecard/plugins/scorecard-backend-module-codecov/package.json): Changed version from 0.1.0 to 0.0.0 for initial release.
  3. Add a changeset (workspaces/scorecard/.changeset/add-codecov-module.md): Added changeset for the new codecov backend module as a minor change.

Tests: passed

Updated by fullsend fix agent

@fullsend-ai-review

fullsend-ai-review Bot commented Jun 20, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 8:09 AM UTC · Completed 8:18 AM UTC
Commit: c3c2966 · View workflow run →

@fullsend-ai-review fullsend-ai-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the review comment for full details.

const { service, owner, repo, accountName } =
resolveCodecovEntityInfo(entity);
const repoInfo = await this.client.getRepoInfo(
service,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[high] nil-deref

Both calculateMetric and calculateMetrics access repoInfo.totals[field] without null-checking totals. The Codecov repos_retrieve API returns totals: null for repositories without coverage uploads. At runtime repoInfo.totals will be null and property access will throw TypeError.

Suggested fix: Change the totals field in CodecovRepoResponse to totals: CodecovTotals | null, and add a null guard in both calculateMetric and calculateMetrics.

/**
* Maps scorecard metric IDs to the field in the Codecov API totals response.
*/
export const CODECOV_TOTALS_FIELD_MAP: Record<

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[medium] api-contract

The coverage_trend metric is described as Code coverage trend for the last 7 days but maps to totals.diff which represents the coverage diff of the most recent commit, not a 7-day trend.

Suggested fix: Update the description to accurately reflect what totals.diff represents or use a different Codecov API endpoint for time-series data.

`Codecov API error: ${response.status} ${response.statusText} for ${url}`,
);
}
return response.json() as Promise<CodecovRepoResponse>;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] error-handling

getRepoInfo calls response.json() without a try-catch. A 200 response with non-JSON body will produce a confusing error.

Suggested fix: Wrap response.json() in a try-catch and throw a descriptive error.

@@ -0,0 +1,262 @@
/*

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] test-inadequate

No test coverage for null totals response or resolveCodecovEntityInfo throwing. Tests only exercise the happy path.

id: 'codecov.coverage_trend',
title: 'Codecov Coverage Trend (7d)',
description: 'Code coverage trend for the last 7 days.',
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[info] edge-case

CODECOV_TOTALS_FIELD_MAP type includes redundant diff union since diff is already a key of CodecovTotals.

@christoph-jerolimov

Copy link
Copy Markdown
Member

/fs-fix run prettier 😆

@fullsend-ai-coder

fullsend-ai-coder Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 8:49 AM UTC · Completed 8:59 AM UTC
Commit: c3c2966 · View workflow run →

Format index.ts and README.md with prettier to fix formatting
inconsistencies (import line wrapping, markdown table alignment).

Addresses review feedback on #3477
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 3 (human-triggered)

Ran prettier on all PR-changed files per human instruction. 2 files were reformatted: packages/backend/src/index.ts and plugins/scorecard-backend-module-codecov/README.md.

Fixed (1):

  1. run prettier on PR files: Ran prettier on all PR-changed files. Two files were reformatted: index.ts (collapsed multi-line import statements to single lines) and README.md (fixed markdown table column alignment). All other files were already formatted correctly.

Tests: passed

Updated by fullsend fix agent

@sonarqubecloud

Copy link
Copy Markdown

@fullsend-ai-review

fullsend-ai-review Bot commented Jun 20, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:03 AM UTC · Completed 9:14 AM UTC
Commit: c3c2966 · View workflow run →

@fullsend-ai-review fullsend-ai-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the review comment for full details.

/**
* Represents the full response from the Codecov repos_retrieve API.
* @see https://docs.codecov.com/reference/repos_retrieve
*/

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[high] nil/null handling

The totals field in CodecovRepoResponse is typed as non-nullable CodecovTotals, but the Codecov repos_retrieve API can return totals: null for repositories that exist but have never uploaded coverage data. When totals is null, both calculateMetric and calculateMetrics in CodecovMetricProvider will crash with Cannot read properties of null when accessing repoInfo.totals[field].

Suggested fix: Change the type to totals: CodecovTotals | null and add a null check in CodecovMetricProvider.calculateMetric and calculateMetrics before accessing totals fields. Return a sensible default (e.g., 0) or throw a descriptive error when totals is null.

tracked_files: {
id: 'codecov.tracked_files',
title: 'Codecov Tracked Files',
description: 'Number of files tracked by Codecov.',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[medium] API behavior claims

The coverage_trend metric is described as 'Code coverage trend for the last 7 days' and mapped to totals.diff. However, the Codecov repos_retrieve API's totals.diff field represents the diff coverage of the latest commit/comparison, not a 7-day rolling trend. The README and the metric description both make this incorrect claim.

Suggested fix: Update the description to accurately reflect what totals.diff represents (e.g., 'Diff coverage for the latest comparison'), or use a different Codecov API endpoint that actually provides a time-based trend.

const repoInfo = await this.client.getRepoInfo(
service,
owner,
repo,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] error handling gaps

If the Codecov API returns a response where specific totals fields are null, the returned value will be null cast as number, silently passing invalid data downstream. This is largely addressed by the null-handling fix for CodecovRepoResponse.totals.

coverage: 'coverage',
coverage_trend: 'diff',
tracked_files: 'files',
tracked_lines: 'lines',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] edge cases

The threshold rules for partial_lines and missed_lines use absolute numeric boundaries (<10, 10-50, >50) that are not meaningful as universal defaults. A large project would reasonably have more than 50 missed lines even with excellent coverage.

},
covered_lines: {
id: 'codecov.covered_lines',
title: 'Codecov Covered Lines',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] logic errors

The type of CODECOV_TOTALS_FIELD_MAP includes | 'diff' but diff is already a member of keyof CodecovTotals. The redundant union can be simplified.

@@ -0,0 +1,262 @@
/*

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] test adequacy

Tests do not cover the case where the Codecov API returns totals: null or where individual fields within totals are null — the most likely failure scenarios in production.

this.logger = logger.child({ component: 'CodecovClient' });
}

private resolveAuthToken(accountName?: string): string | undefined {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] Fail-Open Authentication

When a specific account name is requested via annotation but not found in config, resolveAuthToken() returns undefined and the API call proceeds without authentication. A warning is logged, but the silent fallback could mask configuration errors for private repos.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create a new codecov module for scorecard

4 participants