Upload per-database failed SARIFs, if applicable

This commit is contained in:
Angela P Wen
2023-03-02 17:56:25 -08:00
parent 6b27d473a0
commit 02778762a2
4 changed files with 111 additions and 52 deletions
+11 -9
View File
@@ -132,7 +132,7 @@ test("doesn't upload failed SARIF for workflow with upload: false", async (t) =>
const result = await testFailedSarifUpload(t, actionsWorkflow, {
expectUpload: false,
});
t.is(result.upload_failed_run_skipped_because, "SARIF upload is disabled");
t.is(result[0].upload_failed_run_skipped_because, "SARIF upload is disabled");
});
test("uploading failed SARIF run succeeds when workflow uses an input with a matrix var", async (t) => {
@@ -187,7 +187,7 @@ test("uploading failed SARIF run fails when workflow uses a complex upload input
expectUpload: false,
});
t.is(
result.upload_failed_run_error,
result[0].upload_failed_run_error,
"Could not get upload input to github/codeql-action/analyze since it contained an " +
"unrecognized dynamic value."
);
@@ -204,11 +204,11 @@ test("uploading failed SARIF run fails when workflow does not reference github/c
expectUpload: false,
});
t.is(
result.upload_failed_run_error,
result[0].upload_failed_run_error,
"Could not get upload input to github/codeql-action/analyze since the analyze job does not " +
"call github/codeql-action/analyze."
);
t.truthy(result.upload_failed_run_stack_trace);
t.truthy(result[0].upload_failed_run_stack_trace);
});
function createTestWorkflow(
@@ -246,7 +246,7 @@ async function testFailedSarifUpload(
expectUpload?: boolean;
matrix?: { [key: string]: string };
} = {}
): Promise<initActionPostHelper.UploadFailedSarifResult> {
): Promise<initActionPostHelper.UploadFailedSarifResult[]> {
const config = {
codeQLCmd: "codeql",
debugMode: true,
@@ -282,10 +282,12 @@ async function testFailedSarifUpload(
getRunnerLogger(true)
);
if (expectUpload) {
t.deepEqual(result, {
raw_upload_size_bytes: 20,
zipped_upload_size_bytes: 10,
});
t.deepEqual(result, [
{
raw_upload_size_bytes: 20,
zipped_upload_size_bytes: 10,
},
]);
}
if (expectUpload) {
t.true(
+81 -35
View File
@@ -6,9 +6,17 @@ import { Config, getConfig } from "./config-utils";
import { Feature, FeatureEnablement } from "./feature-flags";
import { Logger } from "./logging";
import { RepositoryNwo } from "./repository";
import { CODEQL_ACTION_ANALYZE_DID_COMPLETE_SUCCESSFULLY } from "./shared-environment";
import {
CODEQL_ACTION_ANALYZE_DID_COMPLETE_SUCCESSFULLY,
CODEQL_ACTION_IS_DATABASE_CLUSTER,
} from "./shared-environment";
import * as uploadLib from "./upload-lib";
import { getRequiredEnvParam, isInTestMode, parseMatrixInput } from "./util";
import {
getExistingDatabasePaths,
getRequiredEnvParam,
isInTestMode,
parseMatrixInput,
} from "./util";
import {
getCategoryInputOrThrow,
getCheckoutPathInputOrThrow,
@@ -45,9 +53,9 @@ async function maybeUploadFailedSarif(
repositoryNwo: RepositoryNwo,
featureEnablement: FeatureEnablement,
logger: Logger
): Promise<UploadFailedSarifResult> {
): Promise<UploadFailedSarifResult[]> {
if (!config.codeQLCmd) {
return { upload_failed_run_skipped_because: "CodeQL command not found" };
return [{ upload_failed_run_skipped_because: "CodeQL command not found" }];
}
const codeql = await getCodeQL(config.codeQLCmd);
if (
@@ -56,7 +64,7 @@ async function maybeUploadFailedSarif(
codeql
))
) {
return { upload_failed_run_skipped_because: "Feature disabled" };
return [{ upload_failed_run_skipped_because: "Feature disabled" }];
}
const workflow = await getWorkflow();
const jobName = getRequiredEnvParam("GITHUB_JOB");
@@ -65,28 +73,62 @@ async function maybeUploadFailedSarif(
getUploadInputOrThrow(workflow, jobName, matrix) !== "true" ||
isInTestMode()
) {
return { upload_failed_run_skipped_because: "SARIF upload is disabled" };
return [{ upload_failed_run_skipped_because: "SARIF upload is disabled" }];
}
const category = getCategoryInputOrThrow(workflow, jobName, matrix);
const checkoutPath = getCheckoutPathInputOrThrow(workflow, jobName, matrix);
const sarifFile = "../codeql-failed-run.sarif";
await codeql.diagnosticsExport(sarifFile, category);
core.info(`Uploading failed SARIF file ${sarifFile}`);
const uploadResult = await uploadLib.uploadFromActions(
sarifFile,
checkoutPath,
category,
logger
);
await uploadLib.waitForProcessing(
repositoryNwo,
uploadResult.sarifID,
logger,
{ isUnsuccessfulExecution: true }
);
return uploadResult?.statusReport ?? {};
const existingDatabasePaths = getExistingDatabasePaths(config);
if (existingDatabasePaths.length === 0) {
const sarifFile = "../codeql-failed-run.sarif";
await codeql.diagnosticsExport(sarifFile, category);
core.info(`Uploading failed SARIF file ${sarifFile}`);
const uploadResult = await uploadLib.uploadFromActions(
sarifFile,
checkoutPath,
category,
logger
);
await uploadLib.waitForProcessing(
repositoryNwo,
uploadResult.sarifID,
logger,
{ isUnsuccessfulExecution: true }
);
return [uploadResult?.statusReport ?? {}];
} else {
const uploadStatusReports: uploadLib.UploadStatusReport[] = [];
const maybeIsDatabaseCluster =
process.env[CODEQL_ACTION_IS_DATABASE_CLUSTER];
// If we have already created database(s), then we call database diagnostic export.
existingDatabasePaths.map(async (databasePath) => {
const databaseSarifFile = `../codeql-failed-run-${existingDatabasePaths}.sarif`;
await codeql.databaseExportDiagnostics(
databasePath,
maybeIsDatabaseCluster === undefined ||
maybeIsDatabaseCluster.length === 0
? false
: true,
databaseSarifFile,
category
);
core.info(`Uploading failed SARIF file ${databaseSarifFile}`);
const uploadResult = await uploadLib.uploadFromActions(
databaseSarifFile,
checkoutPath,
category,
logger
);
await uploadLib.waitForProcessing(
repositoryNwo,
uploadResult.sarifID,
logger,
{ isUnsuccessfulExecution: true }
);
uploadStatusReports.push(uploadResult.statusReport);
});
return uploadStatusReports ?? [];
}
}
export async function tryUploadSarifIfRunFailed(
@@ -94,7 +136,7 @@ export async function tryUploadSarifIfRunFailed(
repositoryNwo: RepositoryNwo,
featureEnablement: FeatureEnablement,
logger: Logger
): Promise<UploadFailedSarifResult> {
): Promise<UploadFailedSarifResult[]> {
if (process.env[CODEQL_ACTION_ANALYZE_DID_COMPLETE_SUCCESSFULLY] !== "true") {
try {
return await maybeUploadFailedSarif(
@@ -107,13 +149,15 @@ export async function tryUploadSarifIfRunFailed(
logger.debug(
`Failed to upload a SARIF file for this failed CodeQL code scanning run. ${e}`
);
return createFailedUploadFailedSarifResult(e);
return [createFailedUploadFailedSarifResult(e)];
}
} else {
return {
upload_failed_run_skipped_because:
"Analyze Action completed successfully",
};
return [
{
upload_failed_run_skipped_because:
"Analyze Action completed successfully",
},
];
}
}
@@ -133,27 +177,29 @@ export async function run(
return;
}
const uploadFailedSarifResult = await tryUploadSarifIfRunFailed(
const uploadFailedSarifResults = await tryUploadSarifIfRunFailed(
config,
repositoryNwo,
featureEnablement,
logger
);
if (uploadFailedSarifResult.upload_failed_run_skipped_because) {
// If the upload was skipped, it is the only item in the results array.
if (uploadFailedSarifResults[0].upload_failed_run_skipped_because) {
logger.debug(
"Won't upload a failed SARIF file for this CodeQL code scanning run because: " +
`${uploadFailedSarifResult.upload_failed_run_skipped_because}.`
`${uploadFailedSarifResults[0].upload_failed_run_skipped_because}.`
);
}
// Throw an error if in integration tests, we expected to upload a SARIF file for a failed run
// but we didn't upload anything.
if (
process.env["CODEQL_ACTION_EXPECT_UPLOAD_FAILED_SARIF"] === "true" &&
!uploadFailedSarifResult.raw_upload_size_bytes
!uploadFailedSarifResults[0].raw_upload_size_bytes
) {
throw new Error(
"Expected to upload a failed SARIF file for this CodeQL code scanning run, " +
`but the result was instead ${uploadFailedSarifResult}.`
`but the result was instead ${uploadFailedSarifResults}.`
);
}
@@ -168,5 +214,5 @@ export async function run(
await printDebugLogs(config);
}
return uploadFailedSarifResult;
return uploadFailedSarifResults;
}
+10 -8
View File
@@ -28,8 +28,8 @@ interface InitPostStatusReport
async function runWrapper() {
const startedAt = new Date();
let uploadFailedSarifResult:
| initActionPostHelper.UploadFailedSarifResult
let uploadFailedSarifResults:
| initActionPostHelper.UploadFailedSarifResult[]
| undefined;
try {
const logger = getActionsLogger();
@@ -46,7 +46,7 @@ async function runWrapper() {
logger
);
uploadFailedSarifResult = await initActionPostHelper.run(
uploadFailedSarifResults = await initActionPostHelper.run(
debugArtifacts.uploadDatabaseBundleDebugArtifact,
debugArtifacts.uploadLogsDebugArtifact,
printDebugLogs,
@@ -74,11 +74,13 @@ async function runWrapper() {
"success",
startedAt
);
const statusReport: InitPostStatusReport = {
...statusReportBase,
...uploadFailedSarifResult,
};
await sendStatusReport(statusReport);
for (const result of uploadFailedSarifResults ?? []) {
const statusReport: InitPostStatusReport = {
...statusReportBase,
...result,
};
await sendStatusReport(statusReport);
}
}
void runWrapper();
+9
View File
@@ -252,6 +252,15 @@ export function getCodeQLDatabasePath(config: Config, language: Language) {
return path.resolve(config.dbLocation, language);
}
/**
* Gets the paths of all CodeQL databases that currently exist.
*/
export function getExistingDatabasePaths(config: Config): string[] {
return config.languages
.map((language) => getCodeQLDatabasePath(config, language))
.filter((databasePath) => fs.existsSync(databasePath));
}
/**
* Parses user input of a github.com or GHES URL to a canonical form.
* Removes any API prefix or suffix if one is present.