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
+2 -2
View File
@@ -28149,11 +28149,11 @@ var require_out = __commonJS({
async.read(path20, getSettings(optionsOrSettingsOrCallback), callback);
}
exports2.stat = stat;
function statSync3(path20, optionsOrSettings) {
function statSync4(path20, optionsOrSettings) {
const settings = getSettings(optionsOrSettings);
return sync.read(path20, settings);
}
exports2.statSync = statSync3;
exports2.statSync = statSync4;
function getSettings(settingsOrOptions = {}) {
if (settingsOrOptions instanceof settings_1.default) {
return settingsOrOptions;
+2 -2
View File
@@ -28149,11 +28149,11 @@ var require_out = __commonJS({
async.read(path19, getSettings(optionsOrSettingsOrCallback), callback);
}
exports2.stat = stat;
function statSync2(path19, optionsOrSettings) {
function statSync3(path19, optionsOrSettings) {
const settings = getSettings(optionsOrSettings);
return sync.read(path19, settings);
}
exports2.statSync = statSync2;
exports2.statSync = statSync3;
function getSettings(settingsOrOptions = {}) {
if (settingsOrOptions instanceof settings_1.default) {
return settingsOrOptions;
+70 -10
View File
@@ -29446,11 +29446,11 @@ var require_out = __commonJS({
async.read(path15, getSettings(optionsOrSettingsOrCallback), callback);
}
exports2.stat = stat;
function statSync2(path15, optionsOrSettings) {
function statSync3(path15, optionsOrSettings) {
const settings = getSettings(optionsOrSettings);
return sync.read(path15, settings);
}
exports2.statSync = statSync2;
exports2.statSync = statSync3;
function getSettings(settingsOrOptions = {}) {
if (settingsOrOptions instanceof settings_1.default) {
return settingsOrOptions;
@@ -84821,6 +84821,7 @@ __export(upload_lib_exports, {
InvalidSarifUploadError: () => InvalidSarifUploadError,
buildPayload: () => buildPayload,
findSarifFilesInDir: () => findSarifFilesInDir,
getGroupedSarifFilePaths: () => getGroupedSarifFilePaths,
getSarifFilePaths: () => getSarifFilePaths,
populateRunAutomationDetails: () => populateRunAutomationDetails,
readSarifFile: () => readSarifFile,
@@ -88530,6 +88531,31 @@ async function runTool(cmd, args = [], opts = {}) {
return stdout;
}
// src/analyses.ts
var AnalysisKind = /* @__PURE__ */ ((AnalysisKind2) => {
AnalysisKind2["CodeScanning"] = "code-scanning";
AnalysisKind2["CodeQuality"] = "code-quality";
return AnalysisKind2;
})(AnalysisKind || {});
var supportedAnalysisKinds = new Set(Object.values(AnalysisKind));
var CodeScanning = {
kind: "code-scanning" /* CodeScanning */,
name: "code scanning",
target: "PUT /repos/:owner/:repo/code-scanning/analysis" /* CODE_SCANNING */,
sarifExtension: ".sarif",
sarifPredicate: (name) => name.endsWith(CodeScanning.sarifExtension) && !CodeQuality.sarifPredicate(name),
sentinelPrefix: "CODEQL_UPLOAD_SARIF_"
};
var CodeQuality = {
kind: "code-quality" /* CodeQuality */,
name: "code quality",
target: "PUT /repos/:owner/:repo/code-quality/analysis" /* CODE_QUALITY */,
sarifExtension: ".quality.sarif",
sarifPredicate: (name) => name.endsWith(CodeQuality.sarifExtension),
sentinelPrefix: "CODEQL_UPLOAD_QUALITY_SARIF_"
};
var SarifScanOrder = [CodeQuality, CodeScanning];
// src/api-client.ts
var core5 = __toESM(require_core());
var githubUtils = __toESM(require_utils4());
@@ -88921,14 +88947,6 @@ function wrapCliConfigurationError(cliError) {
var fs7 = __toESM(require("fs"));
var path9 = __toESM(require("path"));
// src/analyses.ts
var AnalysisKind = /* @__PURE__ */ ((AnalysisKind2) => {
AnalysisKind2["CodeScanning"] = "code-scanning";
AnalysisKind2["CodeQuality"] = "code-quality";
return AnalysisKind2;
})(AnalysisKind || {});
var supportedAnalysisKinds = new Set(Object.values(AnalysisKind));
// src/caching-utils.ts
var core6 = __toESM(require_core());
@@ -92391,6 +92409,47 @@ function getSarifFilePaths(sarifPath, isSarif) {
}
return sarifFiles;
}
async function getGroupedSarifFilePaths(logger, sarifPath) {
const stats = fs13.statSync(sarifPath, { throwIfNoEntry: false });
if (stats === void 0) {
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 SarifScanOrder) {
const files = sarifFiles.filter(analysisConfig.sarifPredicate);
if (files.length > 0) {
logger.debug(
`The following SARIF files are for ${analysisConfig.name}: ${files.join(", ")}`
);
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 SarifScanOrder) {
if (analysisConfig.kind === "code-scanning" /* CodeScanning */ || analysisConfig.sarifPredicate(sarifPath)) {
logger.debug(
`Using '${sarifPath}' as a SARIF file for ${analysisConfig.name}.`
);
results[analysisConfig.kind] = [sarifPath];
break;
}
}
}
return results;
}
function countResultsInSarif(sarif) {
let numResults = 0;
const parsedSarif = JSON.parse(sarif);
@@ -92750,6 +92809,7 @@ function filterAlertsByDiffRange(logger, sarif) {
InvalidSarifUploadError,
buildPayload,
findSarifFilesInDir,
getGroupedSarifFilePaths,
getSarifFilePaths,
populateRunAutomationDetails,
readSarifFile,
+2 -2
View File
@@ -28149,11 +28149,11 @@ var require_out = __commonJS({
async.read(path16, getSettings(optionsOrSettingsOrCallback), callback);
}
exports2.stat = stat;
function statSync2(path16, optionsOrSettings) {
function statSync3(path16, optionsOrSettings) {
const settings = getSettings(optionsOrSettings);
return sync.read(path16, settings);
}
exports2.statSync = statSync2;
exports2.statSync = statSync3;
function getSettings(settingsOrOptions = {}) {
if (settingsOrOptions instanceof settings_1.default) {
return settingsOrOptions;
+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;