Skip to main content

SonarQube

The SonarQube integration plugin for Soundcheck supports the collection of the following facts:

Prerequisites

Configure SonarQube integration in Backstage

Integrations are configured at the root level of app-config.yaml. Here's an example configuration for SonarQube:

soundcheck:
collectors:
sonarqube:
token: ${SONARQUBE_TOKEN}

# Alternatively you may use username and password
# username: admin
# password: ${SONARQUBE_PASSWORD}

# If you wish to override the api URL
# baseUrl: https://sonarqube.com

Additionally, you can use multiple instances of SonarQube. When using multiple instances, the instance is determined by the sonarqube.org/project-key annotation from the entity configuration.

soundcheck:
collectors:
sonarqube:
- name: sonarqube-instance1
baseUrl: http://sonarqube-instance1.example.com
token: ${SONARQUBE_INSTANCE1_TOKEN}
- name: sonarqube-instance2
baseUrl: http://sonarqube-instance2.example.com
token: ${SONARQUBE_INSTANCE2_TOKEN}

Note: The configuration of multiple SonarQube instances is only supported in YAML at the moment.

Entity configuration

In your catalog-info.yaml file, add sonarqube.org/project-key metadata annotation to allow the plugin to map an entity to a project in SonarQube.

metadata:
annotations:
sonarqube.org/project-key: test-project-key

When using multiple SonarQube instances, make sure you sonarqube.org/project-key metadata annotation is prefixed with the corresponding SonarQube instance name configured at soundcheck.collectors.sonarqube.name:

metadata:
annotations:
sonarqube.org/project-key: sonarqube-instance1/test-project-key

When using SonarQube Cloud, add sonarqube.org/organization-key metadata annotation to allow the plugin to map an entity to an organization in SonarQube Cloud.

metadata:
annotations:
sonarqube.org/organization-key: test-organization-key
sonarqube.org/project-key: test-project-key

Permissions

The token used for authentication must have the necessary permissions to access the SonarQube API.

Listed below are the permissions required for each fact supported by this collector:

  • issues
    • Requires the 'Browse' permission on the specified project(s).
  • measures
    • Requires the 'Browse' permission on the specified project(s).
  • projects
    • Requires ‘Administer System’ permission.
  • project-tags
    • Requires the 'Browse' permission on the specified project(s).

Add the SonarQubeFactCollector to Soundcheck

First, add the package:

yarn workspace backend add @spotify/backstage-plugin-soundcheck-backend-module-sonarqube

Then add the following to your packages/backend/src/index.ts file:

packages/backend/src/index.ts
const backend = createBackend();

backend.add(import('@spotify/backstage-plugin-soundcheck-backend'));
backend.add(
import('@spotify/backstage-plugin-soundcheck-backend-module-sonarqube'),
);
// ...

backend.start();

Consult the Soundcheck Backend documentation for additional details on setting up the Soundcheck backend.

Plugin Configuration

The collection of SonarQube facts is driven by configuration. To learn more about the configuration, consult the Defining SonarQube Fact Collectors section.

SonarQube Fact Collector can be configured via YAML or No-Code UI. If you configure it via both YAML and No-Code UI, the configurations will be merged. It's preferable to choose a single source for the Fact Collectors configuration (either No-Code UI or YAML) to avoid confusing merge results.

No-Code UI Configuration Option

  1. Make sure the prerequisite Configure SonarQube integration in Backstage is completed and SonarQube instance details are configured.

  2. To enable the SonarQube Integration, go to Soundcheck > Integrations > SonarQube and click the Configure button. To learn more about the No-Code UI config, see the Configuring a fact collector (integration) via the no-code UI.

SonarQube Integration

YAML Configuration Option

  1. Create a sonarqube-facts-collectors.yaml file in the root of your Backstage repository and fill in all your SonarQube fact collectors. A simple example SonarQube fact collector is listed below.

    ---
    baseUrl: https://sonarqube.com
    token: ${SONARQUBE_TOKEN}
    frequency:
    cron: '0 * * * *'
    filter:
    - kind: 'component'
    spec.type: 'service'
    cache: false
    collects:
    - type: projects
    - type: project-tags
    - type: issues
    - type: measures

    Note: this file will be loaded at runtime along with the rest of your Backstage configuration files, so make sure it's available in deployed environments in the same way as your app-config.yaml files.

  2. Add a Soundcheck collectors field to the app-config.yaml and reference the newly created sonarqube-facts-collectors.yaml file.

    # app-config.yaml
    soundcheck:
    collectors:
    sonarqube:
    $include: ./sonarqube-facts-collectors.yaml
    # You can also utilize multiple instances with the Soundcheck collectors:
    # soundcheck:
    # collectors:
    # sonarqube:
    # - $include: ./sonarqube-facts-collectors-instance1.yaml
    # - $include: ./sonarqube-facts-collectors-instance2.yaml

Rate Limiting (Optional)

This fact collector can be rate limited in Soundcheck using the following configuration:

soundcheck:
job:
workers:
sonarqube:
limiter:
max: 1000
duration: 60000

In this example, the fact collector is limited to 1000 executions per minute.

This fact collector handles 429 rate limit errors from SonarQube. Soundcheck will automatically wait and retry requests that are rate limited.

Defining SonarQube Fact Collectors

This section describes the data shape and semantics of SonarQube fact collectors. The following is an example of a descriptor file for a SonarQube fact collector:

---
baseUrl: https://sonarqube.com
token: ${SONARQUBE_TOKEN}
frequency:
cron: '0 * * * *'
filter:
- kind: 'component'
spec.type: 'service'
cache: false
collects:
- type: projects
- type: project-tags
- type: issues
- type: measures

Below are the details for each field.

name [optional]

The unique identifier of the SonarQube instance. Omit if you are using a single SonarQube instance. If you are using multiple SonarQube instances, make sure the sonarqube.org/project-key entity annotations contain this name as prefix as described in the entity configuration section.

baseUrl [optional]

The base URL of the SonarQube instance to use. If not provided, the plugin will attempt to use the default URL https://sonarqube.com.

token [optional]

The SonarQube token to use for authentication. If not provided, the plugin will attempt to use username and password instead.

username [optional]

The SonarQube username to use for authentication. If not provided, the plugin will attempt to use token instead.

password [optional]

The SonarQube password to use for authentication. If not provided, the plugin will attempt to use token instead.

frequency [optional]

The frequency at which the collection should be executed. Possible values are either a cron expression { cron: ... } or HumanDuration. This is the default frequency for each collector.

Example:

frequency:
minutes: 10

initialDelay [optional]

The amount of time that should pass before the first invocation happens. Possible values are either a cron expression { cron: ... } or HumanDuration. This is the default initial delay for each collector.

Example:

initialDelay:
seconds: 30

batchSize [optional]

The number of entities to collect facts for at once. Optional, the default value is 1. This is the default batch size for each collector.

Note: Fact collection for a batch of entities is still considered as one hit towards the rate limits by the Soundcheck Rate Limiting engine, while the actual number of hits will be equal to the batchSize.

Example:

batchSize: 100

filter [optional]

A filter specifying which entities to collect the specified facts for. Matches the filter format used by the Catalog API. This is the default filter for each collector.

See filters for more details.

exclude [optional]

Entities matching this filter will be skipped during the fact collection process. Can be used in combination with filter. Matches the filter format used by the Catalog API. This is the default exclude filter for each collector.

Example:

filter:
- kind: component
exclude:
- spec.type: documentation

cache [optional]

If the collected facts should be cached, and if so for how long. Possible values are either true or false or a nested { duration: HumanDuration } field. This is the default cache config for each collector.

Example:

cache:
duration:
hours: 24

collects [required]

An array describing which facts to collect and how to collect them. See below for details about the fact collector configuration.

Fact Collector Configuration

Each collector supports the fields described below.

factName [optional]

The name of the fact to be collected (must be unique). If not provided, fact type will be used as a name.

  • Minimum length of 1
  • Maximum length of 100
  • Alphanumeric with single separator instances of periods, dashes, underscores, or forward slashes

type [required]

The type of the collector (e.g. projects, project-tags, issues, measures).

frequency [optional]

The frequency at which the fact collection should be executed. Possible values are either a cron expression { cron: ... } or HumanDuration. If provided, it overrides the default frequency provided at the top level. If not provided, it defaults to the frequency provided at the top level. If neither collector's frequency, nor default frequency is provided, the fact will only be collected on demand.

Example:

frequency:
minutes: 10

initialDelay [optional]

The amount of time that should pass before the first invocation happens. Possible values are either a cron expression { cron: ... } or HumanDuration. If provided, it overrides the default initial delay provided at the top level. If not provided, it defaults to the initial delay provided at the top level. If neither collector's initial delay nor default initial delay is provided, the initial delay will be calculated automatically.

Example:

initialDelay:
seconds: 30

batchSize [optional]

The number of entities to collect facts for at once. If provided, it overrides the default batch size provided at the top level. If not provided, it defaults to the batch size provided at the top level. If neither collector's batch size, nor default batch size is provided, the batch size will be defaulted to 1.

Note: Fact collection for a batch of entities is still considered as one hit towards the rate limits by the Soundcheck Rate Limiting engine, while the actual number of hits will be equal to the batchSize.

Example:

batchSize: 100

filter [optional]

A filter specifying which entities to collect the specified facts for. Matches the filter format used by the Catalog API. If provided, it overrides the default filter provided at the top level. If not provided, it defaults to the filter provided at the top level. If neither collector's filter nor default filter is provided, the fact will be collected for all entities.

See filters for more details.

exclude [optional]

Entities matching this filter will be skipped during the fact collection process. Can be used in combination with filter. Matches the filter format used by the Catalog API. If provided, it overrides the default exclude filter provided at the top level. If not provided, it defaults to the exclude filter provided at the top level. If neither collector's exclude filter nor default exclude filter is provided, only the inclusion filter will be applied.

Example:

filter:
- kind: component
exclude:
- spec.type: documentation

cache [optional]

If the collected facts should be cached, and if so for how long. Possible values are either true or false or a nested { duration: HumanDuration } field. If provided it, overrides the default cache config provided at the top level. If not provided, it defaults to the cache config provided at the top level. If neither collector's cache nor default cache config is provided, the fact will not be cached.

Example:

cache:
duration:
hours: 24

Collecting Projects Fact

The projects fact contains information about the entity's project from SonarQube Projects API.

Shape of A Projects Fact Collector

The shape of a projects fact collector matches the Fact Collector Configuration (restriction: type: projects).

Here's an example of the projects fact collector configuration:

- type: projects
cache: true
frequency:
cron: '0 * * * *'

Shape of A Projects Fact

The following is an example response from the projects fact collector. See SonarQube API documentation for more details.

{
"paging": {
"pageIndex": 1,
"pageSize": 500,
"total": 2
},
"components": [
{
"key": "test-project-key",
"name": "Project Name 1",
"qualifier": "TRK",
"visibility": "public",
"lastAnalysisDate": "2017-03-01T11:39:03+0300",
"revision": "cfb82f55c6ef32e61828c4cb3db2da12795fd767",
"managed": false
}
]
}

Shape of A Projects Fact Check

The shape of a projects fact check matches the Shape of a Fact Check.

The following is an example of the projects fact checks:

soundcheck:
checks:
- id: requires_project_to_exist
description: Project exists in sonarqube
passedMessage: The check has passed!
failedMessage: The check has failed!
rule:
factRef: sonarqube:default/projects
path: $.paging.total
operator: greaterThan
value: 0

Collecting Project Tags Fact

The project-tags fact contains information about entity's project tags from SonarQube Components API.

Shape of A Project Tags Fact Collector

The shape of a project-tags fact collector matches the Fact Collector Configuration (restriction: type: project-tags).

Here's an example of a project-tags fact collector configuration:

- type: project-tags
cache: true
frequency:
cron: '0 * * * *'

Shape of A Project Tags Fact

The following is an example response from the project-tags fact collector. See SonarQube API documentation for more details.

{
"tags": ["production", "test", "internal"]
}

Shape of A Project Tags Fact Check

The shape of a project-tags fact check matches the Shape of a Fact Check.

The following is an example of the project-tags fact check:

soundcheck:
checks:
- id: project_has_production_tag
description: Project has production tag
passedMessage: The check has passed!
failedMessage: The check has failed!
rule:
factRef: sonarqube:default/project-tags
path: $.tags
operator: contains
value: production

Collecting Issues Fact

The issues fact contains information about issues found in the entity's project from SonarQube Issues API.

Shape of An Issues Fact Collector

The shape of an issues fact collector matches the Fact Collector Configuration (restriction: type: issues) and supports additional configuration options:

statuses [optional]

If specified, only issues with these statuses will be collected. Possible values:

  • OPEN
  • CONFIRMED
  • FALSE_POSITIVE
  • ACCEPTED
  • FIXED

severities [optional]

If specified, only issues with these severities will be collected. Possible values:

  • BLOCKER
  • CRITICAL
  • MAJOR
  • MINOR
  • INFO

types [optional]

If specified, only issues with these types will be collected. Possible values:

  • VULNERABILITY
  • BUG
  • CODE_SMELL

Here's an example of an issues fact collector configuration:

- type: issues
cache: true
frequency:
cron: '0 * * * *'
statuses:
- OPEN
severities:
- BLOCKER
- CRITICAL
types:
- VULNERABILITY

Shape of An Issues Fact

The following is an example response from the issues fact collector. See SonarQube API documentation for more details.

{
"paging": {
"pageIndex": 1,
"pageSize": 500,
"total": 1
},
"issues": [
{
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"component": "com.github.kevinsawicki:http-request:com.github.kevinsawicki.http.HttpRequest",
"project": "com.github.kevinsawicki:http-request",
"rule": "java:S1144",
"cleanCodeAttribute": "CLEAR",
"cleanCodeAttributeCategory": "INTENTIONAL",
"issueStatus": "ACCEPTED",
"impacts": [
{
"softwareQuality": "SECURITY",
"severity": "HIGH"
}
],
"message": "Remove this unused private \"getKee\" method.",
"messageFormattings": [
{
"start": 0,
"end": 4,
"type": "CODE"
}
],
"line": 81,
"hash": "a227e508d6646b55a086ee11d63b21e9",
"author": "Developer 1",
"effort": "2h1min",
"creationDate": "2013-05-13T17:55:39+0200",
"updateDate": "2013-05-13T17:55:39+0200",
"tags": ["bug"],
"comments": [
{
"key": "7d7c56f5-7b5a-41b9-87f8-36fa70caa5ba",
"login": "john.smith",
"htmlText": "Must be "public"!",
"markdown": "Must be \"public\"!",
"updatable": false,
"createdAt": "2013-05-13T18:08:34+0200"
}
],
"attr": {
"jira-issue-key": "SONAR-1234"
},
"transitions": ["reopen"],
"actions": ["comment"],
"textRange": {
"startLine": 2,
"endLine": 2,
"startOffset": 0,
"endOffset": 204
},
"flows": [],
"quickFixAvailable": false,
"ruleDescriptionContextKey": "spring",
"codeVariants": ["windows", "linux"]
}
],
"components": [
{
"key": "com.github.kevinsawicki:http-request:src/main/java/com/github/kevinsawicki/http/HttpRequest.java",
"enabled": true,
"qualifier": "FIL",
"name": "HttpRequest.java",
"longName": "src/main/java/com/github/kevinsawicki/http/HttpRequest.java",
"path": "src/main/java/com/github/kevinsawicki/http/HttpRequest.java"
},
{
"key": "com.github.kevinsawicki:http-request",
"enabled": true,
"qualifier": "TRK",
"name": "http-request",
"longName": "http-request"
}
],
"facets": []
}

Shape of An Issues Fact Check

The shape of a issues fact check matches the Shape of a Fact Check.

The following is an example of the issues fact check:

soundcheck:
checks:
- id: no_blocker_issues
description: No blocker issues
passedMessage: The check has passed!
failedMessage: The check has failed!
rule:
factRef: sonarqube:default/issues
path: $.issues[?(@.severity === 'BLOCKER')]
operator: hasLengthOf
value: 0

Collecting Measures Fact

The measures fact contains information about entity's project measures from SonarQube Measures API.

Shape of A Measures Fact Collector

The shape of a measures fact collector matches the Fact Collector Configuration (restriction: type: measures) and supports an additional configuration option:

metrics [optional]

An array of metric keys that should be fetched. If not provided, it will fetch the following metric keys:

  • ncloc
  • complexity
  • violations
  • open_issues

You can see a full list of available metrics in your instance with the following HTTP request:

GET https://sonarqube.com/api/metrics/search?ps=500
Authorization: Token
Accept: application/json

The following is an example of the measures fact collector config:

- type: measures
cache: true
frequency:
cron: '0 * * * *'
metrics:
- ncloc
- complexity
- violations
- open_issues
- bugs
- code_smells

Shape of A Measures Fact

The following is an example response from the measures fact collector. See SonarQube API documentation for more details.

{
"component": {
"key": "MY_PROJECT:ElementImpl.java",
"name": "ElementImpl.java",
"qualifier": "FIL",
"language": "java",
"path": "src/main/java/com/sonarsource/markdown/impl/ElementImpl.java",
"measures": [
{
"metric": "complexity",
"value": "1"
},
{
"metric": "ncloc",
"value": "61"
},
{
"metric": "violations",
"value": "5",
"bestValue": false
},
{
"metric": "open_issues",
"value": "5",
"bestValue": false
}
]
}
}

Shape of A Measures Fact Check

The shape of a measures fact check matches the Shape of a Fact Check.

The following is an example of the measures fact check:

soundcheck:
checks:
- id: no_violations
description: No violations found
passedMessage: The check has passed!
failedMessage: The check has failed!
rule:
factRef: sonarqube:default/measures
path: $.component.measures[?(@.metric === 'violations')].value
operator: equal
value: 0

Shape of A SonarQube Track

The following is an example of the Soundcheck track that utilizes these checks

soundcheck:
programs:
- id: sonarqube-integration-demo
name: SonarQube Integration Demo
ownerEntityRef: group:default/owning_group
description: Demonstration of Soundcheck SonarQube Fact Collector
levels:
- ordinal: 1
name: First level
description: Checks leveraging SoundCheck's SonarQube integration
checks:
- id: requires_project_to_exist
name: Project exists in sonarqube
description: Project exists in sonarqube
- id: project_has_production_tag
name: Project has production tag
description: Project has production tag
- id: no_blocker_issues
name: No blocker issues
description: No blocker issues
- id: no_violations
name: No violations found
description: No violations found