GitLab CI pipelines can run SonarQube analysis to identify code quality and security issues.

Let’s see it in action. Imagine you have a Python project. Your .gitlab-ci.yml file might look something like this:

stages:
  - test
  - analyze

variables:
  SONAR_USER_HOME: ${CI_PROJECT_DIR}/.sonar/cache  # Define a cache directory for SonarScanner
  SONAR_TOKEN: $SONAR_TOKEN # Inject SonarQube token from GitLab CI/CD variables

test_job:
  stage: test
  script:
    - echo "Running tests..."
    - python -m unittest discover tests/

sonarqube_analysis:
  stage: analyze
  image: sonarsource/sonar-scanner-cli:latest # Use the official SonarScanner Docker image
  script:
    - echo "Starting SonarQube analysis..."
    - sonar-scanner \
        -Dsonar.host.url=$SONAR_HOST_URL \
        -Dsonar.login=$SONAR_TOKEN \
        -Dsonar.projectKey=$CI_PROJECT_PATH_SLUG \
        -Dsonar.sources=. \
        -Dsonar.exclusions=**/migrations/**,**/tests/** \
        -Dsonar.python.version=3.9 # Adjust Python version if needed
  only:
    - main # Run analysis only on the main branch

In this pipeline:

  • The test_job runs your unit tests. This is crucial because SonarQube analysis is often performed after successful tests.
  • The sonarqube_analysis job uses the sonarsource/sonar-scanner-cli Docker image, which contains the SonarScanner executable.
  • We define SONAR_USER_HOME to ensure the SonarScanner cache is isolated to the project directory, improving build performance and avoiding conflicts.
  • SONAR_TOKEN is passed as a protected CI/CD variable in GitLab’s settings. This token grants the scanner permission to communicate with your SonarQube instance.
  • SONAR_HOST_URL should also be set as a CI/CD variable, pointing to your SonarQube server (e.g., http://localhost:9000).
  • sonar.projectKey is set to $CI_PROJECT_PATH_SLUG, which is a GitLab predefined variable that creates a unique key for your project within SonarQube based on its path.
  • sonar.sources=. tells the scanner to analyze the current directory.
  • sonar.exclusions is used to prevent SonarQube from analyzing directories you don’t want it to, like database migrations or test files.
  • sonar.python.version is specified for Python projects, ensuring the scanner uses the correct language-specific rules.

The problem this solves is the manual, often forgotten, process of checking code for bugs, vulnerabilities, and code smells. By integrating SonarQube into your CI/CD pipeline, you automate this quality gate. The scanner will connect to your SonarQube server, upload the analysis results, and SonarQube will then present a dashboard with detailed reports. If the analysis fails due to critical issues (configured in SonarQube’s Quality Gates), the pipeline can be set to fail, preventing bad code from being merged.

Internally, the sonar-scanner command executes a series of steps. It first identifies the project’s source files based on the provided paths and configurations. It then applies language-specific analyzers (e.g., Python, Java, JavaScript) to parse the code and build an Abstract Syntax Tree (AST). This AST is then traversed to identify patterns that match predefined rules for bugs, vulnerabilities, code smells, and duplication. The results are collected and sent to the SonarQube server via HTTP requests. The server then processes these results, updates the project’s dashboard, and evaluates it against the configured Quality Gate.

The levers you control are primarily within the sonar-scanner command-line arguments and your SonarQube server configuration. You can fine-tune which files are analyzed (sonar.sources, sonar.exclusions, sonar.inclusions), specify language versions (sonar.java.version, sonar.python.version), and even provide coverage reports (sonar.coverage.reportPaths) or test execution data (sonar.tests, sonar.testExecutionReportPaths). On the SonarQube server itself, you define your Quality Gates, which are sets of conditions (e.g., "no new critical vulnerabilities," "code coverage > 80%") that determine if a project’s quality is acceptable.

A common pitfall is not correctly configuring the sonar.projectKey. If this value is not unique for each project being analyzed, or if it changes between pipeline runs for the same project, SonarQube will treat them as separate projects, leading to fragmented analysis history and incorrect metrics. It’s best practice to use a GitLab predefined variable like $CI_PROJECT_PATH_SLUG or a combination of $CI_GROUP_PATH_SLUG-$CI_PROJECT_NAME to ensure uniqueness across your SonarQube instance.

The next logical step after integrating SonarQube analysis is to configure Quality Gates in SonarQube to enforce specific code quality standards.

Want structured learning?

Take the full Gitlab-ci course →