Acerca del análisis incremental
Los exámenes completos CodeQL en cada solicitud de incorporación de cambios pueden ser lentos, especialmente en código base de gran tamaño. Si ejecuta CodeQL CLI en su propio sistema de CI/CD, el análisis incremental le ofrece dos formas de acelerar el proceso:
- Análisis basado en diferencias muestra alertas solo en las líneas añadidas o modificadas, por lo que las consultas se ejecutan más rápido y los resultados son más relevantes.
- El análisis de superposición reutiliza una base de datos almacenada en caché de la rama predeterminada en lugar de crear una desde cero, lo que reduce considerablemente el tiempo de creación de bases de datos y evaluación de consultas.
Puede usar estas características de forma independiente o conjunta. Para la mayoría de los equipos que analizan solicitudes de extracción en bases de código consolidadas, recomendamos usar ambos: el análisis de superposición para crear bases de datos rápidamente y evaluar consultas, y el análisis basado en diferencias para obtener resultados específicos y relevantes.
Si usa code scanning la configuración predeterminada o la opción codeql-action en GitHub, el análisis incremental ya se gestiona automáticamente. Este artículo es para los equipos que ejecutan CodeQL CLI directamente en su propia infraestructura de CI/CD.
Prerequisites
Antes de configurar el análisis incremental, asegúrese de cumplir los siguientes requisitos:
- CodeQL CLI versión del paquete: 2.21.0 o una versión posterior para el análisis basado en diferencias; 2.23.8 o una versión posterior para el análisis de superposiciones (con mínimos por idioma, consulte Versiones mínimas del paquete de la CLI)
- El directorio raíz de origen debe estar dentro de un repositorio de Git.
- Git versión 2.38.0 o posterior (necesaria para el análisis de superposición, específicamente la
--formatopción usada porgit ls-files) - Git debe realizar un seguimiento de todos los archivos de interés (no en
.gitignore) - El índice de Git debe reflejar con precisión el árbol de origen que se está analizando
- Modo de compilación: El análisis de superposiciones solo admite
build-mode: none(no se admiten compilaciones con seguimiento). Go funciona con análisis de superposición a pesar de no admitir explícitamente este modo.
Elección de un enfoque
| Scenario | Basado en diferencias | Overlay |
|---|---|---|
| Envío a la rama predeterminada | No (no es una PR) | modo de superposición base |
| Análisis de PR (primera vez, sin caché) | Yes | No (ejecutar análisis completos) |
| Análisis de PR (con base almacenada en caché) | Yes | modo de superposición |
| No PR, rama no predeterminada | No | No |
Para ver ejemplos completos y funcionales en varios sistemas de CI, consulte el repositorio de configuraciones de canalización de CodeQL de ejemplo.
Análisis basado en diferencias
El análisis basado en diferencias es una optimización para el análisis de pull requests. En lugar de informar de todas las alertas encontradas en la base de código, solo informa de las alertas en las líneas que se han añadido o modificado en el diff de la solicitud de incorporación de cambios.
Paso 1: Identificar los intervalos de diferencias
Necesitas los rangos de líneas añadidos o modificados del diff de la solicitud de extracción. La entrada puede proceder de cualquier origen (git diff, la API de la plataforma de CI u otro mecanismo).
Para cada archivo cambiado, genere una lista de rangos con la siguiente estructura:
path: Ruta de acceso absoluta del archivo (utilice siempre barras diagonales)startLine: línea de inicio inclusiva basada en 1endLine: línea final inclusiva basada en 1
Por ejemplo, dada esta diferencia unificada (generada por 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>`;
}
Los intervalos de diferencias resultantes para src/utils.ts serían:
["/path/to/repo/src/utils.ts", 16, 17](las dos líneas añadidas en el segundo hunk)["/path/to/repo/src/utils.ts", 27, 27](la línea modificada en el tercer bloque)
El primer hunk contiene solo una eliminación, por lo que no genera ningún rango. Tenga en cuenta que los rangos usan los números de línea de «destino» (archivo nuevo), no los números de línea de «origen» (archivo anterior).
Casos especiales:
- Archivos binarios o diferencias de gran tamaño (sin contenido del parche disponible): Use el intervalo centinela
{path, startLine: 0, endLine: 0}para indicar "el archivo completo". - Archivos renombrados sin cambios en el contenido: Devolver un array vacío (sin rangos).
- Diffs truncados: si la fuente del diff está incompleta para pull requests grandes (por ejemplo, una API que limita el número de archivos modificados), debe omitir el análisis basado en diffs y ejecutar un análisis completo para esa ejecución.
Para obtener una implementación de referencia del análisis de diferencias, consulte getDiffRanges() en el codeql-action código fuente.
Paso 2: Crear un paquete de extensiones de datos
Cree un directorio temporal que contenga dos archivos. Este paquete de extensiones se alimenta del restrictAlertsTo predicado extensible definido en la CodeQL biblioteca estándar.
**
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]
Cada fila de datos es [filePath, lineStart, lineEnd]. Los números de línea están basados en 1. El caso especial lineStart = 0, lineEnd = 0 denota una coincidencia con el archivo completo.
Importante
Si el diff tiene cero líneas añadidas o modificadas (por ejemplo, solo eliminaciones), debe seguir proporcionando una extensión de datos no vacía con una entrada centinela ["", 0, 0]. Una sección vacía data dejaría inactivo el restrictAlertsTo predicado, lo que significa que se generarían todas las alertas, lo contrario al comportamiento deseado.
Paso 3: Pasar el paquete de extensiones al CodeQL CLI
Al ejecutar consultas, agregue las marcas siguientes a 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-packsindica CodeQL dónde buscar el paquete en el disco. Para obtener más información, vea ejecución de consultas en la base de datos.--extension-packsindica CodeQL que cargue el paquete de extensiones con nombre.
Paso 4: Excluir consultas de diagnóstico
Al utilizar el análisis basado en diferencias, debe excluir las consultas etiquetadas con exclude-from-incremental. Estas consultas de diagnóstico no generan alertas (por ejemplo, métricas o cobertura de código), por lo que no proporcionan ningún valor en un contexto incremental, pero siguen usando recursos.
Puede agregarlo al archivo de configuración de examen de código:
query-filters:
- exclude:
tags: exclude-from-incremental
Como alternativa, cree un archivo de conjunto de consultas (.qls) que excluya esas consultas:
- description: Pull request queries for Java
- import: codeql-suites/java-code-scanning.qls
from: codeql/java-queries
- exclude:
tags contain: exclude-from-incremental
Para obtener más información, vea Opciones de configuración de flujo de trabajo para el examen de código.
Paso 5: Filtrar la salida de SARIF
Después de que CodeQL genere el archivo SARIF, debe filtrar la salida en la parte de CI para eliminar los resultados cuyas ubicaciones queden fuera de los rangos del diff.
Para cada resultado del SARIF, compruebe si alguno de sus locations o relatedLocations se cruza con un intervalo de diferencias para ese archivo. Una ubicación interseca un intervalo cuando range.startLine <= location.endLine y location.startLine <= range.endLine. El caso range.startLine == range.endLine == 0 especial coincide con cualquier ubicación del archivo. Asegúrese de que las ubicaciones de artefactos SARIF se resuelvan al mismo formato de ruta de acceso absoluta que se usa en los rangos de diferencias antes de comparar.
El predicado restrictAlertsTo permite, pero no garantiza, que las consultas omitan las alertas fuera del intervalo, por lo que es necesario filtrar en el lado de CI para obtener resultados estables.
Para obtener una implementación de referencia del filtrado SARIF, consulte filterAlertsByDiffRange() en el codeql-action código fuente.
Resumen de las opciones de la CLI para el análisis basado en diferencias
| Comando de la CLI | Flag | Purpose |
|---|---|---|
codeql database init | --codescanning-config=FILE | Archivo de configuración de examen de código (para el filtro de consulta) |
codeql database run-queries | --additional-packs=DIR | Ubicación del paquete de extensiones |
codeql database run-queries | --extension-packs=my-ci/ | Nombre del paquete de extensiones que se va a cargar |
codeql database interpret-results | --sarif-run-property=incremental | (Opcional) Etiquetar SARIF con metadatos basados en diferencias |
Análisis de superposición
El análisis superpuesto acelera la CodeQL creación de bases de datos y la evaluación de consultas para las solicitudes de extracción al basarse en una base de datos "base" preexistente:
- En la rama predeterminada: Cree una base de datos "overlay-base" (una base de datos completa con resultados intermedios almacenados en caché). Puede ser cualquier rama de larga duración que tenga como destino las solicitudes de incorporación de cambios.
- En las solicitudes de incorporación de cambios: Descargue la base de datos superpuesta almacenada en caché y, a continuación, cree una base de datos ligera de "superposición" que solo procese los archivos modificados.
Modo de base de superposición (rama predeterminada)
Ejecute el modo overlay-base en la rama de destino predeterminada o persistente después de cada fusión para crear y almacenar en caché una base de datos de referencia.
1. Inicializar la base de datos con --overlay-base
codeql database init \
--overlay-base \
--db-cluster \
PATH_TO_DATABASE \
--source-root=PATH_TO_SOURCE \
--language=LANGUAGE
La --overlay-base marca indica CodeQL que cree una base de datos que pueda servir como base para el análisis de superposición futuro.
2. Compilar y extraer como de costumbre
Ejecute los pasos de compilación y la extracción como lo haría normalmente para el proyecto.
3. Registrar los OID del archivo
Una vez completada la extracción, registre los identificadores de objeto de Git (OID) de todos los archivos con seguimiento en la raíz de origen. Ejecute este comando desde el directorio raíz de origen (PATH_TO_SOURCE). Esta instantánea se usa más adelante para determinar qué archivos han cambiado.
cd PATH_TO_SOURCE && git ls-files --recurse-submodules --format='%(objectname)_%(path)'
Analice esta salida en un mapa JSON de { "relative/path": "git-oid" } y almacénelo junto con la base de datos. La salida incluye archivos en submódulos de Git, que el análisis de la superposición debe tener en cuenta para rastrear con precisión todos los cambios en los archivos entre la base y la superposición.
4. Ejecutar consultas y conservar la memoria caché
Al ejecutar consultas en una base de datos superpuesta, no pase --expect-discarded-cache. Los resultados intermedios en caché son los que hacen que las compilaciones de pull request sean rápidas. Descartarlos obligaría a realizar una reevaluación completa en cada PR.
5. Limpiar y almacenar en caché la base de datos
Después del análisis, limpie la base de datos mediante el overlay nivel de limpieza:
codeql database cleanup PATH_TO_DATABASE --cache-cleanup=overlay
El overlay nivel de limpieza conserva más datos almacenados en caché que el nivel predeterminado clear . El modo de superposición reutiliza estos datos almacenados en caché para una evaluación eficaz de consultas en las solicitudes de incorporación de cambios, por lo que descartarlo eliminaría la ventaja de rendimiento.
A continuación, almacene la base de datos (incluido el archivo OIDs) en el sistema de almacenamiento en caché para su recuperación posterior mediante compilaciones de solicitudes de incorporación de cambios.
Modo de superposición (solicitudes de extracción)
Ejecute el modo de superposición en compilaciones de solicitudes de incorporación de cambios para crear una base de datos ligera sobre la base almacenada en caché. Si no hay ninguna base de datos base de superposición compatible disponible en la memoria caché (por ejemplo, en la primera ejecución o después de una CodeQL CLI actualización de versión), omita --overlay-changes y ejecute un análisis completo normal en su lugar. Las claves de caché deben incluir al menos la versión CodeQL CLI y el idioma configurado para evitar bases de datos incompatibles.
1. Descargar la base de datos superpuesta almacenada en caché
Recupere la base de datos base de superposición más reciente de la memoria caché. La base de datos debe incluir el archivo de OIDs registrado durante el modo overlay-base.
2. Calcular archivos modificados
Compare los OID registrados en la base de datos base con el estado de Git actual. Ejecute este comando desde el mismo directorio raíz de origen (PATH_TO_SOURCE) usado durante el modo de superposición base:
cd PATH_TO_SOURCE && git ls-files --recurse-submodules --format='%(objectname)_%(path)'
Compare los dos mapas para buscar archivos que se agregaron, quitaron o modificaron (OID diferentes). Escriba el resultado como un archivo JSON:
{
"changes": ["src/modified-file.ts", "src/new-file.ts", "src/deleted-file.ts"]
}
Las rutas de archivo deben ser relativas a la raíz del código fuente.
3. Inicializar la base de datos con --overlay-changes
Ejecute codeql database init en el directorio de base de datos base de superposición restaurado.
PATH_TO_DATABASE debe apuntar a la base de datos superpuesta almacenada en caché restaurada, no a un nuevo directorio vacío; el comando extiende la base existente para el análisis de solicitudes de incorporación de cambios.
codeql database init \
--overlay-changes=PATH_TO_OVERLAY_CHANGES_JSON \
--db-cluster \
PATH_TO_DATABASE \
--source-root=PATH_TO_SOURCE \
--language=LANGUAGE
Importante
En el modo de superposición, no pase --overwrite ni --force-overwrite. Está trabajando sobre la base de datos base existente almacenada en caché, no reemplazándola.
4. Cree, extraiga y ejecute consultas como de costumbre
Continúe con la compilación, extracción y ejecución de consultas como normal. Puede añadir la marca --sarif-run-property a su comando codeql database interpret-results existente para etiquetar la salida SARIF con metadatos de superposición:
codeql database interpret-results \
--format=sarif-latest \
--output=results.sarif \
--sarif-run-property=incrementalMode=overlay \
PATH_TO_DATABASE \
QUERIES_OR_SUITES
Si tanto la superposición como el análisis basado en diferencias están activos, use incrementalMode=overlay,diff-informed.
Las alertas procedentes de un análisis incremental aparecen en los resultados del análisis de código de la pull request de la misma forma que las alertas de los análisis completos. Cualquier base de datos base de superposición funcionará independientemente de la edad, pero las bases más frescas producen resultados más rápidos y precisos.
Al igual que con el análisis basado en diferencias, excluya las consultas etiquetadas exclude-from-incremental al usar el modo superpuesto. Para obtener más información, consulte Paso 4: Excluir consultas de diagnóstico.
Resumen de los indicadores de la línea de comandos para el análisis de superposición
| Comando de la CLI | Flag | Mode | Purpose |
|---|---|---|---|
codeql database init | --codescanning-config=FILE | superposición | Archivo de configuración de examen de código (para el filtro de consulta) |
codeql database init | --overlay-base | overlay-base | Crear una base de datos básica para futuras superposiciones |
codeql database init | --overlay-changes=FILE | superposición | Compilación de una base de datos superpuesta con solo archivos modificados |
codeql database init | |||
(no --overwrite) | superposición | No sobrescriba la base de datos base almacenada en caché | |
codeql database run-queries | |||
(no --expect-discarded-cache) | overlay-base | Conservar los resultados intermedios almacenados en caché | |
codeql database cleanup | --cache-cleanup=overlay | overlay-base | Uso del nivel de limpieza específico de la superposición |
codeql database interpret-results | --sarif-run-property=incremental | superposición | Etiqueta SARIF con metadatos de superposición |
Versiones mínimas del paquete de la CLI
La versión mínima base para el análisis de superposición es 2.23.8. Algunos idiomas requieren versiones mínimas superiores:
| Language | Versión mínima del paquete CodeQL CLI |
|---|---|
| C/C++ | 2.25.0 |
| C# | 2.24.1 |
| Ir | 2.24.2 |
| Java | 2.23.8 |
| JavaScript | 2.23.9 |
| Python | 2.23.9 |
| Ruby | 2.23.9 |