Skip to content

Fix Scorecard SonarQube "Open Issues" metric pulling - #4274

Closed
imykhno wants to merge 1 commit into
redhat-developer:mainfrom
imykhno:fix/scorecard-sonarqube-open-issues-metric
Closed

Fix Scorecard SonarQube "Open Issues" metric pulling#4274
imykhno wants to merge 1 commit into
redhat-developer:mainfrom
imykhno:fix/scorecard-sonarqube-open-issues-metric

Conversation

@imykhno

@imykhno imykhno commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Hey, I just made a Pull Request!

✔️ Checklist

  • A changeset describing the change and affected packages. (more info)
  • Added or Updated documentation
  • Tests for new functionality and regression tests for bug fixes
  • Screenshots attached (for UI changes)

Signed-off-by: Ihor Mykhno <imykhno@redhat.com>
@rhdh-gh-app

rhdh-gh-app Bot commented Aug 12, 2026

Copy link
Copy Markdown

Changed Packages

Package Name Package Path Changeset Bump Current Version
@red-hat-developer-hub/backstage-plugin-scorecard-backend-module-sonarqube workspaces/scorecard/plugins/scorecard-backend-module-sonarqube patch v1.0.2

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 12, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 3:43 PM UTC · Completed 3:59 PM UTC

Commit: 8aadb79 · View workflow run →

@sonarqubecloud

Copy link
Copy Markdown

@fullsend-ai-review

Copy link
Copy Markdown

Review

Findings

Medium

  • [error handling] workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts:121 — The bare catch block around the project-access check swallows all errors indiscriminately and replaces them with a misleading "project is not accessible" message. This catches not only HTTP 4xx responses (the intended case) but also: (1) configuration errors from resolveInstance (e.g., SonarQube instance 'unknown' not found in configuration would be masked), (2) network failures, (3) server errors (HTTP 500/503). For case (1), this is a behavioral regression — before this change, passing an invalid instanceName would throw a clear configuration error; now it throws a misleading "project not accessible" error. Additionally, sibling methods (getQualityGateStatus, getMeasures) let fetchApi errors propagate directly; the new try/catch pattern discards the original error context, diverging from established convention.
    Remediation: Narrow the catch to only handle the fetchApi HTTP error case. One approach: check if (error instanceof Error && error.message.includes('SonarQube API error')) and only rethrow the project-accessibility message for that case, re-throwing the original error otherwise.

  • [scope-creep] workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts:132 — The claimed intent is to handle inaccessible projects, but the change also silently alters return-value parsing from data.total to data.paging?.total ?? data.total ?? 0. This is a separate behavioral change not mentioned in the PR title, body, or changeset description.
    Remediation: Either revert the return expression to data.total if the pre-flight check alone solves the bug, or document why the parsing change was needed.

Low

  • [error handling / silent fallback] workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts:133data.paging?.total ?? data.total ?? 0 silently returns 0 when neither paging.total nor total is present in the API response. If the SonarQube API response structure changes unexpectedly, this will produce a silent 0 rather than a visible failure.

  • [test adequacy] workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.test.ts:113 — The test "throws when project access check fails" only tests the HTTP 404 case. There is no test covering the scenario where an invalid instanceName is passed to getOpenIssuesCount — this would reveal the regression where the configuration error ("instance not found") is now incorrectly masked as "project not accessible".

  • [indentation] workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts:113 — The method body of getOpenIssuesCount is indented at 6 spaces, while every other method in this class uses 4 spaces. This inconsistency was introduced by the diff.

  • [brace placement] workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts:123 — The catch keyword is placed on a new line after the closing brace. The standard JavaScript/TypeScript convention (and what prettier enforces) is same-line style (} catch {).

  • [missing-authorization] No linked issue. The diff modifies non-trivial behavior (adds a pre-flight API call and changes return-value parsing). Non-trivial changes should trace to an authorized issue so reviewers can verify scope.

  • [intent-clarity] workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts:113 — The pre-flight call to /api/components/show runs on every invocation, adding latency and load to the happy path. The changeset describes fixing an issue "when the project is inaccessible" but the implementation adds overhead to all calls.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR

@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.

}
catch {
throw new Error(
`SonarQube project '${projectKey}' is not accessible or the project key is missing or invalid`,

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

The bare catch block around the project-access check swallows all errors indiscriminately and replaces them with a misleading project is not accessible message. This catches not only HTTP 4xx responses (the intended case) but also: (1) configuration errors from resolveInstance (e.g., SonarQube instance unknown not found in configuration would be masked), (2) network failures, (3) server errors (HTTP 500/503). For case (1), this is a behavioral regression — before this change, passing an invalid instanceName would throw a clear configuration error; now it throws a misleading project not accessible error.

Suggested fix: Narrow the catch to only handle the fetchApi HTTP error case. Check if (error instanceof Error && error.message.includes(SonarQube API error)) and only rethrow the project-accessibility message for that case, re-throwing the original error otherwise.

instanceName,
);

return data.paging?.total ?? data.total ?? 0;

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] scope-creep

The claimed intent is to handle inaccessible projects, but the change also silently alters return-value parsing from data.total to data.paging?.total ?? data.total ?? 0. This is a separate behavioral change not mentioned in the PR title, body, or changeset description.

Suggested fix: Either revert the return expression to data.total if the pre-flight check alone solves the bug, or document why the parsing change was needed.

);

return data.paging?.total ?? data.total ?? 0;
}

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 / silent fallback

data.paging?.total ?? data.total ?? 0 silently returns 0 when neither paging.total nor total is present in the API response. If the SonarQube API response structure changes unexpectedly, this will produce a silent 0 rather than a visible failure.

'https://sonarcloud.io/api/issues/search?componentKeys=my-project&statuses=OPEN,CONFIRMED,REOPENED&ps=1',
expect.any(Object),
);
});

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

The test throws when project access check fails only tests the HTTP 404 case. There is no test covering the scenario where an invalid instanceName is passed to getOpenIssuesCount — this would reveal the regression where the configuration error (instance not found) is now incorrectly masked as project not accessible.

this.logger.debug(`Fetching open issues count for project ${projectKey}`);

// Additional check to ensure the project is accessible
try {

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] indentation

The method body of getOpenIssuesCount is indented at 6 spaces, while every other method in this class uses 4 spaces. This inconsistency was introduced by the diff.

Suggested fix: Re-indent the entire getOpenIssuesCount method body to use 4-space indentation, matching the sibling methods.

throw new Error(
`SonarQube project '${projectKey}' is not accessible or the project key is missing or invalid`,
);
}

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] brace placement

The catch keyword is placed on a new line after the closing brace. The standard JavaScript/TypeScript convention (and what prettier enforces) is same-line style (} catch {).

Suggested fix: Move catch onto the same line as the closing brace: } catch {.

this.logger.debug(`Fetching open issues count for project ${projectKey}`);

// Additional check to ensure the project is accessible
try {

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] intent-clarity

The pre-flight call to /api/components/show runs on every invocation, adding latency and load to the happy path. The changeset describes fixing an issue when the project is inaccessible but the implementation adds overhead to all calls.

@imykhno imykhno closed this Aug 13, 2026
@fullsend-ai-retro

fullsend-ai-retro Bot commented Aug 13, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ✅ Success · Started 1:51 PM UTC · Completed 2:05 PM UTC

Commit: 8aadb79 · View workflow run →

@fullsend-ai-retro

Copy link
Copy Markdown

Retro: PR #4274 — Fix Scorecard SonarQube "Open Issues" metric pulling

Timeline

  1. 2026-08-12T15:42Z — Human author (imykhno) opened PR #4274 adding a pre-flight project accessibility check to the SonarQube open-issues metric (3 files, ~100 lines). Labeled do-not-merge/work-in-progress.
  2. 15:43–15:59Z — Review agent ran (workflow 31613795716). Dispatched three sub-agents (correctness, style-conventions, intent-coherence) plus a challenger. Posted CHANGES_REQUESTED with 7 inline comments.
  3. 15:42Z — CI triggered (run 31613798496). Failed on prettier:check for SonarQubeClient.ts in both Node 22 and Node 24.
  4. 2026-08-13T13:50Z — PR closed without merge. No human reviews were submitted.

Review quality assessment

The review agent performed well. Its 2 medium-severity findings were substantive and actionable:

  • Bare catch block masking errors — correctly identified that the catch swallows all errors (including config errors and network failures) and replaces them with a misleading "project not accessible" message. This is a real behavioral regression.
  • Undocumented scope creep — caught that the return-value parsing was silently changed from data.total to data.paging?.total ?? data.total ?? 0 without mention in the PR description or changeset.

The 5 low-severity findings (silent fallback to 0, missing test for non-HTTP errors, indentation inconsistency, brace placement, missing linked issue) were all valid. The challenger appropriately merged one pair and downgraded two findings. No false positives were observed. Cost: $4.29, 28 turns — reasonable for the finding quality.

Evidence for existing issues

  • Sonnet model unavailability (agents#379, fullsend#1771): The style-conventions and intent-coherence sub-agents both failed on initial dispatch due to sonnet model unavailability and were retried with the default model. The fallback succeeded but added ~1 minute of overhead. This is the same pattern described in both existing issues.

  • Formatter-awareness gap (agents#493): The review agent flagged indentation inconsistency (6 vs 4 spaces) and non-standard brace placement as low-severity style findings. These same issues caused CI to fail on prettier:check. If the style sub-agent were formatter-aware (as chore(deps): bump @babel/runtime-corejs3 from 7.26.0 to 7.26.10 in /workspaces/orchestrator #493 proposes), it could have: (a) confirmed these are formatter violations rather than mere style preferences, and (b) noted they would block CI — elevating severity accordingly. This retro provides a complementary angle to chore(deps): bump @babel/runtime-corejs3 from 7.26.0 to 7.26.10 in /workspaces/orchestrator #493: beyond suppressing false positives when code matches the formatter, the sub-agent should also elevate findings that violate an enforced formatter.

Conclusion

The workflow performed well overall. The review agent caught real issues with appropriate severity, and the CI pipeline correctly blocked the formatting violation. No new proposals are warranted — the improvement opportunities identified are already tracked in existing open issues.

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.

1 participant