mirror of
https://github.com/github/codeql-action.git
synced 2025-12-06 15:58:06 +08:00
Compare commits
14 Commits
mbg/csharp
...
mbg/fix/up
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4989465202 | ||
|
|
e080dc827a | ||
|
|
7b7f47d02c | ||
|
|
b26519c6c0 | ||
|
|
320fdb3773 | ||
|
|
f6ff68d584 | ||
|
|
0e1ad534f3 | ||
|
|
4c60b18145 | ||
|
|
d7509a4aaf | ||
|
|
ff359630c6 | ||
|
|
c1f21e42fb | ||
|
|
fc8843601d | ||
|
|
975347365f | ||
|
|
022d7d5aa6 |
2
.github/workflows/__upload-quality-sarif.yml
generated
vendored
2
.github/workflows/__upload-quality-sarif.yml
generated
vendored
@@ -89,7 +89,7 @@ jobs:
|
||||
ref: refs/heads/main
|
||||
sha: 5e235361806c361d4d3f8859e3c897658025a9a2
|
||||
- name: Check output from `upload-sarif` step
|
||||
if: fromJSON(steps.upload-sarif.outputs.sarif-ids)[0].analysis != 'code-quality'
|
||||
if: !fromJSON(steps.upload-sarif.outputs.sarif-ids).code-quality
|
||||
run: exit 1
|
||||
env:
|
||||
CODEQL_ACTION_TEST_MODE: true
|
||||
|
||||
38
lib/analyze-action.js
generated
38
lib/analyze-action.js
generated
@@ -90177,6 +90177,12 @@ var CodeQuality = {
|
||||
sarifPredicate: (name) => name.endsWith(CodeQuality.sarifExtension),
|
||||
sentinelPrefix: "CODEQL_UPLOAD_QUALITY_SARIF_"
|
||||
};
|
||||
var Analyses = [CodeScanning, CodeQuality];
|
||||
function isOtherAnalysisSarif(current, filepath) {
|
||||
return Analyses.some(
|
||||
(config) => config.kind !== current && config.sarifPredicate(filepath)
|
||||
);
|
||||
}
|
||||
|
||||
// src/analyze.ts
|
||||
var fs15 = __toESM(require("fs"));
|
||||
@@ -95568,19 +95574,11 @@ function findSarifFilesInDir(sarifPath, isSarif) {
|
||||
walkSarifFiles(sarifPath);
|
||||
return sarifFiles;
|
||||
}
|
||||
function getSarifFilePaths(sarifPath, isSarif) {
|
||||
if (!fs18.existsSync(sarifPath)) {
|
||||
throw new ConfigurationError(`Path does not exist: ${sarifPath}`);
|
||||
}
|
||||
let sarifFiles;
|
||||
if (fs18.lstatSync(sarifPath).isDirectory()) {
|
||||
sarifFiles = findSarifFilesInDir(sarifPath, isSarif);
|
||||
if (sarifFiles.length === 0) {
|
||||
throw new ConfigurationError(
|
||||
`No SARIF files found to upload in "${sarifPath}".`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
function getSarifFilePaths(sarifPath, analysis, pathStats) {
|
||||
let sarifFiles = [];
|
||||
if (pathStats.isDirectory()) {
|
||||
sarifFiles = findSarifFilesInDir(sarifPath, analysis.sarifPredicate);
|
||||
} else if (!isOtherAnalysisSarif(analysis.kind, sarifPath)) {
|
||||
sarifFiles = [sarifPath];
|
||||
}
|
||||
return sarifFiles;
|
||||
@@ -95681,10 +95679,16 @@ function buildPayload(commitOid, ref, analysisKey, analysisName, zippedSarif, wo
|
||||
return payloadObj;
|
||||
}
|
||||
async function uploadFiles(inputSarifPath, checkoutPath, category, features, logger, uploadTarget) {
|
||||
const sarifPaths = getSarifFilePaths(
|
||||
inputSarifPath,
|
||||
uploadTarget.sarifPredicate
|
||||
);
|
||||
const pathStats = fs18.lstatSync(inputSarifPath, { throwIfNoEntry: false });
|
||||
if (pathStats === void 0) {
|
||||
throw new ConfigurationError(`Path does not exist: ${inputSarifPath}`);
|
||||
}
|
||||
const sarifPaths = getSarifFilePaths(inputSarifPath, uploadTarget, pathStats);
|
||||
if (sarifPaths.length === 0) {
|
||||
throw new ConfigurationError(
|
||||
`No SARIF files found to upload in "${inputSarifPath}".`
|
||||
);
|
||||
}
|
||||
return uploadSpecifiedFiles(
|
||||
sarifPaths,
|
||||
checkoutPath,
|
||||
|
||||
38
lib/init-action-post.js
generated
38
lib/init-action-post.js
generated
@@ -128817,6 +128817,12 @@ var CodeQuality = {
|
||||
sarifPredicate: (name) => name.endsWith(CodeQuality.sarifExtension),
|
||||
sentinelPrefix: "CODEQL_UPLOAD_QUALITY_SARIF_"
|
||||
};
|
||||
var Analyses = [CodeScanning, CodeQuality];
|
||||
function isOtherAnalysisSarif(current, filepath) {
|
||||
return Analyses.some(
|
||||
(config) => config.kind !== current && config.sarifPredicate(filepath)
|
||||
);
|
||||
}
|
||||
|
||||
// src/caching-utils.ts
|
||||
var core6 = __toESM(require_core());
|
||||
@@ -133002,19 +133008,11 @@ function findSarifFilesInDir(sarifPath, isSarif) {
|
||||
walkSarifFiles(sarifPath);
|
||||
return sarifFiles;
|
||||
}
|
||||
function getSarifFilePaths(sarifPath, isSarif) {
|
||||
if (!fs17.existsSync(sarifPath)) {
|
||||
throw new ConfigurationError(`Path does not exist: ${sarifPath}`);
|
||||
}
|
||||
let sarifFiles;
|
||||
if (fs17.lstatSync(sarifPath).isDirectory()) {
|
||||
sarifFiles = findSarifFilesInDir(sarifPath, isSarif);
|
||||
if (sarifFiles.length === 0) {
|
||||
throw new ConfigurationError(
|
||||
`No SARIF files found to upload in "${sarifPath}".`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
function getSarifFilePaths(sarifPath, analysis, pathStats) {
|
||||
let sarifFiles = [];
|
||||
if (pathStats.isDirectory()) {
|
||||
sarifFiles = findSarifFilesInDir(sarifPath, analysis.sarifPredicate);
|
||||
} else if (!isOtherAnalysisSarif(analysis.kind, sarifPath)) {
|
||||
sarifFiles = [sarifPath];
|
||||
}
|
||||
return sarifFiles;
|
||||
@@ -133115,10 +133113,16 @@ function buildPayload(commitOid, ref, analysisKey, analysisName, zippedSarif, wo
|
||||
return payloadObj;
|
||||
}
|
||||
async function uploadFiles(inputSarifPath, checkoutPath, category, features, logger, uploadTarget) {
|
||||
const sarifPaths = getSarifFilePaths(
|
||||
inputSarifPath,
|
||||
uploadTarget.sarifPredicate
|
||||
);
|
||||
const pathStats = fs17.lstatSync(inputSarifPath, { throwIfNoEntry: false });
|
||||
if (pathStats === void 0) {
|
||||
throw new ConfigurationError(`Path does not exist: ${inputSarifPath}`);
|
||||
}
|
||||
const sarifPaths = getSarifFilePaths(inputSarifPath, uploadTarget, pathStats);
|
||||
if (sarifPaths.length === 0) {
|
||||
throw new ConfigurationError(
|
||||
`No SARIF files found to upload in "${inputSarifPath}".`
|
||||
);
|
||||
}
|
||||
return uploadSpecifiedFiles(
|
||||
sarifPaths,
|
||||
checkoutPath,
|
||||
|
||||
70
lib/upload-lib.js
generated
70
lib/upload-lib.js
generated
@@ -88530,6 +88530,36 @@ 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 Analyses = [CodeScanning, CodeQuality];
|
||||
function isOtherAnalysisSarif(current, filepath) {
|
||||
return Analyses.some(
|
||||
(config) => config.kind !== current && config.sarifPredicate(filepath)
|
||||
);
|
||||
}
|
||||
|
||||
// src/api-client.ts
|
||||
var core5 = __toESM(require_core());
|
||||
var githubUtils = __toESM(require_utils4());
|
||||
@@ -88921,14 +88951,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());
|
||||
|
||||
@@ -92374,19 +92396,11 @@ function findSarifFilesInDir(sarifPath, isSarif) {
|
||||
walkSarifFiles(sarifPath);
|
||||
return sarifFiles;
|
||||
}
|
||||
function getSarifFilePaths(sarifPath, isSarif) {
|
||||
if (!fs13.existsSync(sarifPath)) {
|
||||
throw new ConfigurationError(`Path does not exist: ${sarifPath}`);
|
||||
}
|
||||
let sarifFiles;
|
||||
if (fs13.lstatSync(sarifPath).isDirectory()) {
|
||||
sarifFiles = findSarifFilesInDir(sarifPath, isSarif);
|
||||
if (sarifFiles.length === 0) {
|
||||
throw new ConfigurationError(
|
||||
`No SARIF files found to upload in "${sarifPath}".`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
function getSarifFilePaths(sarifPath, analysis, pathStats) {
|
||||
let sarifFiles = [];
|
||||
if (pathStats.isDirectory()) {
|
||||
sarifFiles = findSarifFilesInDir(sarifPath, analysis.sarifPredicate);
|
||||
} else if (!isOtherAnalysisSarif(analysis.kind, sarifPath)) {
|
||||
sarifFiles = [sarifPath];
|
||||
}
|
||||
return sarifFiles;
|
||||
@@ -92487,10 +92501,16 @@ function buildPayload(commitOid, ref, analysisKey, analysisName, zippedSarif, wo
|
||||
return payloadObj;
|
||||
}
|
||||
async function uploadFiles(inputSarifPath, checkoutPath, category, features, logger, uploadTarget) {
|
||||
const sarifPaths = getSarifFilePaths(
|
||||
inputSarifPath,
|
||||
uploadTarget.sarifPredicate
|
||||
);
|
||||
const pathStats = fs13.lstatSync(inputSarifPath, { throwIfNoEntry: false });
|
||||
if (pathStats === void 0) {
|
||||
throw new ConfigurationError(`Path does not exist: ${inputSarifPath}`);
|
||||
}
|
||||
const sarifPaths = getSarifFilePaths(inputSarifPath, uploadTarget, pathStats);
|
||||
if (sarifPaths.length === 0) {
|
||||
throw new ConfigurationError(
|
||||
`No SARIF files found to upload in "${inputSarifPath}".`
|
||||
);
|
||||
}
|
||||
return uploadSpecifiedFiles(
|
||||
sarifPaths,
|
||||
checkoutPath,
|
||||
|
||||
403
lib/upload-sarif-action.js
generated
403
lib/upload-sarif-action.js
generated
File diff suppressed because it is too large
Load Diff
@@ -22,5 +22,5 @@ steps:
|
||||
ref: 'refs/heads/main'
|
||||
sha: '5e235361806c361d4d3f8859e3c897658025a9a2'
|
||||
- name: "Check output from `upload-sarif` step"
|
||||
if: fromJSON(steps.upload-sarif.outputs.sarif-ids)[0].analysis != 'code-quality'
|
||||
if: !fromJSON(steps.upload-sarif.outputs.sarif-ids).code-quality
|
||||
run: exit 1
|
||||
|
||||
@@ -2,6 +2,7 @@ import test from "ava";
|
||||
|
||||
import {
|
||||
AnalysisKind,
|
||||
isOtherAnalysisSarif,
|
||||
parseAnalysisKinds,
|
||||
supportedAnalysisKinds,
|
||||
} from "./analyses";
|
||||
@@ -34,3 +35,23 @@ test("Parsing analysis kinds requires at least one analysis kind", async (t) =>
|
||||
instanceOf: ConfigurationError,
|
||||
});
|
||||
});
|
||||
|
||||
test("isOtherAnalysisSarif", async (t) => {
|
||||
function expectTrue(analysisKind: AnalysisKind, filepath: string) {
|
||||
t.assert(
|
||||
isOtherAnalysisSarif(analysisKind, filepath),
|
||||
`Expected ${filepath} to be for another analysis kind, but it matched ${analysisKind}.`,
|
||||
);
|
||||
}
|
||||
function expectFalse(analysisKind: AnalysisKind, filepath: string) {
|
||||
t.assert(
|
||||
!isOtherAnalysisSarif(analysisKind, filepath),
|
||||
`Expected ${filepath} to be for ${analysisKind}, but it matched some other analysis kind.`,
|
||||
);
|
||||
}
|
||||
expectTrue(AnalysisKind.CodeQuality, "test.sarif");
|
||||
expectFalse(AnalysisKind.CodeQuality, "test.quality.sarif");
|
||||
expectTrue(AnalysisKind.CodeScanning, "test.quality.sarif");
|
||||
expectFalse(AnalysisKind.CodeScanning, "test.sarif");
|
||||
expectFalse(AnalysisKind.CodeScanning, "test.json");
|
||||
});
|
||||
|
||||
@@ -86,3 +86,23 @@ export const CodeQuality: AnalysisConfig = {
|
||||
sarifPredicate: (name) => name.endsWith(CodeQuality.sarifExtension),
|
||||
sentinelPrefix: "CODEQL_UPLOAD_QUALITY_SARIF_",
|
||||
};
|
||||
|
||||
// An array of all analysis configurations we know of.
|
||||
export const Analyses = [CodeScanning, CodeQuality];
|
||||
|
||||
/**
|
||||
* Determines based on the given `filepath`, whether the file is a SARIF file for
|
||||
* an analysis other than the one given by `current`.
|
||||
*
|
||||
* @param current The current analysis kind.
|
||||
* @param filepath The path of the file to check.
|
||||
* @returns True if `filepath` is a SARIF file for an analysis other than `current`; False otherwise.
|
||||
*/
|
||||
export function isOtherAnalysisSarif(
|
||||
current: AnalysisKind,
|
||||
filepath: string,
|
||||
): boolean {
|
||||
return Analyses.some(
|
||||
(config) => config.kind !== current && config.sarifPredicate(filepath),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -437,23 +437,13 @@ export function findSarifFilesInDir(
|
||||
|
||||
export function getSarifFilePaths(
|
||||
sarifPath: string,
|
||||
isSarif: (name: string) => boolean,
|
||||
analysis: analyses.AnalysisConfig,
|
||||
pathStats: fs.Stats,
|
||||
) {
|
||||
if (!fs.existsSync(sarifPath)) {
|
||||
// This is always a configuration error, even for first-party runs.
|
||||
throw new ConfigurationError(`Path does not exist: ${sarifPath}`);
|
||||
}
|
||||
|
||||
let sarifFiles: string[];
|
||||
if (fs.lstatSync(sarifPath).isDirectory()) {
|
||||
sarifFiles = findSarifFilesInDir(sarifPath, isSarif);
|
||||
if (sarifFiles.length === 0) {
|
||||
// This is always a configuration error, even for first-party runs.
|
||||
throw new ConfigurationError(
|
||||
`No SARIF files found to upload in "${sarifPath}".`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let sarifFiles: string[] = [];
|
||||
if (pathStats.isDirectory()) {
|
||||
sarifFiles = findSarifFilesInDir(sarifPath, analysis.sarifPredicate);
|
||||
} else if (!analyses.isOtherAnalysisSarif(analysis.kind, sarifPath)) {
|
||||
sarifFiles = [sarifPath];
|
||||
}
|
||||
return sarifFiles;
|
||||
@@ -623,10 +613,21 @@ export async function uploadFiles(
|
||||
logger: Logger,
|
||||
uploadTarget: analyses.AnalysisConfig,
|
||||
): Promise<UploadResult> {
|
||||
const sarifPaths = getSarifFilePaths(
|
||||
inputSarifPath,
|
||||
uploadTarget.sarifPredicate,
|
||||
);
|
||||
const pathStats = fs.lstatSync(inputSarifPath, { throwIfNoEntry: false });
|
||||
|
||||
if (pathStats === undefined) {
|
||||
// This is always a configuration error, even for first-party runs.
|
||||
throw new ConfigurationError(`Path does not exist: ${inputSarifPath}`);
|
||||
}
|
||||
|
||||
const sarifPaths = getSarifFilePaths(inputSarifPath, uploadTarget, pathStats);
|
||||
|
||||
if (sarifPaths.length === 0) {
|
||||
// This is always a configuration error, even for first-party runs.
|
||||
throw new ConfigurationError(
|
||||
`No SARIF files found to upload in "${inputSarifPath}".`,
|
||||
);
|
||||
}
|
||||
|
||||
return uploadSpecifiedFiles(
|
||||
sarifPaths,
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
isThirdPartyAnalysis,
|
||||
} from "./status-report";
|
||||
import * as upload_lib from "./upload-lib";
|
||||
import { uploadResultsToSarifIds, uploadSarif } from "./upload-sarif";
|
||||
import {
|
||||
ConfigurationError,
|
||||
checkActionVersion,
|
||||
@@ -32,55 +33,6 @@ interface UploadSarifStatusReport
|
||||
extends StatusReportBase,
|
||||
upload_lib.UploadStatusReport {}
|
||||
|
||||
/**
|
||||
* Searches for SARIF files for the given `analysis` in the given `sarifPath`.
|
||||
* If any are found, then they are uploaded to the appropriate endpoint for the given `analysis`.
|
||||
*
|
||||
* @param logger The logger to use.
|
||||
* @param features Information about FFs.
|
||||
* @param sarifPath The path to a SARIF file or directory containing SARIF files.
|
||||
* @param pathStats Information about `sarifPath`.
|
||||
* @param checkoutPath The checkout path.
|
||||
* @param analysis The configuration of the analysis we should upload SARIF files for.
|
||||
* @param category The SARIF category to use for the upload.
|
||||
* @returns The result of uploading the SARIF file(s) or `undefined` if there are none.
|
||||
*/
|
||||
async function findAndUpload(
|
||||
logger: Logger,
|
||||
features: Features,
|
||||
sarifPath: string,
|
||||
pathStats: fs.Stats,
|
||||
checkoutPath: string,
|
||||
analysis: analyses.AnalysisConfig,
|
||||
category?: string,
|
||||
): Promise<upload_lib.UploadResult | undefined> {
|
||||
let sarifFiles: string[] | undefined;
|
||||
|
||||
if (pathStats.isDirectory()) {
|
||||
sarifFiles = upload_lib.findSarifFilesInDir(
|
||||
sarifPath,
|
||||
analysis.sarifPredicate,
|
||||
);
|
||||
} else if (pathStats.isFile() && analysis.sarifPredicate(sarifPath)) {
|
||||
sarifFiles = [sarifPath];
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (sarifFiles.length !== 0) {
|
||||
return await upload_lib.uploadSpecifiedFiles(
|
||||
sarifFiles,
|
||||
checkoutPath,
|
||||
category,
|
||||
features,
|
||||
logger,
|
||||
analysis,
|
||||
);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function sendSuccessStatusReport(
|
||||
startedAt: Date,
|
||||
uploadStats: upload_lib.UploadStatusReport,
|
||||
@@ -145,50 +97,32 @@ async function run() {
|
||||
throw new ConfigurationError(`Path does not exist: ${sarifPath}.`);
|
||||
}
|
||||
|
||||
const sarifIds: Array<{ analysis: string; id: string }> = [];
|
||||
const uploadResult = await findAndUpload(
|
||||
const uploadResults = await uploadSarif(
|
||||
logger,
|
||||
features,
|
||||
sarifPath,
|
||||
pathStats,
|
||||
checkoutPath,
|
||||
analyses.CodeScanning,
|
||||
category,
|
||||
);
|
||||
if (uploadResult !== undefined) {
|
||||
core.setOutput("sarif-id", uploadResult.sarifID);
|
||||
sarifIds.push({
|
||||
analysis: analyses.AnalysisKind.CodeScanning,
|
||||
id: uploadResult.sarifID,
|
||||
});
|
||||
|
||||
if (Object.keys(uploadResults).length === 0) {
|
||||
logger.warning(`No SARIF files were uploaded.`);
|
||||
}
|
||||
|
||||
// If there are `.quality.sarif` files in `sarifPath`, then upload those to the code quality service.
|
||||
const qualityUploadResult = await findAndUpload(
|
||||
logger,
|
||||
features,
|
||||
sarifPath,
|
||||
pathStats,
|
||||
checkoutPath,
|
||||
analyses.CodeQuality,
|
||||
actionsUtil.fixCodeQualityCategory(logger, category),
|
||||
);
|
||||
if (qualityUploadResult !== undefined) {
|
||||
sarifIds.push({
|
||||
analysis: analyses.AnalysisKind.CodeQuality,
|
||||
id: qualityUploadResult.sarifID,
|
||||
});
|
||||
}
|
||||
const sarifIds = uploadResultsToSarifIds(uploadResults);
|
||||
core.setOutput("sarif-ids", JSON.stringify(sarifIds));
|
||||
|
||||
// We don't upload results in test mode, so don't wait for processing
|
||||
if (isInTestMode()) {
|
||||
core.debug("In test mode. Waiting for processing is disabled.");
|
||||
} else if (actionsUtil.getRequiredInput("wait-for-processing") === "true") {
|
||||
if (uploadResult !== undefined) {
|
||||
const codeScanningUploadResult =
|
||||
uploadResults[analyses.AnalysisKind.CodeScanning];
|
||||
if (codeScanningUploadResult !== undefined) {
|
||||
await upload_lib.waitForProcessing(
|
||||
getRepositoryNwo(),
|
||||
uploadResult.sarifID,
|
||||
codeScanningUploadResult.sarifID,
|
||||
logger,
|
||||
);
|
||||
}
|
||||
@@ -197,7 +131,7 @@ async function run() {
|
||||
}
|
||||
await sendSuccessStatusReport(
|
||||
startedAt,
|
||||
uploadResult?.statusReport || {},
|
||||
uploadResults[analyses.AnalysisKind.CodeScanning]?.statusReport || {},
|
||||
logger,
|
||||
);
|
||||
} catch (unwrappedError) {
|
||||
|
||||
263
src/upload-sarif.test.ts
Normal file
263
src/upload-sarif.test.ts
Normal file
@@ -0,0 +1,263 @@
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
import test, { ExecutionContext } from "ava";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
import {
|
||||
AnalysisConfig,
|
||||
AnalysisKind,
|
||||
CodeQuality,
|
||||
CodeScanning,
|
||||
} from "./analyses";
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import { createFeatures, setupTests } from "./testing-utils";
|
||||
import { UploadResult } from "./upload-lib";
|
||||
import * as uploadLib from "./upload-lib";
|
||||
import { findAndUpload, uploadSarif } from "./upload-sarif";
|
||||
import * as util from "./util";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
const findAndUploadMacro = test.macro({
|
||||
exec: async (
|
||||
t: ExecutionContext<unknown>,
|
||||
sarifFiles: string[],
|
||||
analysis: AnalysisConfig,
|
||||
sarifPath: (tempDir: string) => string = (tempDir) => tempDir,
|
||||
expectedResult: UploadResult | undefined,
|
||||
) => {
|
||||
await util.withTmpDir(async (tempDir) => {
|
||||
sinon.stub(uploadLib, "uploadSpecifiedFiles").resolves(expectedResult);
|
||||
const logger = getRunnerLogger(true);
|
||||
const features = createFeatures([]);
|
||||
|
||||
for (const sarifFile of sarifFiles) {
|
||||
fs.writeFileSync(path.join(tempDir, sarifFile), "");
|
||||
}
|
||||
|
||||
const stats = fs.statSync(sarifPath(tempDir));
|
||||
const actual = await findAndUpload(
|
||||
logger,
|
||||
features,
|
||||
sarifPath(tempDir),
|
||||
stats,
|
||||
"",
|
||||
analysis,
|
||||
);
|
||||
|
||||
t.deepEqual(actual, expectedResult);
|
||||
});
|
||||
},
|
||||
title: (providedTitle = "") => `findAndUpload - ${providedTitle}`,
|
||||
});
|
||||
|
||||
test(
|
||||
"no matching files",
|
||||
findAndUploadMacro,
|
||||
["test.json"],
|
||||
CodeScanning,
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
|
||||
test(
|
||||
"matching files for Code Scanning with directory path",
|
||||
findAndUploadMacro,
|
||||
["test.sarif"],
|
||||
CodeScanning,
|
||||
undefined,
|
||||
{
|
||||
statusReport: {},
|
||||
sarifID: "some-id",
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
"matching files for Code Scanning with file path",
|
||||
findAndUploadMacro,
|
||||
["test.sarif"],
|
||||
CodeScanning,
|
||||
(tempDir) => path.join(tempDir, "test.sarif"),
|
||||
{
|
||||
statusReport: {},
|
||||
sarifID: "some-id",
|
||||
},
|
||||
);
|
||||
|
||||
interface UploadSarifExpectedResult {
|
||||
uploadResult?: UploadResult;
|
||||
expectedFiles?: string[];
|
||||
}
|
||||
|
||||
const uploadSarifMacro = test.macro({
|
||||
exec: async (
|
||||
t: ExecutionContext<unknown>,
|
||||
sarifFiles: string[],
|
||||
sarifPath: (tempDir: string) => string = (tempDir) => tempDir,
|
||||
expectedResult: Partial<Record<AnalysisKind, UploadSarifExpectedResult>>,
|
||||
) => {
|
||||
await util.withTmpDir(async (tempDir) => {
|
||||
const logger = getRunnerLogger(true);
|
||||
const testPath = sarifPath(tempDir);
|
||||
const features = createFeatures([]);
|
||||
|
||||
const toFullPath = (filename: string) => path.join(tempDir, filename);
|
||||
|
||||
const uploadSpecifiedFiles = sinon.stub(
|
||||
uploadLib,
|
||||
"uploadSpecifiedFiles",
|
||||
);
|
||||
|
||||
for (const analysisKind of Object.values(AnalysisKind)) {
|
||||
uploadSpecifiedFiles
|
||||
.withArgs(
|
||||
sinon.match.any,
|
||||
sinon.match.any,
|
||||
sinon.match.any,
|
||||
features,
|
||||
logger,
|
||||
analysisKind === AnalysisKind.CodeScanning
|
||||
? CodeScanning
|
||||
: CodeQuality,
|
||||
)
|
||||
.resolves(expectedResult[analysisKind as AnalysisKind]?.uploadResult);
|
||||
}
|
||||
|
||||
const fullSarifPaths = sarifFiles.map(toFullPath);
|
||||
for (const sarifFile of fullSarifPaths) {
|
||||
fs.writeFileSync(sarifFile, "");
|
||||
}
|
||||
|
||||
const stats = fs.statSync(testPath);
|
||||
const actual = await uploadSarif(logger, features, testPath, stats, "");
|
||||
|
||||
for (const analysisKind of Object.values(AnalysisKind)) {
|
||||
const analyisKindResult = expectedResult[analysisKind];
|
||||
if (analyisKindResult) {
|
||||
// We are expecting a result for this analysis kind, check that we have it.
|
||||
t.deepEqual(actual[analysisKind], analyisKindResult.uploadResult);
|
||||
// Additionally, check that the mocked `uploadSpecifiedFiles` was called with only the file paths
|
||||
// that we expected it to be called with.
|
||||
t.assert(
|
||||
uploadSpecifiedFiles.calledWith(
|
||||
analyisKindResult.expectedFiles?.map(toFullPath) ??
|
||||
fullSarifPaths,
|
||||
sinon.match.any,
|
||||
sinon.match.any,
|
||||
features,
|
||||
logger,
|
||||
analysisKind === AnalysisKind.CodeScanning
|
||||
? CodeScanning
|
||||
: CodeQuality,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// Otherwise, we are not expecting a result for this analysis kind. However, note that `undefined`
|
||||
// is also returned by our mocked `uploadSpecifiedFiles` when there is no expected result for this
|
||||
// analysis kind.
|
||||
t.is(actual[analysisKind], undefined);
|
||||
// Therefore, we also check that the mocked `uploadSpecifiedFiles` was not called for this analysis kind.
|
||||
t.assert(
|
||||
!uploadSpecifiedFiles.calledWith(
|
||||
sinon.match.any,
|
||||
sinon.match.any,
|
||||
sinon.match.any,
|
||||
features,
|
||||
logger,
|
||||
analysisKind === AnalysisKind.CodeScanning
|
||||
? CodeScanning
|
||||
: CodeQuality,
|
||||
),
|
||||
`uploadSpecifiedFiles was called for ${analysisKind}, but should not have been.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
title: (providedTitle = "") => `uploadSarif - ${providedTitle}`,
|
||||
});
|
||||
|
||||
test(
|
||||
"SARIF file",
|
||||
uploadSarifMacro,
|
||||
["test.sarif"],
|
||||
(tempDir) => path.join(tempDir, "test.sarif"),
|
||||
{
|
||||
"code-scanning": {
|
||||
uploadResult: {
|
||||
statusReport: {},
|
||||
sarifID: "code-scanning-sarif",
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
"JSON file",
|
||||
uploadSarifMacro,
|
||||
["test.json"],
|
||||
(tempDir) => path.join(tempDir, "test.json"),
|
||||
{
|
||||
"code-scanning": {
|
||||
uploadResult: {
|
||||
statusReport: {},
|
||||
sarifID: "code-scanning-sarif",
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
"Code Scanning files",
|
||||
uploadSarifMacro,
|
||||
["test.json", "test.sarif"],
|
||||
undefined,
|
||||
{
|
||||
"code-scanning": {
|
||||
uploadResult: {
|
||||
statusReport: {},
|
||||
sarifID: "code-scanning-sarif",
|
||||
},
|
||||
expectedFiles: ["test.sarif"],
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
"Code Quality file",
|
||||
uploadSarifMacro,
|
||||
["test.quality.sarif"],
|
||||
(tempDir) => path.join(tempDir, "test.quality.sarif"),
|
||||
{
|
||||
"code-quality": {
|
||||
uploadResult: {
|
||||
statusReport: {},
|
||||
sarifID: "code-quality-sarif",
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
"Mixed files",
|
||||
uploadSarifMacro,
|
||||
["test.sarif", "test.quality.sarif"],
|
||||
undefined,
|
||||
{
|
||||
"code-scanning": {
|
||||
uploadResult: {
|
||||
statusReport: {},
|
||||
sarifID: "code-scanning-sarif",
|
||||
},
|
||||
expectedFiles: ["test.sarif"],
|
||||
},
|
||||
"code-quality": {
|
||||
uploadResult: {
|
||||
statusReport: {},
|
||||
sarifID: "code-quality-sarif",
|
||||
},
|
||||
expectedFiles: ["test.quality.sarif"],
|
||||
},
|
||||
},
|
||||
);
|
||||
115
src/upload-sarif.ts
Normal file
115
src/upload-sarif.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import * as fs from "fs";
|
||||
|
||||
import * as core from "@actions/core";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import * as analyses from "./analyses";
|
||||
import { FeatureEnablement } from "./feature-flags";
|
||||
import { Logger } from "./logging";
|
||||
import * as upload_lib from "./upload-lib";
|
||||
|
||||
/**
|
||||
* Searches for SARIF files for the given `analysis` in the given `sarifPath`.
|
||||
* If any are found, then they are uploaded to the appropriate endpoint for the given `analysis`.
|
||||
*
|
||||
* @param logger The logger to use.
|
||||
* @param features Information about FFs.
|
||||
* @param sarifPath The path to a SARIF file or directory containing SARIF files.
|
||||
* @param pathStats Information about `sarifPath`.
|
||||
* @param checkoutPath The checkout path.
|
||||
* @param analysis The configuration of the analysis we should upload SARIF files for.
|
||||
* @param category The SARIF category to use for the upload.
|
||||
* @returns The result of uploading the SARIF file(s) or `undefined` if there are none.
|
||||
*/
|
||||
export async function findAndUpload(
|
||||
logger: Logger,
|
||||
features: FeatureEnablement,
|
||||
sarifPath: string,
|
||||
pathStats: fs.Stats,
|
||||
checkoutPath: string,
|
||||
analysis: analyses.AnalysisConfig,
|
||||
category?: string,
|
||||
): Promise<upload_lib.UploadResult | undefined> {
|
||||
const sarifFiles: string[] | undefined = upload_lib.getSarifFilePaths(
|
||||
sarifPath,
|
||||
analysis,
|
||||
pathStats,
|
||||
);
|
||||
|
||||
if (sarifFiles.length !== 0) {
|
||||
return await upload_lib.uploadSpecifiedFiles(
|
||||
sarifFiles,
|
||||
checkoutPath,
|
||||
category,
|
||||
features,
|
||||
logger,
|
||||
analysis,
|
||||
);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export interface UploadSarifResult {
|
||||
analysis: analyses.AnalysisKind;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export type UploadSarifResults = Partial<
|
||||
Record<analyses.AnalysisKind, upload_lib.UploadResult>
|
||||
>;
|
||||
|
||||
export async function uploadSarif(
|
||||
logger: Logger,
|
||||
features: FeatureEnablement,
|
||||
sarifPath: string,
|
||||
pathStats: fs.Stats,
|
||||
checkoutPath: string,
|
||||
category?: string,
|
||||
): Promise<UploadSarifResults> {
|
||||
const uploadResults: UploadSarifResults = {};
|
||||
const uploadResult = await findAndUpload(
|
||||
logger,
|
||||
features,
|
||||
sarifPath,
|
||||
pathStats,
|
||||
checkoutPath,
|
||||
analyses.CodeScanning,
|
||||
category,
|
||||
);
|
||||
if (uploadResult !== undefined) {
|
||||
core.setOutput("sarif-id", uploadResult.sarifID);
|
||||
uploadResults[analyses.AnalysisKind.CodeScanning] = uploadResult;
|
||||
}
|
||||
|
||||
// If there are `.quality.sarif` files in `sarifPath`, then upload those to the code quality service.
|
||||
if (
|
||||
pathStats.isDirectory() ||
|
||||
(pathStats.isFile() && analyses.CodeQuality.sarifPredicate(sarifPath))
|
||||
) {
|
||||
const qualityUploadResult = await findAndUpload(
|
||||
logger,
|
||||
features,
|
||||
sarifPath,
|
||||
pathStats,
|
||||
checkoutPath,
|
||||
analyses.CodeQuality,
|
||||
actionsUtil.fixCodeQualityCategory(logger, category),
|
||||
);
|
||||
if (qualityUploadResult !== undefined) {
|
||||
uploadResults[analyses.AnalysisKind.CodeQuality] = qualityUploadResult;
|
||||
}
|
||||
}
|
||||
|
||||
return uploadResults;
|
||||
}
|
||||
|
||||
export function uploadResultsToSarifIds(
|
||||
uploadResults: UploadSarifResults,
|
||||
): Partial<Record<analyses.AnalysisKind, string>> {
|
||||
const result = {};
|
||||
for (const uploadResult of Object.keys(uploadResults)) {
|
||||
result[uploadResult] = uploadResults[uploadResult].id;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user