Add getGroupedSarifFilePaths with tests

This commit is contained in:
Michael B. Gale
2025-09-29 11:19:30 +01:00
parent 36adfa7b0f
commit fe0376ed1f
7 changed files with 225 additions and 27 deletions
+6
View File
@@ -86,3 +86,9 @@ export const CodeQuality: AnalysisConfig = {
sarifPredicate: (name) => name.endsWith(CodeQuality.sarifExtension),
sentinelPrefix: "CODEQL_UPLOAD_QUALITY_SARIF_",
};
// Since we have overlapping extensions (i.e. ".sarif" includes ".quality.sarif"),
// we want to scan a folder containing SARIF files in an order that finds the more
// specific extensions first. This constant defines an array in the order of analyis
// configurations with more specific extensions to less specific extensions.
export const SarifScanOrder = [CodeQuality, CodeScanning];
+81 -11
View File
@@ -3,7 +3,7 @@ import * as path from "path";
import test from "ava";
import { CodeQuality, CodeScanning } from "./analyses";
import { AnalysisKind, CodeQuality, CodeScanning } from "./analyses";
import { getRunnerLogger, Logger } from "./logging";
import { setupTests } from "./testing-utils";
import * as uploadLib from "./upload-lib";
@@ -127,27 +127,97 @@ test("finding SARIF files", async (t) => {
fs.writeFileSync(path.join(tmpDir, "a.quality.sarif"), "");
fs.writeFileSync(path.join(tmpDir, "dir1", "b.quality.sarif"), "");
const expectedSarifFiles = [
path.join(tmpDir, "a.sarif"),
path.join(tmpDir, "b.sarif"),
path.join(tmpDir, "dir1", "d.sarif"),
path.join(tmpDir, "dir1", "dir2", "e.sarif"),
];
const sarifFiles = uploadLib.findSarifFilesInDir(
tmpDir,
CodeScanning.sarifPredicate,
);
t.deepEqual(sarifFiles, [
path.join(tmpDir, "a.sarif"),
path.join(tmpDir, "b.sarif"),
path.join(tmpDir, "dir1", "d.sarif"),
path.join(tmpDir, "dir1", "dir2", "e.sarif"),
]);
t.deepEqual(sarifFiles, expectedSarifFiles);
const expectedQualitySarifFiles = [
path.join(tmpDir, "a.quality.sarif"),
path.join(tmpDir, "dir1", "b.quality.sarif"),
];
const qualitySarifFiles = uploadLib.findSarifFilesInDir(
tmpDir,
CodeQuality.sarifPredicate,
);
t.deepEqual(qualitySarifFiles, [
path.join(tmpDir, "a.quality.sarif"),
path.join(tmpDir, "dir1", "b.quality.sarif"),
]);
t.deepEqual(qualitySarifFiles, expectedQualitySarifFiles);
const groupedSarifFiles = await uploadLib.getGroupedSarifFilePaths(
getRunnerLogger(true),
tmpDir,
);
t.not(groupedSarifFiles, undefined);
t.not(groupedSarifFiles[AnalysisKind.CodeScanning], undefined);
t.not(groupedSarifFiles[AnalysisKind.CodeQuality], undefined);
t.deepEqual(
groupedSarifFiles[AnalysisKind.CodeScanning],
expectedSarifFiles,
);
t.deepEqual(
groupedSarifFiles[AnalysisKind.CodeQuality],
expectedQualitySarifFiles,
);
});
});
test("getGroupedSarifFilePaths - Code Quality file", async (t) => {
await withTmpDir(async (tmpDir) => {
const sarifPath = path.join(tmpDir, "a.quality.sarif");
fs.writeFileSync(sarifPath, "");
const groupedSarifFiles = await uploadLib.getGroupedSarifFilePaths(
getRunnerLogger(true),
sarifPath,
);
t.not(groupedSarifFiles, undefined);
t.is(groupedSarifFiles[AnalysisKind.CodeScanning], undefined);
t.not(groupedSarifFiles[AnalysisKind.CodeQuality], undefined);
t.deepEqual(groupedSarifFiles[AnalysisKind.CodeQuality], [sarifPath]);
});
});
test("getGroupedSarifFilePaths - Code Scanning file", async (t) => {
await withTmpDir(async (tmpDir) => {
const sarifPath = path.join(tmpDir, "a.sarif");
fs.writeFileSync(sarifPath, "");
const groupedSarifFiles = await uploadLib.getGroupedSarifFilePaths(
getRunnerLogger(true),
sarifPath,
);
t.not(groupedSarifFiles, undefined);
t.not(groupedSarifFiles[AnalysisKind.CodeScanning], undefined);
t.is(groupedSarifFiles[AnalysisKind.CodeQuality], undefined);
t.deepEqual(groupedSarifFiles[AnalysisKind.CodeScanning], [sarifPath]);
});
});
test("getGroupedSarifFilePaths - Other file", async (t) => {
await withTmpDir(async (tmpDir) => {
const sarifPath = path.join(tmpDir, "a.json");
fs.writeFileSync(sarifPath, "");
const groupedSarifFiles = await uploadLib.getGroupedSarifFilePaths(
getRunnerLogger(true),
sarifPath,
);
t.not(groupedSarifFiles, undefined);
t.not(groupedSarifFiles[AnalysisKind.CodeScanning], undefined);
t.is(groupedSarifFiles[AnalysisKind.CodeQuality], undefined);
t.deepEqual(groupedSarifFiles[AnalysisKind.CodeScanning], [sarifPath]);
});
});
+62
View File
@@ -459,6 +459,68 @@ export function getSarifFilePaths(
return sarifFiles;
}
/**
* Finds SARIF files in `sarifPath`, and groups them by analysis kind, following `SarifScanOrder`.
*
* @param logger The logger to use.
* @param sarifPath The path of a file or directory to recursively scan for SARIF files.
* @returns The `.sarif` files found in `sarifPath`, grouped by analysis kind.
*/
export async function getGroupedSarifFilePaths(
logger: Logger,
sarifPath: string,
): Promise<Partial<Record<analyses.AnalysisKind, string[]>>> {
const stats = fs.statSync(sarifPath, { throwIfNoEntry: false });
if (stats === undefined) {
// This is always a configuration error, even for first-party runs.
throw new ConfigurationError(`Path does not exist: ${sarifPath}`);
}
const results = {};
if (stats.isDirectory()) {
let sarifFiles = findSarifFilesInDir(sarifPath, (name) =>
name.endsWith(".sarif"),
);
logger.debug(
`Found the following .sarif files in ${sarifPath}: ${sarifFiles.join(", ")}`,
);
for (const analysisConfig of analyses.SarifScanOrder) {
const files = sarifFiles.filter(analysisConfig.sarifPredicate);
if (files.length > 0) {
logger.debug(
`The following SARIF files are for ${analysisConfig.name}: ${files.join(", ")}`,
);
// Looping through the array a second time is not efficient, but more readable.
// Change this to one loop for both calls to `filter` if this becomes a bottleneck.
sarifFiles = sarifFiles.filter(
(name) => !analysisConfig.sarifPredicate(name),
);
results[analysisConfig.kind] = files;
} else {
logger.debug(`Found no SARIF files for ${analysisConfig.name}`);
}
}
} else {
for (const analysisConfig of analyses.SarifScanOrder) {
if (
analysisConfig.kind === analyses.AnalysisKind.CodeScanning ||
analysisConfig.sarifPredicate(sarifPath)
) {
logger.debug(
`Using '${sarifPath}' as a SARIF file for ${analysisConfig.name}.`,
);
results[analysisConfig.kind] = [sarifPath];
break;
}
}
}
return results;
}
// Counts the number of results in the given SARIF file
function countResultsInSarif(sarif: string): number {
let numResults = 0;