diff --git a/lib/entry-points.js b/lib/entry-points.js index 674e6425f..629ee5b6b 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -177425,7 +177425,7 @@ async function getCodeQLForCmd(logger2, cmd, checkVersion) { } await new toolrunner3.ToolRunner(cmd, args).exec(); }, - async diagnosticsExport(sarifFile, automationDetailsId, config) { + async diagnosticsExport(sarifFile, automationDetailsId, config, diagnosticDir) { const args = [ "diagnostics", "export", @@ -177434,6 +177434,7 @@ async function getCodeQLForCmd(logger2, cmd, checkVersion) { `--sarif-codescanning-config=${getGeneratedCodeScanningConfigPath( config )}`, + ...diagnosticDir !== void 0 ? [`--diagnostic-dir=${diagnosticDir}`] : [], ...getExtraOptionsFromEnv(["diagnostics", "export"]) ]; if (automationDetailsId !== void 0) { @@ -187021,7 +187022,13 @@ async function generateFailedSarif(logger2, features, config, category, checkout sarifFile = "../codeql-failed-run.sarif"; } if (databasePath === void 0 || !await features.getValue("export_diagnostics_enabled" /* ExportDiagnosticsEnabled */, codeql)) { - await codeql.diagnosticsExport(sarifFile, category, config); + let diagnosticDir = getTempDiagnosticPath( + config.tempDir + ); + if (!fs31.existsSync(diagnosticDir)) { + diagnosticDir = void 0; + } + await codeql.diagnosticsExport(sarifFile, category, config, diagnosticDir); } else { await codeql.databaseExportDiagnostics(databasePath, sarifFile, category); } diff --git a/src/codeql.ts b/src/codeql.ts index a76e29727..5981929c1 100644 --- a/src/codeql.ts +++ b/src/codeql.ts @@ -199,6 +199,7 @@ export interface CodeQL { sarifFile: string, automationDetailsId: string | undefined, config: ActionConfig<"tempDir">, + diagnosticDir: string | undefined, ): Promise; /** Get the location of an extractor for the specified language. */ resolveExtractor(language: Language): Promise; @@ -915,6 +916,7 @@ async function getCodeQLForCmd( sarifFile: string, automationDetailsId: string | undefined, config: ActionConfig<"tempDir">, + diagnosticDir: string | undefined, ): Promise { const args = [ "diagnostics", @@ -924,6 +926,9 @@ async function getCodeQLForCmd( `--sarif-codescanning-config=${getGeneratedCodeScanningConfigPath( config, )}`, + ...(diagnosticDir !== undefined + ? [`--diagnostic-dir=${diagnosticDir}`] + : []), ...getExtraOptionsFromEnv(["diagnostics", "export"]), ]; if (automationDetailsId !== undefined) { diff --git a/src/init-action-post-helper.test.ts b/src/init-action-post-helper.test.ts index 554add71f..b90ad3047 100644 --- a/src/init-action-post-helper.test.ts +++ b/src/init-action-post-helper.test.ts @@ -1,3 +1,6 @@ +import * as fs from "fs"; +import * as path from "path"; + import * as core from "@actions/core"; import test, { ExecutionContext } from "ava"; import * as sinon from "sinon"; @@ -8,6 +11,7 @@ import * as apiClient from "./api-client"; import * as codeql from "./codeql"; import * as configUtils from "./config-utils"; import * as debugArtifacts from "./debug-artifacts"; +import { getTempDiagnosticPath } from "./diagnostics"; import { EnvVar } from "./environment"; import { Feature } from "./feature-flags"; import * as initActionPostHelper from "./init-action-post-helper"; @@ -35,6 +39,47 @@ const NUM_BYTES_PER_GIB = 1024 * 1024 * 1024; setupTests(test); +test.serial("generateFailedSarif picks up unwritten diagnostics", async (t) => + util.withTmpDir(async (tmpDir) => { + const logger = new RecordingLogger(); + + const tempDiagDir = getTempDiagnosticPath(tmpDir); + fs.mkdirSync(tempDiagDir); + + const codeqlObject = await codeql.getCodeQLForTesting(); + const diagnosticsExport = sinon + .stub(codeqlObject, "diagnosticsExport") + .resolves(); + + sinon.stub(codeql, "getCodeQL").resolves(codeqlObject); + + const sarifFile = path.join(tmpDir, "failed.sarif"); + const result = await initActionPostHelper.generateFailedSarif( + logger, + createFeatures([]), + createTestConfig({ + tempDir: tmpDir, + // Explicitly undefined to ensure we get the "no database" path. + dbLocation: undefined, + }), + "category", + "", + sarifFile, + ); + + t.truthy(result); + t.is(diagnosticsExport.callCount, 1); + t.true( + diagnosticsExport.calledOnceWith( + sarifFile, + "category", + sinon.match.any, + tempDiagDir, + ), + ); + }), +); + test.serial("init-post action with debug mode off", async (t) => { return await util.withTmpDir(async (tmpDir) => { setupActionsVars(tmpDir, tmpDir); diff --git a/src/init-action-post-helper.ts b/src/init-action-post-helper.ts index cfaab312f..83c2aec9f 100644 --- a/src/init-action-post-helper.ts +++ b/src/init-action-post-helper.ts @@ -24,6 +24,7 @@ import { type UploadDebugArtifactsConfig, } from "./debug-artifacts"; import * as dependencyCaching from "./dependency-caching"; +import { getTempDiagnosticPath } from "./diagnostics"; import { EnvVar, ReadOnlyEnv } from "./environment"; import { Feature, FeatureEnablement } from "./feature-flags"; import { Logger } from "./logging"; @@ -177,7 +178,16 @@ async function prepareFailedSarif( } } -async function generateFailedSarif( +/** + * Generates a SARIF file for a failed run, i.e. an analysis where no SARIF was produced as a result of the analysis. + * + * Not intended to be called directly, other than by `prepareFailedSarif`. + * + * @param category The analysis category to include in the SARIF. + * @param checkoutPath The path where the repository was checked out at. + * @param sarifFile The SARIF file to produce. + */ +export async function generateFailedSarif( logger: Logger, features: FeatureEnablement, config: ActionConfig<"codeQLCmd" | "tempDir">, @@ -198,7 +208,19 @@ async function generateFailedSarif( databasePath === undefined || !(await features.getValue(Feature.ExportDiagnosticsEnabled, codeql)) ) { - await codeql.diagnosticsExport(sarifFile, category, config); + // If we don't have a database path, then we may have diagnostics that were written to a + // temporary location. Get the path that they would have been written to and check whether + // it exists. If so, pass it on to the CLI. The CLI will not complain if we give it a + // path that doesn't exist, so we are just being extra defensive here. + let diagnosticDir: string | undefined = getTempDiagnosticPath( + config.tempDir, + ); + + if (!fs.existsSync(diagnosticDir)) { + diagnosticDir = undefined; + } + + await codeql.diagnosticsExport(sarifFile, category, config, diagnosticDir); } else { // We call 'database export-diagnostics' to find any per-database diagnostics. await codeql.databaseExportDiagnostics(databasePath, sarifFile, category);