> ## Documentation Index
> Fetch the complete documentation index at: https://backstage.spotify.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# SonarQube

> Collect SonarQube projects, project tags, issues, and code quality measures as Soundcheck facts for code health checks.

The SonarQube integration plugin for [Soundcheck](https://www.npmjs.com/package/@spotify/backstage-plugin-soundcheck-backend-module-sonarqube) supports the collection of the following facts:

* [Projects](#collecting-projects-fact)
* [Project Tags](#collecting-project-tags-fact)
* [Issues](#collecting-issues-fact)
* [Measures](#collecting-measures-fact)

## Prerequisites

### Configure SonarQube integration in Backstage

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

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
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](#entity-configuration).

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
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.

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
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`:

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
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.

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
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:

```bash theme={"theme":{"light":"github-light","dark":"dracula"}}
yarn workspace backend add @spotify/backstage-plugin-soundcheck-backend-module-sonarqube
```

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

```ts packages/backend/src/index.ts highlight={4} theme={"theme":{"light":"github-light","dark":"dracula"}}
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](../../../setup-and-installation#backend-setup) 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](#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](#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](../index#configuring-a-fact-collector-integration-via-the-no-code-ui).

<Frame>
  <img
    src="https://mintcdn.com/spotify-89f50c35/ulmNVnZzSFL3BsWR/plugins/soundcheck/images/collectors/sonarqube-collector-ncui.png?fit=max&auto=format&n=ulmNVnZzSFL3BsWR&q=85&s=13a02021a5c4a200ba71b76a497e285a"
    alt="SonarQube
Integration"
    width="3424"
    height="1920"
    data-path="plugins/soundcheck/images/collectors/sonarqube-collector-ncui.png"
  />
</Frame>

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

   ```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
   ---
   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.

   ```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
   # 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:

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
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:

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
---
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](#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](https://backstage.io/docs/reference/types.humanduration).
This is the default frequency for each collector.

Example:

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
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](https://backstage.io/docs/reference/types.humanduration).
This is the default initial delay for each collector.

Example:

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
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](#rate-limiting-optional) engine, while the actual number of hits
will be equal to the `batchSize`.

Example:

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
batchSize: 100
```

#### `filter` \[optional]

A filter specifying which entities to collect the specified facts for. Matches the [filter format](https://backstage.io/docs/reference/catalog-client.entityfilterquery) used by the Catalog API.
This is the default filter for each collector.

See [filters](/plugins/soundcheck/core-concepts/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](https://backstage.io/docs/reference/catalog-client.entityfilterquery) used by the Catalog API.
This is the default exclude filter for each collector.

Example:

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
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](https://backstage.io/docs/reference/types.humanduration) `}` field.
This is the default cache config for each collector.

Example:

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
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](https://backstage.io/docs/reference/types.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:

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
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](https://backstage.io/docs/reference/types.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:

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
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](#rate-limiting-optional) engine, while the actual number of hits
will be equal to the `batchSize`.

Example:

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
batchSize: 100
```

#### `filter` \[optional]

A filter specifying which entities to collect the specified facts for. Matches the [filter format](https://backstage.io/docs/reference/catalog-client.entityfilterquery) 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](/plugins/soundcheck/core-concepts/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](https://backstage.io/docs/reference/catalog-client.entityfilterquery) 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:

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
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](https://backstage.io/docs/reference/types.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:

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
cache:
  duration:
    hours: 24
```

## Collecting Projects Fact

The `projects` fact contains information about the entity's project from [SonarQube Projects API](https://next.sonarqube.com/sonarqube/web_api/api/projects/search).

### Shape of A Projects Fact Collector

The shape of a `projects` fact collector matches the [Fact Collector Configuration](#fact-collector-configuration) (restriction: `type: projects`).

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

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
- 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](https://next.sonarqube.com/sonarqube/web_api/api/projects/search) for more details.

```json theme={"theme":{"light":"github-light","dark":"dracula"}}
{
  "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](https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/checks#overall-shape-of-a-check).

The following is an example of the `projects` fact checks:

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
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](https://next.sonarqube.com/sonarqube/web_api/api/components/show).

### Shape of A Project Tags Fact Collector

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

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

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
- 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](https://next.sonarqube.com/sonarqube/web_api/api/components/show) for more details.

```json theme={"theme":{"light":"github-light","dark":"dracula"}}
{
  "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](https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/checks#overall-shape-of-a-check).

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

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
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](https://next.sonarqube.com/sonarqube/web_api/api/issues/search).

### Shape of An Issues Fact Collector

The shape of an `issues` fact collector matches the [Fact Collector Configuration](#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:

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
- 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](https://next.sonarqube.com/sonarqube/web_api/api/issues/search) for more details.

```json theme={"theme":{"light":"github-light","dark":"dracula"}}
{
  "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 &quot;public&quot;!",
          "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](https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/checks#overall-shape-of-a-check).

The following is an example of the `issues` fact check:

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
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](https://next.sonarqube.com/sonarqube/web_api/api/measures/component).

### Shape of A Measures Fact Collector

The shape of a `measures` fact collector matches the [Fact Collector Configuration](#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:

```http request theme={"theme":{"light":"github-light","dark":"dracula"}}
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:

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
- 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](https://next.sonarqube.com/sonarqube/web_api/api/measures/component) for more details.

```json theme={"theme":{"light":"github-light","dark":"dracula"}}
{
  "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](https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/checks#overall-shape-of-a-check).

The following is an example of the `measures` fact check:

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
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

```yaml theme={"theme":{"light":"github-light","dark":"dracula"}}
soundcheck:
  tracks:
    - 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
```
