Aller au contenu

Integration

Principe général

Le service d'analyse de qualité s'integre a trois niveaux : dans l'IDE du développeur (feedback instantane), dans le pipeline CI (quality gate automatique) et dans l'ecosysteme de la DSI (SSO, observabilité, alerting).

graph TD
    IDE["IDE<br/>SonarLint + Semgrep"] --> SQ["SonarQube Server"]
    CICD["Pipeline CI/CD<br/>Scanner + Quality Gate<br/>PR status"] --> SQ
    DSI["Ecosysteme DSI<br/>Keycloak, Grafana<br/>Webhooks"] --> SQ

Integration CI/CD — Gitea Actions

Workflow d'analyse SonarQube

# .gitea/workflows/quality.yml
name: Quality Analysis
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Historique complet pour l'analyse incrementale

      - name: Setup Java
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'

      - name: Build and test with coverage
        run: |
          ./gradlew test jacocoTestReport
          # ou pour Maven :
          # mvn verify jacoco:report

      - name: Semgrep SAST scan
        run: |
          pip install semgrep
          semgrep scan \
            --config auto \
            --config .semgrep/rules/ \
            --sarif \
            --output semgrep-results.sarif

      - name: SonarQube scan
        uses: sonarsource/sonarqube-scan-action@v3
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
          SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
        with:
          args: >
            -Dsonar.projectKey=${{ gitea.repository }}
            -Dsonar.sources=src/
            -Dsonar.tests=test/
            -Dsonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml
            -Dsonar.sarifReportPaths=semgrep-results.sarif

      - name: Quality gate check
        uses: sonarsource/sonarqube-quality-gate-action@v1
        timeout-minutes: 5
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
          SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}

Configuration du projet SonarQube

# sonar-project.properties (a la racine du depot)
sonar.projectKey=mon-projet
sonar.projectName=Mon Projet
sonar.projectVersion=1.0

# Sources et tests
sonar.sources=src/main
sonar.tests=src/test
sonar.sourceEncoding=UTF-8

# Exclusions
sonar.exclusions=**/generated/**,**/vendor/**,**/node_modules/**
sonar.test.exclusions=**/test/**

# Couverture (adapter selon l'outil)
sonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml
# sonar.python.coverage.reportPaths=coverage.xml
# sonar.javascript.lcov.reportPaths=coverage/lcov.info

# Import SARIF (resultats Semgrep)
sonar.sarifReportPaths=semgrep-results.sarif

Workflow pour projets Python

# .gitea/workflows/quality-python.yml
name: Quality Analysis (Python)
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'

      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install pytest pytest-cov semgrep

      - name: Run tests with coverage
        run: |
          pytest --cov=src --cov-report=xml:coverage.xml

      - name: Semgrep SAST scan
        run: |
          semgrep scan \
            --config p/python \
            --config p/security-audit \
            --sarif \
            --output semgrep-results.sarif

      - name: SonarQube scan
        uses: sonarsource/sonarqube-scan-action@v3
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
          SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
        with:
          args: >
            -Dsonar.projectKey=${{ gitea.repository }}
            -Dsonar.sources=src/
            -Dsonar.tests=tests/
            -Dsonar.python.coverage.reportPaths=coverage.xml
            -Dsonar.sarifReportPaths=semgrep-results.sarif

      - name: Quality gate check
        uses: sonarsource/sonarqube-quality-gate-action@v1
        timeout-minutes: 5
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
          SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}

Semgrep en pre-commit

Configuration pre-commit

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/semgrep/semgrep
    rev: v1.70.0
    hooks:
      - id: semgrep
        args:
          - --config
          - p/security-audit
          - --config
          - .semgrep/rules/
          - --error
        stages: [pre-commit]
# Installation du hook
pip install pre-commit
pre-commit install

Pre-commit pour le feedback rapide

Semgrep en pre-commit détecte les problèmes de sécurité avant même le push. Le scan est rapide (< 30s sur la plupart des dépôts) et ne bloque pas le workflow du développeur.


Integration IDE — SonarLint

Configuration SonarLint pour VS Code

// .vscode/settings.json
{
  "sonarlint.connectedMode.connections.sonarqube": [
    {
      "serverUrl": "https://sonar.example.com",
      "token": "${env:SONAR_TOKEN}",
      "connectionId": "organisation-sonarqube"
    }
  ],
  "sonarlint.connectedMode.project": {
    "connectionId": "organisation-sonarqube",
    "projectKey": "mon-projet"
  }
}

Configuration SonarLint pour IntelliJ

  1. Préférences > Tools > SonarLint > Connections
  2. Ajouter une connexion SonarQube : https://sonar.example.com
  3. S'authentifier avec le token personnel
  4. Project Settings > SonarLint > Project Key : mon-projet

Synchronisation des regles

SonarLint synchronise automatiquement les quality profiles du serveur SonarQube. Les développeurs voient les mêmes regles dans l'IDE et dans le pipeline CI, ce qui elimine les surprises au moment du push.


SSO Keycloak pour SonarQube

Configuration SAML dans Keycloak

# Creer le client SAML dans Keycloak
kcadm.sh create clients -r organization \
  -s clientId=sonarqube \
  -s enabled=true \
  -s protocol=saml \
  -s 'redirectUris=["https://sonar.example.com/oauth2/callback/saml"]' \
  -s 'attributes.saml.assertion.signature=true' \
  -s 'attributes.saml.server.signature=true' \
  -s 'attributes.saml_name_id_format=username'

Configuration SonarQube (sonar.properties)

# /opt/sonarqube/conf/sonar.properties

# SAML Authentication
sonar.auth.saml.enabled=true
sonar.auth.saml.applicationId=sonarqube
sonar.auth.saml.providerName=Keycloak
sonar.auth.saml.providerId=https://auth.example.com/realms/organization
sonar.auth.saml.loginUrl=https://auth.example.com/realms/organization/protocol/saml
sonar.auth.saml.certificate.secured=MIICnTCCAYUCBgF...
sonar.auth.saml.user.login=login
sonar.auth.saml.user.name=name
sonar.auth.saml.user.email=email
sonar.auth.saml.group.name=groups

# Forcer l'authentification (pas d'acces anonyme)
sonar.forceAuthentication=true

Mapper les groupes Keycloak vers SonarQube

Configurer un mapper de type Group list dans le client SAML Keycloak pour transmettre les groupes de l'utilisateur. SonarQube peut ensuite mapper ces groupes sur des permissions par projet.

Variables d'environnement (Podman Compose)

# Ajouter dans le service sonarqube du podman-compose.yml
environment:
  SONAR_AUTH_SAML_ENABLED: "true"
  SONAR_AUTH_SAML_APPLICATIONID: "sonarqube"
  SONAR_AUTH_SAML_PROVIDERNAME: "Keycloak"
  SONAR_AUTH_SAML_PROVIDERID: "https://auth.example.com/realms/organization"
  SONAR_AUTH_SAML_LOGINURL: "https://auth.example.com/realms/organization/protocol/saml"
  SONAR_AUTH_SAML_USER_LOGIN: "login"
  SONAR_AUTH_SAML_USER_NAME: "name"
  SONAR_AUTH_SAML_USER_EMAIL: "email"
  SONAR_AUTH_SAML_GROUP_NAME: "groups"
  SONAR_FORCEAUTHENTICATION: "true"

Webhook vers Grafana / Alerting

Configurer le webhook SonarQube

# Creer un webhook global (tous les projets)
curl -u admin:TOKEN -X POST \
  "http://localhost:9000/api/webhooks/create" \
  -d "name=alerting-quality-gate" \
  -d "url=https://alertmanager.example.com/api/v1/alerts" \
  -d "secret=WEBHOOK_SECRET"

Payload du webhook

SonarQube envoie un payload JSON à chaque changement de statut du quality gate :

{
  "serverUrl": "https://sonar.example.com",
  "taskId": "AVh21JS2JepAEhwQ-b3u",
  "status": "SUCCESS",
  "analysedAt": "2026-04-16T14:32:15+0000",
  "project": {
    "key": "mon-projet",
    "name": "Mon Projet",
    "url": "https://sonar.example.com/dashboard?id=mon-projet"
  },
  "qualityGate": {
    "name": "Organisation",
    "status": "ERROR",
    "conditions": [
      {
        "metric": "new_coverage",
        "operator": "LESS_THAN",
        "value": "65.2",
        "status": "ERROR",
        "errorThreshold": "80"
      }
    ]
  }
}

Alerte Prometheus via webhook receiver

# Configuration Alertmanager pour recevoir les webhooks SonarQube
# Utiliser un webhook-to-prometheus bridge ou un script custom
# qui traduit le payload SonarQube en alerte Prometheus

# Exemple de regle d'alerte Grafana
- alert: SonarQubeQualityGateFailed
  expr: sonarqube_quality_gate_status{status="ERROR"} == 1
  for: 0m
  labels:
    severity: warning
    team: platform
  annotations:
    summary: "Quality gate en echec pour {{ $labels.project }}"
    description: "Le projet {{ $labels.project }} ne passe plus le quality gate."

Auto-provisioning des projets depuis Gitea

Script de synchronisation

#!/bin/bash
# /home/sonarqube/scripts/sync-projects.sh
# Synchronise les depots Gitea avec les projets SonarQube

set -euo pipefail

GITEA_URL="https://git.example.com"
GITEA_TOKEN="${GITEA_TOKEN}"
SONAR_URL="http://localhost:9000"
SONAR_TOKEN="${SONAR_TOKEN}"

# Lister les depots Gitea
repos=$(curl -s -H "Authorization: token ${GITEA_TOKEN}" \
  "${GITEA_URL}/api/v1/repos/search?limit=50" | jq -r '.[].full_name')

for repo in ${repos}; do
  project_key=$(echo "${repo}" | tr '/' '-')

  # Verifier si le projet existe deja dans SonarQube
  exists=$(curl -s -u "${SONAR_TOKEN}:" \
    "${SONAR_URL}/api/projects/search?projects=${project_key}" | \
    jq -r '.components | length')

  if [ "${exists}" -eq 0 ]; then
    echo "Creation du projet SonarQube: ${project_key}"
    curl -s -u "${SONAR_TOKEN}:" -X POST \
      "${SONAR_URL}/api/projects/create" \
      -d "name=${repo}" \
      -d "project=${project_key}"
  fi
done

echo "Synchronisation terminee: $(date)"

Cron de synchronisation

# /etc/cron.d/sonarqube-sync
# Synchroniser les projets toutes les heures
0 * * * * sonarqube /home/sonarqube/scripts/sync-projects.sh >> /var/log/sonarqube-sync.log 2>&1

Provisioning on-demand

Pour les organisations avec peu de dépôts, le provisioning automatique peut etre remplace par la creation manuelle des projets. L'auto-provisioning est utile à partir de 20+ dépôts.


Metriques Prometheus

Exporter les metriques SonarQube

SonarQube n'expose pas de metriques Prometheus nativement. Utiliser un exporter tiers :

# Ajouter au podman-compose.yml
  sonarqube-exporter:
    image: docker.io/ekzhang/sonarqube-exporter:latest
    container_name: sonarqube-exporter
    environment:
      SONAR_URL: "http://sonarqube:9000"
      SONAR_TOKEN: "${SONAR_TOKEN}"
    ports:
      - "9394:9394"
    networks:
      - sonarqube-net
    depends_on:
      - sonarqube

Configuration Prometheus

# prometheus.yml
scrape_configs:
  - job_name: 'sonarqube'
    static_configs:
      - targets: ['sonarqube-exporter:9394']
    scrape_interval: 5m

Dashboard Grafana

Metriques utiles a superviser :

Metrique Description
sonarqube_projects_total Nombre total de projets
sonarqube_quality_gate_status Statut du quality gate par projet
sonarqube_coverage Couverture de code par projet
sonarqube_bugs Nombre de bugs par projet
sonarqube_vulnerabilities Nombre de vulnerabilites par projet
sonarqube_code_smells Nombre de code smells par projet
sonarqube_technical_debt_minutes Dette technique en minutes par projet