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:
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
-
Make sure the prerequisite Configure SonarQube integration in Backstage is completed and SonarQube instance details are configured.
-
To enable the SonarQube Integration, go to
Soundcheck > Integrations > SonarQube
and click theConfigure
button. To learn more about the No-Code UI config, see the Configuring a fact collector (integration) via the no-code UI.
YAML Configuration Option
-
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: measuresNote: 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. -
Add a Soundcheck collectors field to the
app-config.yaml
and reference the newly createdsonarqube-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