Skip to main content

Utilisation d’une analyse incrémentielle avec l’interface CLI CodeQL

Obtenez plus rapidement des résultats CodeQL sur les pull requests en analysant uniquement ce qui a changé. L’analyse incrémentielle peut réduire les temps d’analyse jusqu’à 10 fois plus lorsque vous exécutez le CodeQL CLI dans votre propre système CI/CD.

Qui peut utiliser cette fonctionnalité ?

CodeQL est disponible pour les types de référentiels suivants :

À propos de l’analyse incrémentielle

Les analyses complètes CodeQL sur chaque pull request peuvent être lentes, en particulier dans les bases de code volumineuses. Si vous exécutez CodeQL CLI dans votre propre système CI/CD, l’analyse incrémentielle vous permet d’accélérer les choses de deux façons :

  • Analyse basée sur les différences signale uniquement les alertes dans les lignes que vous avez ajoutées ou modifiées, afin que les requêtes s’exécutent plus rapidement et que les résultats soient plus pertinents.
  • L’analyse de superposition réutilise une base de données mise en cache à partir de votre branche par défaut au lieu de créer une base de données à partir de zéro, ce qui réduit considérablement la création de la base de données et le temps d’évaluation des requêtes.

Vous pouvez utiliser ces fonctionnalités indépendamment ou ensemble. Pour la plupart des équipes analysant les demandes de tirage dans des bases de code établies, nous vous recommandons d’utiliser à la fois l’analyse de superposition pour la création rapide de bases de données et l’évaluation des requêtes, ainsi que l’analyse diff-informée pour les résultats ciblés et pertinents.

Si vous utilisez la configuration par défaut code scanning ou l’option codeql-action sur GitHub, l’analyse incrémentielle est déjà gérée automatiquement. Cet article s’adresse aux équipes qui exécutent directement le CodeQL CLI dans leur propre infrastructure CI/CD.

Prerequisites

Avant de configurer l’analyse incrémentielle, veillez à répondre aux exigences suivantes :

  • CodeQL CLI version du bundle : 2.21.0 ou version ultérieure pour l’analyse basée sur les différences ; 2.23.8 ou version ultérieure pour l’analyse par superposition (avec des versions minimales par langue, voir Versions minimales du bundle CLI)
  • La racine source doit se trouver à l’intérieur d’un référentiel Git
  • Git version 2.38.0 ou ultérieure (requis pour l’analyse de superposition, en particulier l’option --format utilisée par git ls-files)
  • Tous les fichiers d’intérêt doivent être suivis par Git (pas dans .gitignore)
  • L’index Git doit refléter avec précision l’arborescence source analysée
  • Mode build : L’analyse de superposition prend uniquement build-mode: none en charge (les builds tracées ne sont pas prises en charge). Go fonctionne avec l’analyse par superposition bien qu’il ne prenne pas explicitement en charge ce mode.

Choix d’une approche

ScénarioDiff-informa mentOverlay
Push de branche par défautNon (pas une demande de tirage)mode de base de superposition
Analyse des PR (première exécution, sans cache)YesNon (exécuter l’analyse complète)
Analyse de PR (avec la base en cache)Yesmode superposition
Branche non-PR, autre que la branche par défautNoNo

Pour obtenir des exemples complets et fonctionnels dans différents systèmes CI, consultez le dépôt exemples de configurations de pipeline CodeQL.

Analyse basée sur les différences

L’analyse basée sur les diffs est une optimisation pour l’analyse des pull requests. Au lieu de signaler toutes les alertes trouvées dans la base de code, elle signale uniquement les alertes présentes sur les lignes ajoutées ou modifiées dans le diff de la pull request.

Étape 1 : Identifier les plages de différences

Vous avez besoin des plages de lignes ajoutées ou modifiées à partir du diff de la pull request. L’entrée peut provenir de n’importe quelle source (git diffAPI de votre plateforme CI ou d’un autre mécanisme).

Pour chaque fichier modifié, produisez une liste de plages avec la structure suivante :

  • path: chemin d’accès absolu au fichier (utilisez toujours des slashs)
  • startLine: ligne de début incluse, indexée à partir de 1
  • endLine: ligne de fin incluse, indexée à partir de 1

Par exemple, étant donné ce diff unifié (généré par git diff) :

--- a/src/utils.ts
+++ b/src/utils.ts
@@ -2,7 +2,6 @@ import { helper } from './helper';
 
 function existing() {
   const x = 1;
-  const unused = 2;
   return x;
 }
 
@@ -14,6 +13,8 @@ function validate(input: string) {
 function process(input: string) {
   // validate
   if (!input) return;
+  const sanitized = input.trim();
+  console.log(sanitized);
   return input;
 }
 
@@ -23,5 +24,5 @@ function format(value: number) {
 
 function render(data: object) {
   const output = JSON.stringify(data);
-  return output;
+  return `<div>${output}</div>`;
 }

Les plages de diff résultantes pour src/utils.ts seraient :

  • ["/path/to/repo/src/utils.ts", 16, 17] (les deux lignes insérées dans le deuxième segment)
  • ["/path/to/repo/src/utils.ts", 27, 27] (ligne modifiée dans le troisième bloc)

Le premier bloc contient uniquement une suppression. Il ne produit donc aucune plage. Notez que les plages utilisent les numéros de ligne « to » correspondant au nouveau fichier, et non les numéros de ligne « from » correspondant à l’ancien fichier.

Cas spéciaux :

  • Fichiers binaires ou différences très volumineuses (aucun contenu de correctif disponible) : utilisez la plage {path, startLine: 0, endLine: 0} sentinelle pour indiquer « fichier entier ».
  • Fichiers renommés sans modification de contenu : renvoyer un tableau vide (aucune plage).
  • Diffs tronqués : si votre source de diffs est incomplète pour les pull requests volumineuses (par exemple, une API qui limite le nombre de fichiers modifiés), vous devez ignorer l’analyse basée sur les diffs et exécuter une analyse complète pour cette exécution.

Pour obtenir une implémentation de référence de l’analyse des différences, consultez getDiffRanges() le codeql-action code source.

Étape 2 : Créer un pack d’extensions de données

Créez un répertoire temporaire contenant deux fichiers. Ce pack d’extension alimente le restrictAlertsTo prédicat extensible défini dans la CodeQL bibliothèque standard.

** qlpack.yml :**

name: my-ci/pr-diff-range
version: 0.0.0
library: true
extensionTargets:
  codeql/util: '*'  # Target the codeql/util pack where restrictAlertsTo is defined
dataExtensions:
  - pr-diff-range.yml

** pr-diff-range.yml :**

extensions:
  - addsTo:
      pack: codeql/util
      extensible: restrictAlertsTo
      checkPresence: false  # Don't error if the predicate doesn't exist in older CLI versions
    data:
      # Each row: [filePath, startLine, endLine]
      - ["/path/to/repo/src/utils.ts", 16, 17]
      - ["/path/to/repo/src/utils.ts", 27, 27]

Chaque ligne de données est [filePath, lineStart, lineEnd]. Les numéros de ligne commencent à 1. Le cas lineStart = 0, lineEnd = 0 spécial désigne une correspondance dans un fichier entier.

Important

Si le diff ne comporte aucune ligne ajoutée ou modifiée (par exemple, uniquement des suppressions), vous devez tout de même fournir une extension de données non vide avec une entrée sentinelle ["", 0, 0]. Une section vide data laisserait le restrictAlertsTo prédicat inactif, ce qui signifie que toutes les alertes seraient produites, à l’opposé du comportement souhaité.

Étape 3 : Passer le pack d’extension au CodeQL CLI

Lors de l’exécution de requêtes, ajoutez les indicateurs suivants à codeql database run-queries:

codeql database run-queries \
  --additional-packs=PATH_TO_EXTENSION_PACK \
  --extension-packs=my-ci/pr-diff-range \
  PATH_TO_DATABASE \
  QUERIES
  • --additional-packs indique CodeQL où trouver le pack sur le disque. Pour plus d’informations, consultez « requêtes d’exécution de base de données ».
  • --extension-packs indique CodeQL de charger le pack d’extension nommé.

Étape 4 : Exclure les requêtes de diagnostic

Lorsque vous utilisez une analyse diff-informée, vous devez exclure les requêtes marquées avec exclude-from-incremental. Ces requêtes de diagnostic ne produisent pas d’alertes (par exemple, des métriques ou une couverture du code), de sorte qu’elles ne fournissent aucune valeur dans un contexte incrémentiel, mais consomment toujours des ressources.

Vous pouvez l’ajouter à votre fichier de configuration d’analyse du code :

query-filters:
  - exclude:
      tags: exclude-from-incremental

Vous pouvez également créer un fichier de suite de requêtes (.qls) qui exclut ces requêtes :

- description: Pull request queries for Java
- import: codeql-suites/java-code-scanning.qls
  from: codeql/java-queries
- exclude:
    tags contain: exclude-from-incremental

Pour plus d’informations, consultez « Options de configuration de flux de travail pour l’analyse du code ».

Étape 5 : Filtrer la sortie SARIF

Après CodeQL avoir généré le fichier SARIF, vous devez filtrer la sortie côté CI pour supprimer les résultats dont les emplacements se trouvent en dehors des plages de différences.

Pour chaque résultat du SARIF, vérifiez si l’un de ses locations ou relatedLocations intersecte une plage de différences pour ce fichier. Une position intersecte un intervalle lorsque range.startLine <= location.endLine et location.startLine <= range.endLine. Le cas range.startLine == range.endLine == 0 spécial correspond à n’importe quel emplacement dans le fichier. Assurez-vous que les emplacements des artefacts SARIF sont résolus dans le même format de chemin absolu que celui utilisé dans les plages de diff avant de comparer.

Le restrictAlertsTo prédicat permet, sans toutefois garantir, que les requêtes omettent les alertes en dehors de la plage, donc un filtrage côté CI est nécessaire pour obtenir des résultats stables.

Pour une implémentation de référence du filtrage SARIF, consultez filterAlertsByDiffRange() dans le code source codeql-action.

Synthèse des options de ligne de commande pour l’analyse basée sur les différences

Commande CLIFlagPurpose
codeql database init--codescanning-config=FILEFichier de configuration de l’analyse du code (pour le filtre de requête)
codeql database run-queries--additional-packs=DIREmplacement du pack d’extension
codeql database run-queries--extension-packs=my-ci/pr-diff-rangeNom du pack d’extension à charger
codeql database interpret-results--sarif-run-property=incrementalMode=diff-informed(Facultatif) Balise SARIF avec des métadonnées diff-informées

Analyse de superposition

L’analyse de superposition accélère la CodeQL création de la base de données et l’évaluation des requêtes pour les demandes d’extraction en s’appuyant sur une base de données « base » préexistante :

  1. Dans la branche par défaut : Créer une base de données « overlay-base » (une base de données complète avec mise en cache des résultats intermédiaires). Il peut s’agir de n’importe quelle branche de longue durée que ciblent les pull requests.
  2. Pour les pull requests : Téléchargez la base de données overlay-base en cache, puis créez une base de données overlay légère qui traite uniquement les fichiers modifiés.

Mode de base de superposition (branche par défaut)

Exécutez le mode de base de superposition sur votre branche cible par défaut ou de longue durée après chaque fusion pour créer et mettre en cache une base de données de base.

1. Initialiser la base de données avec --overlay-base

codeql database init \
  --overlay-base \
  --db-cluster \
  PATH_TO_DATABASE \
  --source-root=PATH_TO_SOURCE \
  --language=LANGUAGE

L’indicateur --overlay-base indique CodeQL de créer une base de données qui peut servir de base pour l’analyse future des superpositions.

2. Compiler et extraire comme d’habitude

Exécutez les étapes de génération et l’extraction comme vous le feriez normalement pour votre projet.

3. Enregistrer les OID du fichier

Une fois l’extraction terminée, enregistrez les ID d’objet Git (OID) de tous les fichiers suivis sous la racine source. Exécutez cette commande à partir de votre répertoire racine source (PATH_TO_SOURCE). Cet instantané est utilisé ultérieurement pour déterminer les fichiers modifiés.

cd PATH_TO_SOURCE && git ls-files --recurse-submodules --format='%(objectname)_%(path)'

Analysez cette sortie sous forme de mappage JSON de { "relative/path": "git-oid" } et stockez-la avec la base de données. La sortie inclut des fichiers dans les sous-modules Git, que l’analyse de la couche de superposition doit suivre avec précision afin de prendre en compte toutes les modifications apportées aux fichiers entre la base et la couche de superposition.

4. Exécuter des requêtes et conserver le cache

Lors de l’exécution de requêtes sur une base de données overlay-base, ne pas transmettre --expect-discarded-cache. Les résultats intermédiaires mis en cache sont ce qui rend les builds de pull request rapides. Les supprimer imposerait une réévaluation complète à chaque PR.

5. Nettoyer et mettre en cache la base de données

Après analyse, nettoyez la base de données à l’aide du overlay niveau de nettoyage :

codeql database cleanup PATH_TO_DATABASE --cache-cleanup=overlay

Le overlay niveau de nettoyage conserve plus de données mises en cache que le niveau par défaut clear . Le mode de superposition réutilise ces données mises en cache pour évaluer efficacement les requêtes sur les pull requests ; les supprimer ferait donc disparaître le gain de performances.

Stockez ensuite la base de données (y compris le fichier OIDs) dans votre système de mise en cache pour pouvoir la récupérer ultérieurement lors des builds de pull request.

Mode superposition (demandes de tirage)

Exécutez le mode overlay pour les builds de pull request afin de créer une base de données légère sur la base mise en cache. Si aucune base de données de base de superposition compatible n’est disponible dans le cache (par exemple, lors de la première exécution ou après une CodeQL CLI mise à niveau de version), ignorez --overlay-changes et exécutez plutôt une analyse complète normale. Les clés de cache doivent inclure au moins la version et la CodeQL CLI langue définies pour éviter les bases de données de base incompatibles.

1. Télécharger la base de données de base de superposition mise en cache

Récupérez la base de données de base de superposition la plus récente à partir de votre cache. La base de données doit inclure le fichier d’OID enregistré pendant le mode de superposition de base.

2. Calcul des fichiers modifiés

Comparez les OID enregistrés dans la base de données de base avec l’état Git actuel. Exécutez cette commande à partir du même répertoire racine source (PATH_TO_SOURCE) utilisé lors du mode de superposition de base :

cd PATH_TO_SOURCE && git ls-files --recurse-submodules --format='%(objectname)_%(path)'

Comparez les deux mappages pour rechercher des fichiers qui ont été ajoutés, supprimés ou modifiés (différents OID). Écrivez le résultat sous la forme d’un fichier JSON :

{
  "changes": ["src/modified-file.ts", "src/new-file.ts", "src/deleted-file.ts"]
}

Les chemins d’accès au fichier doivent être relatifs à la racine source.

3. Initialiser la base de données avec --overlay-changes

Exécutez codeql database init sur le répertoire de base de données overlay-base restauré. Le PATH_TO_DATABASE doit pointer vers la base de données overlay-base restaurée depuis le cache, et non vers un nouveau répertoire vide — la commande étend la base existante pour l’analyse des pull requests.

codeql database init \
  --overlay-changes=PATH_TO_OVERLAY_CHANGES_JSON \
  --db-cluster \
  PATH_TO_DATABASE \
  --source-root=PATH_TO_SOURCE \
  --language=LANGUAGE

Important

En mode de superposition, ne transmettez ni --overwrite ni --force-overwrite. Vous créez sur la base de données de base mise en cache existante, et vous ne la remplacez pas.

4. Générer, extraire et exécuter des requêtes comme normales

Procédez à la compilation, à l’extraction et à l’exécution des requêtes comme d’habitude. Vous pouvez ajouter l’indicateur --sarif-run-property à votre commande existante codeql database interpret-results pour baliser la sortie SARIF avec des métadonnées de superposition :

codeql database interpret-results \
  --format=sarif-latest \
  --output=results.sarif \
  --sarif-run-property=incrementalMode=overlay \
  PATH_TO_DATABASE \
  QUERIES_OR_SUITES

Si la superposition et l’analyse basée sur les différences sont activées, utilisez incrementalMode=overlay,diff-informed.

Les alertes de l’analyse incrémentielle apparaissent dans les résultats de l’analyse du code de la pull request de la même manière que les alertes issues d’analyses complètes. Toute base de données de superposition fonctionne indépendamment de l’âge, mais les bases plus fraîches produisent des résultats plus rapides et plus précis.

Comme avec l’analyse diff-informée, excluez les requêtes marquées exclude-from-incremental lors de l’utilisation du mode superposition. Pour plus d’informations, consultez l’étape 4 : Exclure les requêtes de diagnostic.

Résumé des indicateurs CLI pour l’analyse de superposition

Commande CLIFlagModePurpose
codeql database init--codescanning-config=FILESuperpositionFichier de configuration de l’analyse du code (pour le filtre de requête)
codeql database init--overlay-basesuperposition de baseCréer une base de données de base pour une utilisation future de superposition
codeql database init--overlay-changes=FILESuperpositionGénérer une base de données de superposition à l’aide de fichiers modifiés uniquement
codeql database init
(aucun --overwrite)SuperpositionNe remplacez pas la base de données de base mise en cache
codeql database run-queries
(aucun --expect-discarded-cache)superposition de baseConserver les résultats intermédiaires mis en cache
codeql database cleanup--cache-cleanup=overlaysuperposition de baseUtiliser le niveau de nettoyage spécifique à la superposition
codeql database interpret-results--sarif-run-property=incrementalMode=overlaySuperpositionÉtiqueter SARIF avec des métadonnées de superposition

Versions minimales du bundle CLI

La version minimale de base pour l’analyse de superposition est 2.23.8. Certaines langues nécessitent des versions minimales supérieures :

LanguageVersion minimale du bundle CodeQL CLI
C/C++2.25.0
C#2.24.1
Go2.24.2
Java2.23.8
Javascript2.23.9
Python2.23.9
Ruby2.23.9