top of page

Connecter SonarQube au QE Score : récupère tes premières métriques qualité de code !

Dernière mise à jour : 23 avr.

Dans cet article, tu vas apprendre à connecter SonarQube au QE Score pour remonter automatiquement les métriques qualité de ton code : nombre de lignes, duplication, test coverage, date de dernier scan, code smells, quality gate, etc. 🚀 En quelques minutes, ton tableau de bord QE Score affichera des données clés issues de ton analyse Sonar !


🔍 Connecter SonarQube au QE Score : récupère tes premières métriques qualité en 10 minutes !

📺 Vidéo de démo



🧠 En résumé :

Étape

Titre

Durée estimée

1

Récupérer la clé du projet Sonar

5 min

2

Générer un token d’accès

3 min

3

Rafraîchir les données depuis QE Score

1 min

4

Vérifier les métriques

1 min

5

Visualiser les résultats dans QE Score

0 min

🛠️ Prérequis


✔️ Avoir réalisé la configuration du Data Collection via l’article : 👉 "Télécharge le Quick Start Kit et crée ton 1er QE Score en 30 minutes top chrono !"

✔️ Avoir un projet SonarQube opérationnel avec des données d’analyse.


🔑 Étape 1 – Récupère la clé de ton projet Sonar (⏱️ 5 min)

  1. Connecte-toi à ton projet SonarQube

  2. Clique sur “Information”

  3. Copie la Project Key


    QE score - Quality Engineering - Sonar Key

📝 Ensuite, dans ton Google Sheet (copie de QEscore_DataCollection_v1.0_starterkit), Onglet repository_settings :

  • Colonne A ("Product") : nom de ton app (ex: AppTrading)

  • Colonne B ("RepositoryPath") : chemin Git (ex: /github/monorg/monrepo)

  • Colonne C ("SonarURL") : base URL Sonar (ex: https://sonar.qescore.com)

  • Colonne D ("SonarKey") : colle ici la Project Key récupérée 🔑


QE score - Quality Engineering - repository settings

🔐 Étape 2 – Génére un token Sonar (⏱️ 3 min)


  1. Va dans ton profil utilisateur Sonar

  2. Onglet Sécurité

  3. Clique sur Générer un token

  4. Copie-le sans le perdre !


    QE score - Quality Engineering - token sonar

🧩 Puis, dans ton Google Sheet :

  • Va dans le menu Extensions > Apps Script

  • Dans les paramètres du script (Paramètres du projet)

  • Ajoute une propriété nommée : token_sonar

  • Colle le token comme valeur

  • ✅ Sauvegarde !


QE score - Quality Engineering - appscript token sonar

🔄 Étape 3 – Récupère les données Sonar dans QE Score (⏱️ 1 min)

🧠 Dans ton Google Sheet, clique sur le menu QE SCORE > Refresh Data Sonar 📥 Les données sont automatiquement récupérées et affichées dans ton tableau.


QE score - Quality Engineering - sonar metrics into QE Score

🔍 Étape 4 – Vérifie les métriques sur Sonar

Retourne sur ton interface Sonar et va dans l’onglet Overall du projet. ✅ Tu dois y retrouver les mêmes valeurs que dans ton Sheet.


QE score - Quality Engineering - sonar metrics

🎉 Étape 5 – Bravo ! Tes premières métriques qualité sont là

Va sur l’onglet QE Score dans ton fichier Google Sheet. Tu verras les données intégrées dans le calcul global du score qualité. Et ce n’est que le début ! 🚀




➕ Étape 6 – Ajoute d'autres projets SonarQube

Tu peux suivre le même processus pour plusieurs projets :

  1. Ajoute de nouvelles lignes dans l’onglet repository_settings

  2. Clique à nouveau sur Refresh Data Sonar

  3. QE Score calcule automatiquement une moyenne par application, même avec plusieurs dépôts Git 👌



🧬 Étape 7 – Personnalise les métriques Sonar récupérées

Tu veux aller plus loin ? Ajoute de nouveaux indicateurs qualité !

  1. Ouvre le script QEscore_Sonar.gs dans Apps Script

  2. Modifie les appels API pour intégrer d'autres métriques (ex: bugs, vulnerabilities, etc.)

  3. Ajoute les colonnes correspondantes dans repository_settings

📌 Attention : le nom des colonnes doit être déclaré en début de script, dans la variable columnIndices.


🧑‍💻 Le script complet

Voici le script principal pour récupérer automatiquement les données qualité depuis Sonar. Tu peux le copier tel quel ou l’adapter à tes besoins 👇 


function QESCORE_getSonar() {
  var sheetSettings = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("repository_settings");
  var rawSettings = 3;
  var headers = sheetSettings.getRange(2, 1, 1, sheetSettings.getLastColumn()).getValues()[0];
  var tokenSonar = PropertiesService.getScriptProperties().getProperty('token_sonar');

  var columnIndices = {
    sonarURL: headers.indexOf("SonarURL"),
    sonarKey: headers.indexOf("SonarKey"),
    error: headers.indexOf("Error"),
    scriptExecution: headers.indexOf("ScriptExecution"),
    codeDuplication: headers.indexOf("CodeDuplication"),
    codeUnitTestCoverage: headers.indexOf("unitTestCoverage"),
    codeQualityGate: headers.indexOf("QualityGate"),
    codeSmell: headers.indexOf("CodeSmell"),
    scanDate: headers.indexOf("SonarScanDate"),
    loc: headers.indexOf("NumLines")
  };

  var todayHour = getTodayHour();

  while (sheetSettings.getRange(rawSettings, columnIndices.sonarKey + 1).getValue() !== "") {
    try {
      var rowData = sheetSettings.getRange(rawSettings, 1, 1, sheetSettings.getLastColumn()).getValues()[0];
      var sonarKey = rowData[columnIndices.sonarKey];
      var sonarURL = rowData[columnIndices.sonarURL];
     
      if (!sonarURL || sonarURL.trim() === "") {
        throw new Error("Missing SonarURL in sheet for row " + rawSettings);
      }

      if (!sonarKey || sonarKey.trim() === "") {
        rawSettings++;
        continue;
      }

      var encodedToken = Utilities.base64Encode(tokenSonar + ":");

      // 1. Duplicated code (%)
      var duplicationUrl = `${sonarURL}/api/measures/component?component=${sonarKey}&metricKeys=duplicated_lines_density`;
      var duplication = fetchSonarMetric(duplicationUrl, encodedToken, "duplicated_lines_density") / 100;

      // 2. Lines of code (ncloc)
      var locUrl = `${sonarURL}/api/measures/component?component=${sonarKey}&metricKeys=ncloc`;
      var loc = fetchSonarMetric(locUrl, encodedToken, "ncloc");

      // 3. Last scan date
      var dateUrl = `${sonarURL}/api/project_analyses/search?project=${sonarKey}&ps=1`;
      var scanDate = fetchSonarDate(dateUrl, encodedToken);

      // 4. Unit test coverage (%)
      var coverageUrl = `${sonarURL}/api/measures/component?component=${sonarKey}&metricKeys=coverage`;
      var coverage = fetchSonarMetric(coverageUrl, encodedToken, "coverage") / 100;

      // 5. Quality Gate status (OK / ERROR)
      var qualityGateUrl = `${sonarURL}/api/qualitygates/project_status?projectKey=${sonarKey}`;
      var qualityGateStatus = fetchSonarQualityGateStatus(qualityGateUrl, encodedToken);

      // 6. Code Smells
      var codeSmellUrl = `${sonarURL}/api/measures/component?component=${sonarKey}&metricKeys=code_smells`;
      var codeSmells = fetchSonarMetric(codeSmellUrl, encodedToken, "code_smells");

      // Writing values to the sheet
      sheetSettings.getRange(rawSettings, columnIndices.codeDuplication + 1).setValue(parseFloat(duplication));
      sheetSettings.getRange(rawSettings, columnIndices.loc + 1).setValue(parseInt(loc));
      sheetSettings.getRange(rawSettings, columnIndices.scanDate + 1).setValue(scanDate);
      sheetSettings.getRange(rawSettings, columnIndices.codeUnitTestCoverage + 1).setValue(parseFloat(coverage));
      sheetSettings.getRange(rawSettings, columnIndices.codeQualityGate + 1).setValue(qualityGateStatus);
      sheetSettings.getRange(rawSettings, columnIndices.codeSmell + 1).setValue(parseInt(codeSmells));
      sheetSettings.getRange(rawSettings, columnIndices.scriptExecution + 1).setValue(todayHour);
    } catch (e) {
      var errorMessage = `${todayHour} - Error: ${e.message}`;
      sheetSettings.getRange(rawSettings, columnIndices.error + 1).setValue(errorMessage);
    }
    rawSettings++;
  }
}

// Fetches a specific metric value from SonarCloud API
function fetchSonarMetric(url, encodedToken, metricKey) {
  var options = {
    'headers': {
      'Authorization': 'Basic ' + encodedToken,
      'Accept': 'application/json'
    }
  };
  var response = UrlFetchApp.fetch(url, options);
  var result = JSON.parse(response.getContentText());
  return result.component.measures[0].value;
}

// Fetches the date of the last analysis
function fetchSonarDate(url, encodedToken) {
  var options = {
    'headers': {
      'Authorization': 'Basic ' + encodedToken,
      'Accept': 'application/json'
    }
  };

  var response = UrlFetchApp.fetch(url, options);
  var result = JSON.parse(response.getContentText());
  var isoDate = result.analyses[0].date;
  var parsedDate = new Date(isoDate);
  return Utilities.formatDate(parsedDate, Session.getScriptTimeZone(), "dd/MM/yyyy HH:mm");
}

// Fetches the Quality Gate status (OK, ERROR, etc.)
function fetchSonarQualityGateStatus(url, encodedToken) {
  var options = {
    'headers': {
      'Authorization': 'Basic ' + encodedToken,
      'Accept': 'application/json'
    }
  };

  var response = UrlFetchApp.fetch(url, options);
  var result = JSON.parse(response.getContentText());
  var status = result.projectStatus.status;
  return status === "OK" ? "PASSED" : "FAILED";
}

function getTodayHour() {
    return Utilities.formatDate(new Date(), SpreadsheetApp.getActive().getSpreadsheetTimeZone(), "dd/MM/yyyy HH:mm");
}

Comments


bottom of page