Skip to content

feat(scorecard): Add SonarQube metric providers#2576

Merged
christoph-jerolimov merged 25 commits intoredhat-developer:mainfrom
christoph-jerolimov:scorecard/sonarqube
May 4, 2026
Merged

feat(scorecard): Add SonarQube metric providers#2576
christoph-jerolimov merged 25 commits intoredhat-developer:mainfrom
christoph-jerolimov:scorecard/sonarqube

Conversation

@christoph-jerolimov
Copy link
Copy Markdown
Member

@christoph-jerolimov christoph-jerolimov commented Mar 19, 2026

Hey, I just made a Pull Request!

New module with new metrics from sonarqube/sonarcloud; agentic-eng. by @imykhno, myself and Claude. 😀

image

TODOs

✔️ 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)

@rhdh-gh-app
Copy link
Copy Markdown

rhdh-gh-app Bot commented Mar 19, 2026

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
app-legacy workspaces/scorecard/packages/app-legacy none v0.0.0
app workspaces/scorecard/packages/app none v0.0.0
backend workspaces/scorecard/packages/backend none v0.0.0
@red-hat-developer-hub/backstage-plugin-scorecard-backend-module-sonarqube workspaces/scorecard/plugins/scorecard-backend-module-sonarqube minor v0.0.0
@red-hat-developer-hub/backstage-plugin-scorecard workspaces/scorecard/plugins/scorecard patch v2.7.1

@rhdh-qodo-merge
Copy link
Copy Markdown

Review Summary by Qodo

Add SonarQube metric providers for scorecard with multi-instance support

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add SonarQube backend module with four metric providers
  - Quality gate status (boolean)
  - Open issues count (number)
  - Security rating (number, A=1 to E=5)
  - Security issues/vulnerabilities count (number)
• Support multiple SonarQube instances with named configuration
• Implement SonarQubeClient with Basic/Bearer auth and base64 encoding
• Include comprehensive unit tests and configuration schema
• Add README documentation and example catalog entity
Diagram
flowchart LR
  Config["Config Schema<br/>config.d.ts"]
  Client["SonarQubeClient<br/>API Integration"]
  BoolProvider["BooleanMetricProvider<br/>Quality Gate"]
  NumProvider["NumberMetricProvider<br/>Issues/Rating"]
  Factory["MetricProviderFactory<br/>Provider Creation"]
  Module["Backend Module<br/>Registration"]
  
  Config -->|"Instance Config"| Client
  Client -->|"API Calls"| BoolProvider
  Client -->|"API Calls"| NumProvider
  Factory -->|"Creates"| BoolProvider
  Factory -->|"Creates"| NumProvider
  Module -->|"Registers"| Factory
Loading

Grey Divider

File Changes

1. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/config.d.ts ⚙️ Configuration changes +77/-0

TypeScript configuration schema for SonarQube

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/config.d.ts


2. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts ✨ Enhancement +141/-0

SonarQube API client with multi-instance support

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts


3. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.test.ts 🧪 Tests +289/-0

Comprehensive unit tests for SonarQubeClient

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.test.ts


View more (19)
4. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeBooleanMetricProvider.ts ✨ Enhancement +97/-0

Boolean metric provider for quality gate status

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeBooleanMetricProvider.ts


5. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeBooleanMetricProvider.test.ts 🧪 Tests +189/-0

Unit tests for boolean metric provider

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeBooleanMetricProvider.test.ts


6. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeNumberMetricProvider.ts ✨ Enhancement +134/-0

Number metric provider for issues and ratings

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeNumberMetricProvider.ts


7. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeNumberMetricProvider.test.ts 🧪 Tests +245/-0

Unit tests for number metric provider

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeNumberMetricProvider.test.ts


8. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeConfig.ts ✨ Enhancement +127/-0

Metric configuration and annotation parsing utilities

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeConfig.ts


9. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeMetricProviderFactory.ts ✨ Enhancement +51/-0

Factory for creating metric provider instances

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeMetricProviderFactory.ts


10. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeMetricProviderFactory.test.ts 🧪 Tests +94/-0

Unit tests for metric provider factory

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeMetricProviderFactory.test.ts


11. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/module.ts ✨ Enhancement +42/-0

Backend module registration and initialization

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/module.ts


12. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/index.ts ✨ Enhancement +23/-0

Module entry point and public exports

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/index.ts


13. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/README.md 📝 Documentation +79/-0

Documentation for SonarQube module usage

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/README.md


14. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/package.json ⚙️ Configuration changes +44/-0

Package configuration and dependencies

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/package.json


15. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/.eslintrc.js ⚙️ Configuration changes +1/-0

ESLint configuration for module

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/.eslintrc.js


16. workspaces/scorecard/packages/backend/src/index.ts ✨ Enhancement +5/-0

Register SonarQube module in backend

workspaces/scorecard/packages/backend/src/index.ts


17. workspaces/scorecard/packages/backend/package.json Dependencies +1/-0

Add SonarQube module dependency

workspaces/scorecard/packages/backend/package.json


18. workspaces/scorecard/app-config.yaml ⚙️ Configuration changes +7/-2

Add SonarQube config example and adjust schedules

workspaces/scorecard/app-config.yaml


19. workspaces/scorecard/examples/all-scorecards-location.yaml 📝 Documentation +1/-0

Add SonarQube example component to location

workspaces/scorecard/examples/all-scorecards-location.yaml


20. workspaces/scorecard/examples/components/sonarqube-scorecard-only.yaml 📝 Documentation +12/-0

Example component with SonarQube annotation

workspaces/scorecard/examples/components/sonarqube-scorecard-only.yaml


21. workspaces/scorecard/.changeset/sonarqube-metric-providers.md 📝 Documentation +5/-0

Changeset for metric providers feature

workspaces/scorecard/.changeset/sonarqube-metric-providers.md


22. workspaces/scorecard/.changeset/sonarqube-basic-auth-encoding.md 📝 Documentation +5/-0

Changeset for Basic auth fix

workspaces/scorecard/.changeset/sonarqube-basic-auth-encoding.md


Grey Divider

Qodo Logo

@rhdh-qodo-merge
Copy link
Copy Markdown

rhdh-qodo-merge Bot commented Mar 19, 2026

Code Review by Qodo

🐞 Bugs (5) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Instance apiKey wrongly required 🐞 Bug ≡ Correctness
Description
The config schema requires sonarqube.instances[].apiKey, but the README/tests/implementation
support instances without an apiKey for public projects. This mismatch will cause config schema
validation to reject documented configurations.
Code

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/config.d.ts[R63-68]

+      /**
+       * The api key to access the sonarqube instance.
+       * @visibility secret
+       */
+      apiKey: string;
+
Evidence
The schema declares apiKey required for each instance, but the README includes an instance without
apiKey, tests configure an instance without apiKey, and the runtime code reads it via
getOptionalString('apiKey').

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/config.d.ts[39-75]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/README.md[65-79]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.test.ts[206-223]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.test.ts[273-287]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts[46-55]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`config.d.ts` requires `sonarqube.instances[].apiKey`, but the implementation and documentation allow instances without an apiKey (for public projects). This will cause Backstage config schema validation to fail for valid configs.

### Issue Context
README and tests demonstrate an instance without `apiKey`.

### Fix Focus Areas
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/config.d.ts[39-75]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/README.md[65-79]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.test.ts[206-223]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts[46-55]

### Proposed fix
- Change `instances[].apiKey: string` to `instances[].apiKey?: string`.
- Update the JSDoc comment to state apiKey is optional for public projects (matching top-level `apiKey?: string`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Instance prefix ignored silently 🐞 Bug ≡ Correctness
Description
If an entity uses an instance-prefixed project key (e.g. internal/my-project) but the sonarqube
config block is missing, resolveInstance ignores the instanceName and falls back to the default
instance. This can query the wrong SonarQube/SonarCloud instance without any explicit error about
the missing instance configuration.
Code

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts[R37-69]

+  private resolveInstance(instanceName?: string): SonarQubeInstance {
+    const sonarqubeConfig = this.config.getOptionalConfig('sonarqube');
+
+    if (instanceName && sonarqubeConfig) {
+      const instances =
+        sonarqubeConfig.getOptionalConfigArray('instances') ?? [];
+      const instance = instances.find(
+        i => i.getString('name') === instanceName,
+      );
+      if (instance) {
+        return {
+          baseUrl: instance.getString('baseUrl').replace(/\/$/, ''),
+          apiKey: inst*********************g('apiKey'),
+          authType:
+            (instance.getOptionalString('authType') as
+              | 'Bearer'
+              | 'Basic'
+              | undefined) ?? 'Basic',
+        };
+      }
+      throw new Error(
+        `SonarQube instance '${instanceName}' not found in configuration`,
+      );
+    }
+
+    return {
+      baseUrl: (
+        sonarqubeConfig?.getOptionalString('baseUrl') ?? DEFAULT_BASE_URL
+      ).replace(/\/$/, ''),
+      apiKey: sona*****************************g('apiKey'),
+      authType: 'Basic',
+    };
+  }
Evidence
Metric providers always pass instanceName when the annotation contains a prefix, but
resolveInstance only honors instanceName when sonarqubeConfig exists. When sonarqubeConfig
is undefined, the function returns the default instance configuration and ignores the provided
instanceName.

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeBooleanMetricProvider.ts[84-96]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeNumberMetricProvider.ts[108-119]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts[37-69]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeConfig.ts[32-43]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`SonarQubeClient.resolveInstance(instanceName)` ignores a provided `instanceName` when the `sonarqube` config block is missing, silently falling back to the default instance. This can cause requests to go to the wrong SonarQube/SonarCloud instance.

### Issue Context
Instance names come from entity annotations (`sonarqube.org/project-key`) via `parseProjectKeyAnnotation`, and metric providers pass `instanceName` through to the client.

### Fix Focus Areas
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts[37-69]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeBooleanMetricProvider.ts[84-96]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeConfig.ts[32-43]

### Proposed fix
- If `instanceName` is provided but `sonarqubeConfig` is missing, throw a clear error like:
 `SonarQube instance '<name>' requested but no 'sonarqube' configuration found`.
- (Optional) Add a unit test covering `instanceName` + empty root config to prevent regressions.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Missing measures return undefined 🐞 Bug ☼ Reliability
Description
SonarQubeNumberMetricProvider returns measures.security_rating/measures.vulnerabilities
without verifying they exist in the response. If SonarQube omits a measure, calculateMetric
returns undefined, violating the MetricProvider contract and resulting in null/undefined metric
values stored without an error.
Code

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeNumberMetricProvider.ts[R114-129]

+      case 'security_rating': {
+        const measures = await this.client.getMeasures(
+          projectKey,
+          SONARQUBE_API_METRIC_KEYS.security_rating,
+          instanceName,
+        );
+        return measures.security_rating;
+      }
+      case 'security_issues': {
+        const measures = await this.client.getMeasures(
+          projectKey,
+          SONARQUBE_API_METRIC_KEYS.security_issues,
+          instanceName,
+        );
+        return measures.vulnerabilities;
+      }
Evidence
The MetricProvider interface requires calculateMetric to return a MetricValue (for number
metrics: a number), but the client only populates keys present in data.component.measures. The
provider then directly returns a possibly-missing key from the measures record.

workspaces/scorecard/plugins/scorecard-node/src/api/MetricProvider.ts[25-65]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts[120-140]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeNumberMetricProvider.ts[114-129]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`SonarQubeNumberMetricProvider.calculateMetric` can return `undefined` when a requested measure is missing from the SonarQube API response (e.g., `security_rating` or `vulnerabilities` not present). This violates the `MetricProvider` contract (must return a number) and leads to silent null/undefined values being stored.

### Issue Context
`SonarQubeClient.getMeasures` only adds metrics that appear in `data.component.measures`, and the provider accesses keys directly.

### Fix Focus Areas
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts[120-140]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeNumberMetricProvider.ts[114-129]
- workspaces/scorecard/plugins/scorecard-node/src/api/MetricProvider.ts[25-65]

### Proposed fix
Choose one consistent behavior:
- **Preferred (fail fast):** After fetching measures, check for the requested key(s); if missing, throw an Error like `Missing SonarQube measure 'security_rating' for project '<key>'` so the scheduler stores `error_message`.
- **Alternative (default value):** Return `0` when a measure is absent, if that better matches scorecard semantics.

Also consider guarding against `data.component?.measures` being absent and throwing a clearer error message.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
4. Package marked private🐞 Bug ⚙ Maintainability
Description
The new module package is set to "private": true, which prevents publishing even though this PR adds
changesets for that package. This will break release automation/consumption of the new module as a
published package.
Code

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/package.json[5]

+  "private": true,
Evidence
The sonarqube module is explicitly private, while the PR adds changesets for it (indicating intent
to release/publish) and other scorecard backend modules are not private.

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/package.json[1-18]
workspaces/scorecard/.changeset/sonarqube-metric-providers.md[1-5]
workspaces/scorecard/.changeset/sonarqube-basic-auth-encoding.md[1-5]
workspaces/scorecard/plugins/scorecard-backend-module-dependabot/package.json[1-23]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`@red-hat-developer-hub/backstage-plugin-scorecard-backend-module-sonarqube` is marked `"private": true`, which prevents publishing even though the PR adds changesets for it.

### Issue Context
Other scorecard backend modules (e.g. dependabot) are publishable and include standard Backstage package metadata.

### Fix Focus Areas
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/package.json[1-18]
- workspaces/scorecard/.changeset/sonarqube-metric-providers.md[1-5]
- workspaces/scorecard/.changeset/sonarqube-basic-auth-encoding.md[1-5]
- workspaces/scorecard/plugins/scorecard-backend-module-dependabot/package.json[1-23]

### Proposed fix
- Remove `"private": true`.
- (Optional but recommended for consistency) add `backstage.pluginPackage` and `repository` metadata similarly to other modules.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

5. Invalid annotation not rejected 🐞 Bug ≡ Correctness
Description
parseProjectKeyAnnotation accepts values like internal/ or /my-project, producing an empty
instanceName or projectKey. This leads to confusing downstream API requests (e.g. projectKey=)
instead of a clear validation error at annotation parsing time.
Code

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeConfig.ts[R32-42]

+export function parseProjectKeyAnnotation(
+  annotation: string,
+): SonarQubeAnnotation {
+  const slashIndex = annotation.indexOf('/');
+  if (slashIndex === -1) {
+    return { projectKey: annotation };
+  }
+  return {
+    instanceName: annotation.substring(0, slashIndex),
+    projectKey: annotation.substring(slashIndex + 1),
+  };
Evidence
The parsing logic splits on the first '/', but never validates that the substring before/after the
slash is non-empty. Other providers in this repo validate annotation format and throw early when
invalid (example: Dependabot project-slug parsing).

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeConfig.ts[32-43]
workspaces/scorecard/plugins/scorecard-backend-module-dependabot/src/metricProviders/DependabotMetricProvider.ts[94-113]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`parseProjectKeyAnnotation` does not validate empty parts around `/`, allowing invalid annotations like `internal/` or `/my-project`. This pushes failures to later API calls and makes errors harder to diagnose.

### Issue Context
The annotation is required for these providers and is user-authored, so early validation produces better error messages.

### Fix Focus Areas
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeConfig.ts[32-43]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeBooleanMetricProvider.ts[84-96]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeNumberMetricProvider.ts[92-106]

### Proposed fix
- Trim the annotation string.
- If a `/` is present, ensure both `instanceName` and `projectKey` are non-empty; otherwise throw an Error like:
 `Invalid 'sonarqube.org/project-key' annotation '<value>' (expected '<instance>/<projectKey>' or '<projectKey>')`.
- Add unit tests for `internal/`, `/my-project`, and `internal/my-project`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Config example uses wrong key 🐞 Bug ⚙ Maintainability
Description
The repo app-config example documents sonarqube.token, but the implementation and schema use
sonarqube.apiKey. Users copying this example will configure auth incorrectly and end up sending
unauthenticated requests.
Code

workspaces/scorecard/app-config.yaml[R121-125]

+# SonarQube configuration (optional - defaults to https://sonarcloud.io, no auth for public projects)
+# sonarqube:
+#   baseUrl: https://sonarcloud.io
+#   token: ${SONARQUBE_TOKEN}
+
Evidence
The app-config example uses token, while both the config schema and README define the field as
apiKey. The runtime client reads apiKey from config and will ignore token.

workspaces/scorecard/app-config.yaml[121-125]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/config.d.ts[33-38]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/README.md[58-63]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts[62-68]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The example configuration in `workspaces/scorecard/app-config.yaml` documents `sonarqube.token`, but the module reads `sonarqube.apiKey`. This can cause incorrect configurations when users copy/paste the example.

### Issue Context
Both `config.d.ts` and the module README document the key as `apiKey`.

### Fix Focus Areas
- workspaces/scorecard/app-config.yaml[121-125]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/config.d.ts[33-38]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/README.md[58-63]

### Proposed fix
- Update the commented example to:
 `apiKey: ${SO***************}` (or `${SONARQUBE_TOKEN}` if you prefer, but keep the config key name `apiKey`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/package.json Outdated
@christoph-jerolimov christoph-jerolimov changed the title feat(scorecard): Add SonarQube metric providers for quality gate status, open issues, security rating, and security issues feat(scorecard): Add SonarQube metric providers Mar 19, 2026
Copy link
Copy Markdown
Contributor

@HusneShabbir HusneShabbir left a comment

Choose a reason for hiding this comment

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

@christoph-jerolimov this will unblock your CI

Comment thread workspaces/scorecard/packages/app/e2e-tests/scorecard.test.ts Outdated
@Eswaraiahsapram
Copy link
Copy Markdown
Member

I think we can implement a similar threshold configuration logic used in the Github and Jira modules here

Comment thread workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/config.d.ts Outdated
Comment thread workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/config.d.ts Outdated
@github-actions
Copy link
Copy Markdown
Contributor

This PR has been automatically marked as stale because it has not had recent activity from the author. It will be closed if no further activity occurs. If the PR was closed and you want it re-opened, let us know and we'll re-open the PR so that you can continue the contribution!

@github-actions github-actions Bot added the stale label Apr 16, 2026
@imykhno imykhno removed the stale label Apr 17, 2026
christoph-jerolimov and others added 7 commits April 20, 2026 13:42
Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
…ues, and security

Add four metric providers to the scorecard-backend-module-sonarqube plugin:
- Quality gate status (boolean)
- Open issues count (number)
- Security rating (number, A=1 to E=5)
- Security issues/vulnerabilities count (number)

Includes SonarQubeClient, config, factory, example catalog entity, and unit tests.
SonarQube baseUrl defaults to https://sonarcloud.io; token is optional for public projects.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
…config schema

- Add config.d.ts with typed config schema supporting default + named instances
- Refactor SonarQubeClient to resolve instance by name from sonarqube.instances[]
- Parse sonarqube.org/project-key annotation for optional instance prefix (instance/project-key)
- Use apiKey + authType (Basic/Bearer) from config.d.ts instead of token
- Falls back to default instance when no instance prefix in annotation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
SonarQube expects Basic auth as base64(apiKey:) with an appended colon.
Bearer auth passes the apiKey directly without encoding.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
…blic

Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
…ror thresholds for security rating

Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
@christoph-jerolimov christoph-jerolimov force-pushed the scorecard/sonarqube branch 2 times, most recently from cb1a874 to 9027089 Compare April 20, 2026 23:41
christoph-jerolimov and others added 2 commits April 21, 2026 01:44
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
…are longer then one line

Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
@christoph-jerolimov
Copy link
Copy Markdown
Member Author

@imykhno, I've addressed your comments:

  1. removed unused externalBaseUrl
  2. used fromConfig pattern

I've also changed:

  1. changed the threshold rules for the A-E ratings so that 5 thresholds are shown
  2. added translations
  3. added a tooltip to the card because some translations are really long
image image

Please take a look. :)

Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
3.4% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 30, 2026

Codecov Report

❌ Patch coverage is 91.21622% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 60.63%. Comparing base (0b28051) to head (6b4da98).
⚠️ Report is 6 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2576      +/-   ##
==========================================
+ Coverage   60.56%   60.63%   +0.07%     
==========================================
  Files        2005     2012       +7     
  Lines       62790    62938     +148     
  Branches    16453    16473      +20     
==========================================
+ Hits        38026    38161     +135     
- Misses      23602    23615      +13     
  Partials     1162     1162              
Flag Coverage Δ *Carryforward flag
adoption-insights 83.58% <ø> (ø) Carriedforward from edccef5
app-defaults 69.60% <ø> (ø) Carriedforward from edccef5
augment 69.36% <ø> (ø) Carriedforward from edccef5
bulk-import 72.57% <ø> (ø) Carriedforward from edccef5
cost-management 16.49% <ø> (ø) Carriedforward from edccef5
dcm 32.85% <ø> (ø) Carriedforward from edccef5
extensions 61.42% <ø> (ø) Carriedforward from edccef5
global-floating-action-button 73.75% <ø> (ø) Carriedforward from edccef5
global-header 61.56% <ø> (ø) Carriedforward from edccef5
homepage 50.84% <ø> (ø) Carriedforward from edccef5
konflux 91.01% <ø> (ø) Carriedforward from edccef5
lightspeed 69.64% <ø> (ø) Carriedforward from edccef5
mcp-integrations 81.59% <ø> (ø) Carriedforward from edccef5
orchestrator 33.08% <ø> (ø) Carriedforward from edccef5
quickstart 62.64% <ø> (ø) Carriedforward from edccef5
sandbox 79.56% <ø> (ø) Carriedforward from edccef5
scorecard 83.61% <91.21%> (+0.37%) ⬆️
theme 64.54% <ø> (ø) Carriedforward from edccef5
translations 8.49% <ø> (ø) Carriedforward from edccef5
x2a 82.09% <ø> (ø) Carriedforward from edccef5

*This pull request uses carry forward flags. Click here to find out more.


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0b28051...6b4da98. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

imykhno added 6 commits April 30, 2026 19:52
Signed-off-by: Ihor Mykhno <imykhno@redhat.com>
Signed-off-by: Ihor Mykhno <imykhno@redhat.com>
… tests

Signed-off-by: Ihor Mykhno <imykhno@redhat.com>
Signed-off-by: Ihor Mykhno <imykhno@redhat.com>
…ests

Signed-off-by: Ihor Mykhno <imykhno@redhat.com>
…update README

Signed-off-by: Ihor Mykhno <imykhno@redhat.com>
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 4, 2026

Copy link
Copy Markdown
Contributor

@imykhno imykhno left a comment

Choose a reason for hiding this comment

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

Thank you for implementing the new SonarQube module. I have tested it locally, and the new functionality works as expected (see the attached recording):

  • the uploaded data matches what is displayed on the source
  • tooltip displayed when text is too long
  • translation works for all supported languages
  • metric threshold is customizable form app-config.yaml files
Screen.Recording.2026-05-04.at.11.21.28.mov

/lgtm

@openshift-ci openshift-ci Bot added the lgtm label May 4, 2026
@christoph-jerolimov
Copy link
Copy Markdown
Member Author

Thanks @imykhno for the follow up and cleanups. I've tested the plugin one more time locally and it works fine.

Let us :shipit:

@christoph-jerolimov christoph-jerolimov merged commit 04e95fe into redhat-developer:main May 4, 2026
61 checks passed
@christoph-jerolimov christoph-jerolimov deleted the scorecard/sonarqube branch May 4, 2026 11:10
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.

4 participants