Upload per-database diagnostic SARIFs on green and red runs (#1556)
Co-authored-by: Henry Mercer <henry.mercer@me.com>
This commit is contained in:
parent
b4fba292aa
commit
3cbd063679
28 changed files with 450 additions and 54 deletions
|
|
@ -23,7 +23,10 @@ import { Features } from "./feature-flags";
|
|||
import { Language } from "./languages";
|
||||
import { getActionsLogger, Logger } from "./logging";
|
||||
import { parseRepositoryNwo } from "./repository";
|
||||
import { CODEQL_ACTION_ANALYZE_DID_COMPLETE_SUCCESSFULLY } from "./shared-environment";
|
||||
import {
|
||||
CODEQL_ACTION_ANALYZE_DID_COMPLETE_SUCCESSFULLY,
|
||||
CODEQL_ACTION_DID_AUTOBUILD_GOLANG,
|
||||
} from "./shared-environment";
|
||||
import { getTotalCacheSize, uploadTrapCaches } from "./trap-caching";
|
||||
import * as upload_lib from "./upload-lib";
|
||||
import { UploadResult } from "./upload-lib";
|
||||
|
|
@ -130,7 +133,7 @@ function doesGoExtractionOutputExist(config: Config): boolean {
|
|||
* an autobuild step or manual build steps.
|
||||
*
|
||||
* - We detect whether an autobuild step is present by checking the
|
||||
* `util.DID_AUTOBUILD_GO_ENV_VAR_NAME` environment variable, which is set
|
||||
* `CODEQL_ACTION_DID_AUTOBUILD_GOLANG` environment variable, which is set
|
||||
* when the autobuilder is invoked.
|
||||
* - We detect whether the Go database has already been finalized in case it
|
||||
* has been manually set in a prior Action step.
|
||||
|
|
@ -141,7 +144,7 @@ async function runAutobuildIfLegacyGoWorkflow(config: Config, logger: Logger) {
|
|||
if (!config.languages.includes(Language.go)) {
|
||||
return;
|
||||
}
|
||||
if (process.env[util.DID_AUTOBUILD_GO_ENV_VAR_NAME] === "true") {
|
||||
if (process.env[CODEQL_ACTION_DID_AUTOBUILD_GOLANG] === "true") {
|
||||
logger.debug("Won't run Go autobuild since it has already been run.");
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,11 +14,8 @@ import { determineAutobuildLanguages, runAutobuild } from "./autobuild";
|
|||
import * as configUtils from "./config-utils";
|
||||
import { Language } from "./languages";
|
||||
import { getActionsLogger } from "./logging";
|
||||
import {
|
||||
DID_AUTOBUILD_GO_ENV_VAR_NAME,
|
||||
checkGitHubVersionInRange,
|
||||
initializeEnvironment,
|
||||
} from "./util";
|
||||
import { CODEQL_ACTION_DID_AUTOBUILD_GOLANG } from "./shared-environment";
|
||||
import { checkGitHubVersionInRange, initializeEnvironment } from "./util";
|
||||
|
||||
interface AutobuildStatusReport extends StatusReportBase {
|
||||
/** Comma-separated set of languages being auto-built. */
|
||||
|
|
@ -88,7 +85,7 @@ async function run() {
|
|||
currentLanguage = language;
|
||||
await runAutobuild(language, config, logger);
|
||||
if (language === Language.go) {
|
||||
core.exportVariable(DID_AUTOBUILD_GO_ENV_VAR_NAME, "true");
|
||||
core.exportVariable(CODEQL_ACTION_DID_AUTOBUILD_GOLANG, "true");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -185,6 +185,17 @@ export interface CodeQL {
|
|||
* Run 'codeql database print-baseline'.
|
||||
*/
|
||||
databasePrintBaseline(databasePath: string): Promise<string>;
|
||||
/**
|
||||
* Run 'codeql database export-diagnostics'
|
||||
*
|
||||
* Note that the "--sarif-include-diagnostics" option is always used, as the command should
|
||||
* only be run if the ExportDiagnosticsEnabled feature flag is on.
|
||||
*/
|
||||
databaseExportDiagnostics(
|
||||
databasePath: string,
|
||||
sarifFile: string,
|
||||
automationDetailsId: string | undefined
|
||||
): Promise<void>;
|
||||
/**
|
||||
* Run 'codeql diagnostics export'.
|
||||
*/
|
||||
|
|
@ -429,6 +440,10 @@ export function setCodeQL(partialCodeql: Partial<CodeQL>): CodeQL {
|
|||
partialCodeql,
|
||||
"databasePrintBaseline"
|
||||
),
|
||||
databaseExportDiagnostics: resolveFunction(
|
||||
partialCodeql,
|
||||
"databaseExportDiagnostics"
|
||||
),
|
||||
diagnosticsExport: resolveFunction(partialCodeql, "diagnosticsExport"),
|
||||
};
|
||||
return cachedCodeQL;
|
||||
|
|
@ -880,6 +895,9 @@ export async function getCodeQLForCmd(
|
|||
) {
|
||||
codeqlArgs.push("--sarif-add-baseline-file-info");
|
||||
}
|
||||
if (await features.getValue(Feature.ExportDiagnosticsEnabled, this)) {
|
||||
codeqlArgs.push("--sarif-include-diagnostics");
|
||||
}
|
||||
codeqlArgs.push(databasePath);
|
||||
if (querySuitePaths) {
|
||||
codeqlArgs.push(...querySuitePaths);
|
||||
|
|
@ -982,6 +1000,27 @@ export async function getCodeQLForCmd(
|
|||
];
|
||||
await new toolrunner.ToolRunner(cmd, args).exec();
|
||||
},
|
||||
async databaseExportDiagnostics(
|
||||
databasePath: string,
|
||||
sarifFile: string,
|
||||
automationDetailsId: string | undefined
|
||||
): Promise<void> {
|
||||
const args = [
|
||||
"database",
|
||||
"export-diagnostics",
|
||||
`${databasePath}`,
|
||||
"--db-cluster", // Database is always a cluster for CodeQL versions that support diagnostics.
|
||||
"--format=sarif-latest",
|
||||
`--output=${sarifFile}`,
|
||||
"--sarif-include-diagnostics", // ExportDiagnosticsEnabled is always true if this command is run.
|
||||
"-vvv",
|
||||
...getExtraOptionsFromEnv(["diagnostics", "export"]),
|
||||
];
|
||||
if (automationDetailsId !== undefined) {
|
||||
args.push("--sarif-category", automationDetailsId);
|
||||
}
|
||||
await new toolrunner.ToolRunner(cmd, args).exec();
|
||||
},
|
||||
async diagnosticsExport(
|
||||
sarifFile: string,
|
||||
automationDetailsId: string | undefined,
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ export enum Feature {
|
|||
CliConfigFileEnabled = "cli_config_file_enabled",
|
||||
DisableKotlinAnalysisEnabled = "disable_kotlin_analysis_enabled",
|
||||
ExportCodeScanningConfigEnabled = "export_code_scanning_config_enabled",
|
||||
ExportDiagnosticsEnabled = "export_diagnostics_enabled",
|
||||
MlPoweredQueriesEnabled = "ml_powered_queries_enabled",
|
||||
UploadFailedSarifEnabled = "upload_failed_sarif_enabled",
|
||||
}
|
||||
|
|
@ -60,6 +61,12 @@ export const featureConfig: Record<
|
|||
minimumVersion: "2.12.3",
|
||||
defaultValue: false,
|
||||
},
|
||||
[Feature.ExportDiagnosticsEnabled]: {
|
||||
envVar: "CODEQL_ACTION_EXPORT_DIAGNOSTICS",
|
||||
minimumVersion: "2.12.4",
|
||||
defaultValue: false,
|
||||
},
|
||||
|
||||
[Feature.MlPoweredQueriesEnabled]: {
|
||||
envVar: "CODEQL_ML_POWERED_QUERIES",
|
||||
minimumVersion: "2.7.5",
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ test("post: init action with debug mode on", async (t) => {
|
|||
});
|
||||
});
|
||||
|
||||
test("uploads failed SARIF run for typical workflow", async (t) => {
|
||||
test("uploads failed SARIF run with `diagnostics export` if feature flag is off", async (t) => {
|
||||
const actionsWorkflow = createTestWorkflow([
|
||||
{
|
||||
name: "Checkout repository",
|
||||
|
|
@ -107,6 +107,60 @@ test("uploads failed SARIF run for typical workflow", async (t) => {
|
|||
await testFailedSarifUpload(t, actionsWorkflow, { category: "my-category" });
|
||||
});
|
||||
|
||||
test("uploads failed SARIF run with `diagnostics export` if the database doesn't exist", async (t) => {
|
||||
const actionsWorkflow = createTestWorkflow([
|
||||
{
|
||||
name: "Checkout repository",
|
||||
uses: "actions/checkout@v3",
|
||||
},
|
||||
{
|
||||
name: "Initialize CodeQL",
|
||||
uses: "github/codeql-action/init@v2",
|
||||
with: {
|
||||
languages: "javascript",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Perform CodeQL Analysis",
|
||||
uses: "github/codeql-action/analyze@v2",
|
||||
with: {
|
||||
category: "my-category",
|
||||
},
|
||||
},
|
||||
]);
|
||||
await testFailedSarifUpload(t, actionsWorkflow, {
|
||||
category: "my-category",
|
||||
databaseExists: false,
|
||||
});
|
||||
});
|
||||
|
||||
test("uploads failed SARIF run with database export-diagnostics if the database exists and feature flag is on", async (t) => {
|
||||
const actionsWorkflow = createTestWorkflow([
|
||||
{
|
||||
name: "Checkout repository",
|
||||
uses: "actions/checkout@v3",
|
||||
},
|
||||
{
|
||||
name: "Initialize CodeQL",
|
||||
uses: "github/codeql-action/init@v2",
|
||||
with: {
|
||||
languages: "javascript",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Perform CodeQL Analysis",
|
||||
uses: "github/codeql-action/analyze@v2",
|
||||
with: {
|
||||
category: "my-category",
|
||||
},
|
||||
},
|
||||
]);
|
||||
await testFailedSarifUpload(t, actionsWorkflow, {
|
||||
category: "my-category",
|
||||
exportDiagnosticsEnabled: true,
|
||||
});
|
||||
});
|
||||
|
||||
test("doesn't upload failed SARIF for workflow with upload: false", async (t) => {
|
||||
const actionsWorkflow = createTestWorkflow([
|
||||
{
|
||||
|
|
@ -239,10 +293,14 @@ async function testFailedSarifUpload(
|
|||
actionsWorkflow: workflow.Workflow,
|
||||
{
|
||||
category,
|
||||
databaseExists = true,
|
||||
exportDiagnosticsEnabled = false,
|
||||
expectUpload = true,
|
||||
matrix = {},
|
||||
}: {
|
||||
category?: string;
|
||||
databaseExists?: boolean;
|
||||
exportDiagnosticsEnabled?: boolean;
|
||||
expectUpload?: boolean;
|
||||
matrix?: { [key: string]: string };
|
||||
} = {}
|
||||
|
|
@ -253,6 +311,9 @@ async function testFailedSarifUpload(
|
|||
languages: [],
|
||||
packs: [],
|
||||
} as unknown as configUtils.Config;
|
||||
if (databaseExists) {
|
||||
config.dbLocation = "path/to/database";
|
||||
}
|
||||
process.env["GITHUB_JOB"] = "analyze";
|
||||
process.env["GITHUB_REPOSITORY"] = "github/codeql-action-fake-repository";
|
||||
process.env["GITHUB_WORKSPACE"] =
|
||||
|
|
@ -264,6 +325,10 @@ async function testFailedSarifUpload(
|
|||
|
||||
const codeqlObject = await codeql.getCodeQLForTesting();
|
||||
sinon.stub(codeql, "getCodeQL").resolves(codeqlObject);
|
||||
const databaseExportDiagnosticsStub = sinon.stub(
|
||||
codeqlObject,
|
||||
"databaseExportDiagnostics"
|
||||
);
|
||||
const diagnosticsExportStub = sinon.stub(codeqlObject, "diagnosticsExport");
|
||||
|
||||
sinon.stub(workflow, "getWorkflow").resolves(actionsWorkflow);
|
||||
|
|
@ -275,10 +340,15 @@ async function testFailedSarifUpload(
|
|||
} as uploadLib.UploadResult);
|
||||
const waitForProcessing = sinon.stub(uploadLib, "waitForProcessing");
|
||||
|
||||
const features = [Feature.UploadFailedSarifEnabled];
|
||||
if (exportDiagnosticsEnabled) {
|
||||
features.push(Feature.ExportDiagnosticsEnabled);
|
||||
}
|
||||
|
||||
const result = await initActionPostHelper.tryUploadSarifIfRunFailed(
|
||||
config,
|
||||
parseRepositoryNwo("github/codeql-action"),
|
||||
createFeatures([Feature.UploadFailedSarifEnabled]),
|
||||
createFeatures(features),
|
||||
getRunnerLogger(true)
|
||||
);
|
||||
if (expectUpload) {
|
||||
|
|
@ -288,15 +358,26 @@ async function testFailedSarifUpload(
|
|||
});
|
||||
}
|
||||
if (expectUpload) {
|
||||
t.true(
|
||||
diagnosticsExportStub.calledOnceWith(
|
||||
sinon.match.string,
|
||||
category,
|
||||
sinon.match.any,
|
||||
sinon.match.any
|
||||
),
|
||||
`Actual args were: ${diagnosticsExportStub.args}`
|
||||
);
|
||||
if (databaseExists && exportDiagnosticsEnabled) {
|
||||
t.true(
|
||||
databaseExportDiagnosticsStub.calledOnceWith(
|
||||
config.dbLocation,
|
||||
sinon.match.string,
|
||||
category
|
||||
),
|
||||
`Actual args were: ${databaseExportDiagnosticsStub.args}`
|
||||
);
|
||||
} else {
|
||||
t.true(
|
||||
diagnosticsExportStub.calledOnceWith(
|
||||
sinon.match.string,
|
||||
category,
|
||||
config,
|
||||
sinon.match.any
|
||||
),
|
||||
`Actual args were: ${diagnosticsExportStub.args}`
|
||||
);
|
||||
}
|
||||
t.true(
|
||||
uploadFromActions.calledOnceWith(
|
||||
sinon.match.string,
|
||||
|
|
|
|||
|
|
@ -64,9 +64,20 @@ async function maybeUploadFailedSarif(
|
|||
}
|
||||
const category = getCategoryInputOrThrow(workflow, jobName, matrix);
|
||||
const checkoutPath = getCheckoutPathInputOrThrow(workflow, jobName, matrix);
|
||||
const databasePath = config.dbLocation;
|
||||
|
||||
const sarifFile = "../codeql-failed-run.sarif";
|
||||
await codeql.diagnosticsExport(sarifFile, category, config, features);
|
||||
|
||||
// If there is no database or the feature flag is off, we run 'export diagnostics'
|
||||
if (
|
||||
databasePath === undefined ||
|
||||
!(await features.getValue(Feature.ExportDiagnosticsEnabled, codeql))
|
||||
) {
|
||||
await codeql.diagnosticsExport(sarifFile, category, config, features);
|
||||
} else {
|
||||
// We call 'database export-diagnostics' to find any per-database diagnostics.
|
||||
await codeql.databaseExportDiagnostics(databasePath, sarifFile, category);
|
||||
}
|
||||
|
||||
core.info(`Uploading failed SARIF file ${sarifFile}`);
|
||||
const uploadResult = await uploadLib.uploadFromActions(
|
||||
|
|
@ -134,6 +145,7 @@ export async function run(
|
|||
features,
|
||||
logger
|
||||
);
|
||||
|
||||
if (uploadFailedSarifResult.upload_failed_run_skipped_because) {
|
||||
logger.debug(
|
||||
"Won't upload a failed SARIF file for this CodeQL code scanning run because: " +
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
/**
|
||||
* Environment variable that is set to true when the CodeQL Action has invoked
|
||||
* the Go autobuilder.
|
||||
*/
|
||||
export const CODEQL_ACTION_DID_AUTOBUILD_GOLANG =
|
||||
"CODEQL_ACTION_DID_AUTOBUILD_GOLANG";
|
||||
|
||||
/**
|
||||
* This environment variable is set to true when the `analyze` Action
|
||||
* completes successfully.
|
||||
|
|
|
|||
|
|
@ -42,13 +42,6 @@ export const DEFAULT_DEBUG_ARTIFACT_NAME = "debug-artifacts";
|
|||
*/
|
||||
export const DEFAULT_DEBUG_DATABASE_NAME = "db";
|
||||
|
||||
/**
|
||||
* Environment variable that is set to "true" when the CodeQL Action has invoked
|
||||
* the Go autobuilder.
|
||||
*/
|
||||
export const DID_AUTOBUILD_GO_ENV_VAR_NAME =
|
||||
"CODEQL_ACTION_DID_AUTOBUILD_GOLANG";
|
||||
|
||||
export interface SarifFile {
|
||||
version?: string | null;
|
||||
runs: SarifRun[];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue