Add database export-diagnostics command

This commit is contained in:
Angela P Wen
2023-03-03 11:41:41 -08:00
parent b780f5b820
commit 1d4190aa39
18 changed files with 114 additions and 204 deletions
+1 -5
View File
@@ -1,7 +1,6 @@
import * as fs from "fs";
import * as path from "path";
import * as core from "@actions/core";
import * as toolrunner from "@actions/exec/lib/toolrunner";
import * as yaml from "js-yaml";
@@ -14,7 +13,6 @@ import { ToolsSource } from "./init";
import { isTracedLanguage, Language } from "./languages";
import { Logger } from "./logging";
import * as setupCodeql from "./setup-codeql";
import { CODEQL_ACTION_IS_DATABASE_CLUSTER } from "./shared-environment";
import { toolrunnerErrorCatcher } from "./toolrunner-error-catcher";
import {
getTrapCachingExtractorConfigArgs,
@@ -651,7 +649,6 @@ export async function getCodeQLForCmd(
],
{ stdin: externalRepositoryToken }
);
core.exportVariable(CODEQL_ACTION_IS_DATABASE_CLUSTER, "true");
},
async runAutobuild(language: Language) {
const cmdName =
@@ -994,8 +991,7 @@ export async function getCodeQLForCmd(
): Promise<void> {
const args = [
"database",
"export",
"diagnostics",
"export-diagnostics",
"--format=sarif-latest",
`--output=${sarifFile}`,
...getExtraOptionsFromEnv(["diagnostics", "export"]),
+9 -11
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[0].upload_failed_run_skipped_because, "SARIF upload is disabled");
t.is(result.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[0].upload_failed_run_error,
result.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[0].upload_failed_run_error,
result.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[0].upload_failed_run_stack_trace);
t.truthy(result.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,12 +282,10 @@ 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(
+47 -78
View File
@@ -1,3 +1,5 @@
import * as fs from "fs";
import * as core from "@actions/core";
import * as actionsUtil from "./actions-util";
@@ -6,17 +8,9 @@ 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,
CODEQL_ACTION_IS_DATABASE_CLUSTER,
} from "./shared-environment";
import { CODEQL_ACTION_ANALYZE_DID_COMPLETE_SUCCESSFULLY } from "./shared-environment";
import * as uploadLib from "./upload-lib";
import {
getExistingDatabasePaths,
getRequiredEnvParam,
isInTestMode,
parseMatrixInput,
} from "./util";
import { getRequiredEnvParam, isInTestMode, parseMatrixInput } from "./util";
import {
getCategoryInputOrThrow,
getCheckoutPathInputOrThrow,
@@ -53,9 +47,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 (
@@ -64,7 +58,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");
@@ -73,62 +67,40 @@ 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 databasePath = config.dbLocation;
const existingDatabasePaths = getExistingDatabasePaths(config);
if (existingDatabasePaths.length === 0) {
const sarifFile = "../codeql-failed-run.sarif";
// We call 'database export-diagnostics' to find any per-database diagnostics.
const sarifFile = "../codeql-failed-run.sarif";
await codeql.databaseExportDiagnostics(
databasePath,
true, // Database is always a cluster for CodeQL versions that support diagnostics.
sarifFile,
category
);
// If there was no SARIF file produced, then we fall back on 'export diagnostics'.
if (!fs.existsSync(sarifFile)) {
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 ?? [];
}
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 ?? {};
}
export async function tryUploadSarifIfRunFailed(
@@ -136,7 +108,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(
@@ -149,15 +121,13 @@ 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",
};
}
}
@@ -177,29 +147,28 @@ export async function run(
return;
}
const uploadFailedSarifResults = await tryUploadSarifIfRunFailed(
const uploadFailedSarifResult = await tryUploadSarifIfRunFailed(
config,
repositoryNwo,
featureEnablement,
logger
);
// If the upload was skipped, it is the only item in the results array.
if (uploadFailedSarifResults[0].upload_failed_run_skipped_because) {
if (uploadFailedSarifResult.upload_failed_run_skipped_because) {
logger.debug(
"Won't upload a failed SARIF file for this CodeQL code scanning run because: " +
`${uploadFailedSarifResults[0].upload_failed_run_skipped_because}.`
`${uploadFailedSarifResult.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" &&
!uploadFailedSarifResults[0].raw_upload_size_bytes
!uploadFailedSarifResult.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 ${uploadFailedSarifResults}.`
`but the result was instead ${uploadFailedSarifResult}.`
);
}
@@ -214,5 +183,5 @@ export async function run(
await printDebugLogs(config);
}
return uploadFailedSarifResults;
return uploadFailedSarifResult;
}
+8 -10
View File
@@ -28,8 +28,8 @@ interface InitPostStatusReport
async function runWrapper() {
const startedAt = new Date();
let uploadFailedSarifResults:
| initActionPostHelper.UploadFailedSarifResult[]
let uploadFailedSarifResult:
| initActionPostHelper.UploadFailedSarifResult
| undefined;
try {
const logger = getActionsLogger();
@@ -46,7 +46,7 @@ async function runWrapper() {
logger
);
uploadFailedSarifResults = await initActionPostHelper.run(
uploadFailedSarifResult = await initActionPostHelper.run(
debugArtifacts.uploadDatabaseBundleDebugArtifact,
debugArtifacts.uploadLogsDebugArtifact,
printDebugLogs,
@@ -74,13 +74,11 @@ async function runWrapper() {
"success",
startedAt
);
for (const result of uploadFailedSarifResults ?? []) {
const statusReport: InitPostStatusReport = {
...statusReportBase,
...result,
};
await sendStatusReport(statusReport);
}
const statusReport: InitPostStatusReport = {
...statusReportBase,
...uploadFailedSarifResult,
};
await sendStatusReport(statusReport);
}
void runWrapper();
-6
View File
@@ -1,9 +1,3 @@
/**
* Environment variable that is set to true when `codeql database init --db-cluster" is run.
*/
export const CODEQL_ACTION_IS_DATABASE_CLUSTER =
"CODEQL_ACTION_IS_DATABASE_CLUSTER";
/**
* Environment variable that is set to true when the CodeQL Action has invoked
* the Go autobuilder.
-9
View File
@@ -252,15 +252,6 @@ 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.