Skip to content

SonarQube Setup & Configuration

How to add SonarQube analysis to a CWIQ service pipeline, what data gets imported, and how authentication works.

SonarQube is the central hub for code quality and security analysis across all CWIQ services. It aggregates results from multiple scanners — static analysis, coverage, Semgrep SAST, and Trivy misconfiguration findings — into a single project dashboard.

Browser access: https://sonarqube.shared.cwiq.io (via Tailscale or HTTPS)

CI/CD scanner endpoint: http://10.0.10.8:9000 (VPC private IP, used by pipeline jobs only)


Community Edition — main branch only

SonarQube Community Edition (CE) can only analyze the main branch. Feature branch analysis and MR decoration are NOT available. The sonarqube-scan job runs only on the main branch (enforced by the CI template's rules:). Do not attempt to trigger it on feature branches — it will not run.


What Gets Imported

Each pipeline execution imports four categories of data into SonarQube. All inputs must be present as artifacts before the sonarqube-scan job starts.

Input Source Job Format Purpose
Source code analysis sonarqube-scan Built-in Bugs, code smells, vulnerabilities detected by SonarQube's own engine
Test coverage test coverage.xml (Python) or coverage/lcov.info (Node.js) Code coverage percentage and uncovered lines
Semgrep SAST results semgrep semgrep-results.sarif Security findings from Semgrep rulesets
Trivy FS results trivy-fs-scan trivy-fs-results.sarif Misconfiguration and secrets findings

Adding SonarQube to Your Pipeline

Step 1 — Include the scan template

Add the SonarQube template to your project's .gitlab-ci.yml include block alongside the common and security templates:

include:
  - project: 'orchestrator/ci-templates'
    ref: main
    file:
      - '/.gitlab-ci-common.yml'
      - '/.gitlab-ci-python.yml'   # or .gitlab-ci-node.yml for frontend
      - '/.gitlab-ci-trivy.yml'
      - '/.gitlab-ci-scan.yml'

Step 2 — Create sonar-project.properties

Add a sonar-project.properties file to the root of your project. This file tells the SonarQube scanner where your source, tests, and coverage reports are. See the sonar-project.properties reference for field details and per-language templates.

Step 3 — Define the scan job

Extend the hidden .sonarqube-scan template and wire it up to the jobs that produce coverage and SARIF artifacts:

sonarqube-scan:
  extends: .sonarqube-scan
  needs:
    - job: test
      artifacts: true
    - job: semgrep
      artifacts: true
    - job: trivy-fs-scan
      artifacts: true

The artifacts: true flag is required

The needs: block must include artifacts: true for every upstream job. SonarQube imports coverage and SARIF files that were produced by those jobs. Without artifacts: true, GitLab does not download the artifacts into the current job's workspace and the files will be missing when the scanner runs.

Coverage files require paths: artifacts, not just reports:

Your test job must declare the coverage file under artifacts: paths: in addition to (or instead of) artifacts: reports:. GitLab's reports: artifacts are used exclusively for MR visualisations and are not downloaded by downstream jobs via needs:. If you only declare the coverage file under reports:, it will be absent in the sonarqube-scan workspace.

# Correct — coverage file is available to downstream jobs
artifacts:
  paths:
    - coverage.xml
  reports:
    coverage_report:
      coverage_format: cobertura
      path: coverage.xml

Authentication

The sonarqube-scan job authenticates using Vault JWT auth. No SonarQube credentials are stored in GitLab CI/CD variables.

The flow is:

  1. The CI job uses GitLab's CI_JOB_JWT_V2 token to authenticate with Vault under the nexus-ci role.
  2. Vault returns a short-lived credential scoped to that role.
  3. The job fetches the SonarQube token from secret/data/sonarqube/svc-orchestrator.
  4. The token is passed to the sonar-scanner CLI via the SONAR_TOKEN environment variable.

This means the SonarQube token is never stored in GitLab and is only available for the duration of the job. See Vault JWT Auth for the general pattern.