From 23835d3cbec0d8995d3f1fdda75d91d988a2699f Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 9 Oct 2026 18:56:55 +0100 Subject: [PATCH 01/14] Refactor `writeDiagnosticFile` out of `writeDiagnostic` --- lib/entry-points.js | 25 +++++++++++++----------- src/diagnostics.ts | 46 +++++++++++++++++++++++++++------------------ 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index 44d94d1e08..744a2754a2 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -173013,6 +173013,19 @@ function addNoLanguageDiagnostic(config, diagnostic) { unwrittenDefaultLanguageDiagnostics.push(diagnostic); } } +function writeDiagnosticFile(diagnosticsPath, diagnostic) { + (0, import_fs.mkdirSync)(diagnosticsPath, { recursive: true }); + const uniqueSuffix = (diagnosticCounter++).toString(); + const sanitizedTimestamp = diagnostic.timestamp.replace( + /[^a-zA-Z0-9.-]/g, + "" + ); + const jsonPath = import_path2.default.resolve( + diagnosticsPath, + `codeql-action-${sanitizedTimestamp}-${uniqueSuffix}.json` + ); + (0, import_fs.writeFileSync)(jsonPath, JSON.stringify(diagnostic)); +} function writeDiagnostic(config, language, diagnostic) { const logger2 = getActionsLogger(); const databasePath = language ? getCodeQLDatabasePath(config, language) : config.dbLocation; @@ -173022,17 +173035,7 @@ function writeDiagnostic(config, language, diagnostic) { "codeql-action" ); try { - (0, import_fs.mkdirSync)(diagnosticsPath, { recursive: true }); - const uniqueSuffix = (diagnosticCounter++).toString(); - const sanitizedTimestamp = diagnostic.timestamp.replace( - /[^a-zA-Z0-9.-]/g, - "" - ); - const jsonPath = import_path2.default.resolve( - diagnosticsPath, - `codeql-action-${sanitizedTimestamp}-${uniqueSuffix}.json` - ); - (0, import_fs.writeFileSync)(jsonPath, JSON.stringify(diagnostic)); + writeDiagnosticFile(diagnosticsPath, diagnostic); } catch (err) { logger2.warning(`Unable to write diagnostic message to database: ${err}`); logger2.debug(JSON.stringify(diagnostic)); diff --git a/src/diagnostics.ts b/src/diagnostics.ts index fa0e87c046..caab84934e 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -177,6 +177,33 @@ export function addNoLanguageDiagnostic( } } +/** + * Writes {@link diagnostic} to a file in {@link diagnosticsPath}. + */ +function writeDiagnosticFile( + diagnosticsPath: string, + diagnostic: DiagnosticMessage, +) { + // Create the directory if it doesn't exist yet. + mkdirSync(diagnosticsPath, { recursive: true }); + + // Include a monotonically increasing suffix to avoid filename collisions + // between diagnostics produced within the same millisecond. + const uniqueSuffix = (diagnosticCounter++).toString(); + // We should only need to remove colons, but to be defensive, only allow a restricted set of + // characters. + const sanitizedTimestamp = diagnostic.timestamp.replace( + /[^a-zA-Z0-9.-]/g, + "", + ); + const jsonPath = path.resolve( + diagnosticsPath, + `codeql-action-${sanitizedTimestamp}-${uniqueSuffix}.json`, + ); + + writeFileSync(jsonPath, JSON.stringify(diagnostic)); +} + /** * Writes the given diagnostic to the database. * @@ -200,24 +227,7 @@ function writeDiagnostic( ); try { - // Create the directory if it doesn't exist yet. - mkdirSync(diagnosticsPath, { recursive: true }); - - // Include a monotonically increasing suffix to avoid filename collisions - // between diagnostics produced within the same millisecond. - const uniqueSuffix = (diagnosticCounter++).toString(); - // We should only need to remove colons, but to be defensive, only allow a restricted set of - // characters. - const sanitizedTimestamp = diagnostic.timestamp.replace( - /[^a-zA-Z0-9.-]/g, - "", - ); - const jsonPath = path.resolve( - diagnosticsPath, - `codeql-action-${sanitizedTimestamp}-${uniqueSuffix}.json`, - ); - - writeFileSync(jsonPath, JSON.stringify(diagnostic)); + writeDiagnosticFile(diagnosticsPath, diagnostic); } catch (err) { logger.warning(`Unable to write diagnostic message to database: ${err}`); logger.debug(JSON.stringify(diagnostic)); From 50b827040e072048094bfb4ad0a4c054206f5711 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 9 Oct 2026 19:29:16 +0100 Subject: [PATCH 02/14] Write diagnostics to temporary location when database isn't ready --- lib/entry-points.js | 65 ++++++++++------- src/config/db-config.test.ts | 134 ++++++++++++++++++++--------------- src/diagnostics.ts | 130 ++++++++++++++++++++++----------- src/init-action.ts | 9 +-- src/setup-codeql.test.ts | 86 ++++++++++++---------- 5 files changed, 257 insertions(+), 167 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index 744a2754a2..f58429a076 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -172978,8 +172978,7 @@ var semver4 = __toESM(require_semver2()); // src/diagnostics.ts var import_fs = require("fs"); var import_path2 = __toESM(require("path")); -var unwrittenDiagnostics = []; -var unwrittenDefaultLanguageDiagnostics = []; +var temporaryDiagnostics = []; var diagnosticCounter = 0; function makeDiagnostic(id, name, data = void 0) { return { @@ -172988,6 +172987,16 @@ function makeDiagnostic(id, name, data = void 0) { source: { ...data?.source, id, name } }; } +function getTempDiagnosticPath(tmpDir, language) { + const root = import_path2.default.join(tmpDir, "codeql-action-temp-diagnostics"); + return getDiagnosticsPath(root, language); +} +function getDiagnosticsPath(basePath, language) { + if (language !== void 0) { + return import_path2.default.resolve(basePath, language, "diagnostic", "codeql-action"); + } + return basePath; +} function addDiagnostic(config, language, diagnostic) { const logger2 = getActionsLogger(); const databasePath = language ? getCodeQLDatabasePath(config, language) : config.dbLocation; @@ -172997,7 +173006,9 @@ function addDiagnostic(config, language, diagnostic) { logger2.debug( `Writing a diagnostic for ${language}, but the database at ${databasePath} does not exist yet.` ); - unwrittenDiagnostics.push({ diagnostic, language }); + const tempDiagnosticsPath = getTempDiagnosticPath(config.tempDir, language); + const diagnosticPath = writeDiagnosticFile(tempDiagnosticsPath, diagnostic); + temporaryDiagnostics.push({ path: diagnosticPath, language }); } } function addNoLanguageDiagnostic(config, diagnostic) { @@ -173010,7 +173021,9 @@ function addNoLanguageDiagnostic(config, diagnostic) { diagnostic ); } else { - unwrittenDefaultLanguageDiagnostics.push(diagnostic); + const tempDiagnosticsPath = getTempDiagnosticPath(getTemporaryDirectory()); + const diagnosticPath = writeDiagnosticFile(tempDiagnosticsPath, diagnostic); + temporaryDiagnostics.push({ path: diagnosticPath }); } } function writeDiagnosticFile(diagnosticsPath, diagnostic) { @@ -173025,15 +173038,14 @@ function writeDiagnosticFile(diagnosticsPath, diagnostic) { `codeql-action-${sanitizedTimestamp}-${uniqueSuffix}.json` ); (0, import_fs.writeFileSync)(jsonPath, JSON.stringify(diagnostic)); + return jsonPath; +} +function getDatabaseDiagnosticsPath(config, language) { + return getDiagnosticsPath(config.dbLocation, language); } function writeDiagnostic(config, language, diagnostic) { const logger2 = getActionsLogger(); - const databasePath = language ? getCodeQLDatabasePath(config, language) : config.dbLocation; - const diagnosticsPath = import_path2.default.resolve( - databasePath, - "diagnostic", - "codeql-action" - ); + const diagnosticsPath = getDatabaseDiagnosticsPath(config, language); try { writeDiagnosticFile(diagnosticsPath, diagnostic); } catch (err) { @@ -173041,30 +173053,34 @@ function writeDiagnostic(config, language, diagnostic) { logger2.debug(JSON.stringify(diagnostic)); } } -function logUnwrittenDiagnostics() { +function logTemporaryDiagnostics() { const logger2 = getActionsLogger(); - const num = unwrittenDiagnostics.length; + const num = temporaryDiagnostics.length; if (num > 0) { logger2.warning( `${num} diagnostic(s) could not be written to the database and will not appear on the Tool Status Page.` ); - for (const unwritten of unwrittenDiagnostics) { - logger2.debug(JSON.stringify(unwritten.diagnostic)); + for (const temporary of temporaryDiagnostics) { + logger2.debug((0, import_fs.readFileSync)(temporary.path, "utf-8")); } } } function flushDiagnostics(config) { const logger2 = getActionsLogger(); - const diagnosticsCount = unwrittenDiagnostics.length + unwrittenDefaultLanguageDiagnostics.length; - logger2.debug(`Writing ${diagnosticsCount} diagnostic(s) to database.`); - for (const unwritten of unwrittenDiagnostics) { - writeDiagnostic(config, unwritten.language, unwritten.diagnostic); - } - for (const unwritten of unwrittenDefaultLanguageDiagnostics) { - addNoLanguageDiagnostic(config, unwritten); + const diagnosticsCount = temporaryDiagnostics.length; + logger2.debug( + `Moving ${diagnosticsCount} diagnostic(s) to their respective databases.` + ); + for (const temporary of temporaryDiagnostics) { + const directory = getDatabaseDiagnosticsPath( + config, + temporary.language ?? config.languages[0] + ); + const filename = import_path2.default.basename(temporary.path); + const destination = import_path2.default.join(directory, filename); + (0, import_fs.renameSync)(temporary.path, destination); } - unwrittenDiagnostics = []; - unwrittenDefaultLanguageDiagnostics = []; + temporaryDiagnostics = []; } function makeTelemetryDiagnostic(id, name, attributes, tags) { return makeDiagnostic(id, name, { @@ -186570,6 +186586,7 @@ async function run3(actionState) { if (statusReportBase !== void 0) { await sendStatusReport(statusReportBase); } + logTemporaryDiagnostics(); return; } let overlayBaseDatabaseStats; @@ -186779,7 +186796,7 @@ exec ${goBinaryPath} "$@"` ); return; } finally { - logUnwrittenDiagnostics(); + logTemporaryDiagnostics(); } await sendCompletedStatusReport2( startedAt, diff --git a/src/config/db-config.test.ts b/src/config/db-config.test.ts index 76fa918015..386556f3d3 100644 --- a/src/config/db-config.test.ts +++ b/src/config/db-config.test.ts @@ -9,8 +9,9 @@ import { LoggedMessage, makeMacro, RecordingLogger, + setupActionsVars, } from "../testing-utils"; -import { ConfigurationError, prettyPrintPack } from "../util"; +import { ConfigurationError, prettyPrintPack, withTmpDir } from "../util"; import * as dbConfig from "./db-config"; @@ -562,69 +563,84 @@ test("mergeDefaultSetupAndUserConfigs - keeps other properties from user-supplie t.deepEqual(result, configFile); }); -test("mergeDefaultSetupAndUserConfigs - ignores, but warns about, unknown keys from Default Setup", async (t) => { - const logger = new RecordingLogger(); - const configFile: dbConfig.UserConfig = { - "query-filters": [{ exclude: { a: "b" } }], - "paths-ignore": ["path"], - }; - - const result = dbConfig.mergeDefaultSetupAndUserConfigs( - logger, - { - "default-setup": { - borg: [], - org: { - unknown: "foo", - "model-packs": [], +test.serial( + "mergeDefaultSetupAndUserConfigs - ignores, but warns about, unknown keys from Default Setup", + async (t) => { + const logger = new RecordingLogger(); + const configFile: dbConfig.UserConfig = { + "query-filters": [{ exclude: { a: "b" } }], + "paths-ignore": ["path"], + }; + + await withTmpDir(async (tmpDir) => { + setupActionsVars(tmpDir, tmpDir); + const result = dbConfig.mergeDefaultSetupAndUserConfigs( + logger, + { + "default-setup": { + borg: [], + org: { + unknown: "foo", + "model-packs": [], + }, + } as unknown as dbConfig.DefaultSetupConfig, + "paths-ignore": ["other-path"], }, - } as unknown as dbConfig.DefaultSetupConfig, - "paths-ignore": ["other-path"], - }, - configFile, - ); + configFile, + ); + + t.deepEqual(result, { + ...configFile, + "default-setup": { org: { "model-packs": [] } }, + }); + + const expectedUnrecognisedKeys = [ + ".default-setup.org.unknown", + ".default-setup.borg", + ".paths-ignore", + ].join(", "); + checkExpectedLogMessages(t, logger.messages, [ + `Unrecognised keys in Default Setup configuration: ${expectedUnrecognisedKeys}`, + ]); + }); + }, +); - t.deepEqual(result, { - ...configFile, - "default-setup": { org: { "model-packs": [] } }, - }); - - const expectedUnrecognisedKeys = [ - ".default-setup.org.unknown", - ".default-setup.borg", - ".paths-ignore", - ].join(", "); - checkExpectedLogMessages(t, logger.messages, [ - `Unrecognised keys in Default Setup configuration: ${expectedUnrecognisedKeys}`, - ]); -}); +test.serial( + "mergeDefaultSetupAndUserConfigs - warns about invalid keys from Default Setup", + async (t) => { + const logger = new RecordingLogger(); + const configFile: dbConfig.UserConfig = {}; -test("mergeDefaultSetupAndUserConfigs - warns about invalid keys from Default Setup", async (t) => { - const logger = new RecordingLogger(); - const configFile: dbConfig.UserConfig = {}; + await withTmpDir(async (tmpDir) => { + setupActionsVars(tmpDir, tmpDir); - const result = dbConfig.mergeDefaultSetupAndUserConfigs( - logger, - { - "default-setup": { - org: { - "model-packs": [123], + const result = dbConfig.mergeDefaultSetupAndUserConfigs( + logger, + { + "default-setup": { + org: { + "model-packs": [123], + }, + } as unknown as dbConfig.DefaultSetupConfig, }, - } as unknown as dbConfig.DefaultSetupConfig, - }, - configFile, - ); - - t.deepEqual(result, { - ...configFile, - "default-setup": { org: { "model-packs": [123] } }, - }); - - const expectedInvalidKeys = [".default-setup.org.model-packs[0]"].join(", "); - checkExpectedLogMessages(t, logger.messages, [ - `Invalid keys in Default Setup configuration: ${expectedInvalidKeys}`, - ]); -}); + configFile, + ); + + t.deepEqual(result, { + ...configFile, + "default-setup": { org: { "model-packs": [123] } }, + }); + + const expectedInvalidKeys = [".default-setup.org.model-packs[0]"].join( + ", ", + ); + checkExpectedLogMessages(t, logger.messages, [ + `Invalid keys in Default Setup configuration: ${expectedInvalidKeys}`, + ]); + }); + }, +); /** Parses `contents` as a configuration without validating it. */ function parseUnvalidatedConfig(contents: string): dbConfig.UserConfig { diff --git a/src/diagnostics.ts b/src/diagnostics.ts index caab84934e..f08c83c420 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -1,6 +1,13 @@ -import { existsSync, mkdirSync, writeFileSync } from "fs"; +import { + existsSync, + mkdirSync, + readFileSync, + renameSync, + writeFileSync, +} from "fs"; import path from "path"; +import { getTemporaryDirectory } from "./actions-util"; import type { Config } from "./config-utils"; import { Language } from "./languages"; import { getActionsLogger } from "./logging"; @@ -84,22 +91,16 @@ export type DiagnosticMessage = DiagnosticMessageOptions & { source: DiagnosticSource; }; -/** Represents a diagnostic message that has not yet been written to the database. */ -interface UnwrittenDiagnostic { - /** The diagnostic message that has not yet been written. */ - diagnostic: DiagnosticMessage; - /** The language the diagnostic is for. */ - language: Language; +/** Represents a diagnostic message that has not yet been saved to the database. */ +interface TemporaryDiagnostic { + /** The path to which the diagnostic has temporarily been written to. */ + path: string; + /** The language the diagnostic is for, if any. */ + language?: Language; } -/** A list of diagnostics which have not yet been written to disk. */ -let unwrittenDiagnostics: UnwrittenDiagnostic[] = []; - -/** - * A list of diagnostics which have not yet been written to disk, - * and where the language does not matter. - */ -let unwrittenDefaultLanguageDiagnostics: DiagnosticMessage[] = []; +/** A list of diagnostics which have not yet been saved to the database. */ +let temporaryDiagnostics: TemporaryDiagnostic[] = []; /** * Counter used to generate a unique suffix for each diagnostic filename, so that @@ -128,6 +129,29 @@ export function makeDiagnostic( }; } +/** + * Gets a path relative to {@link tmpDir} where diagnostics for {@link language} + * can temporarily be stored at before the database is initialised. If {@link language} + * is `undefined`, then the common base path for all languages relative to {@link tmpDir} + * is returned. + */ +export function getTempDiagnosticPath(tmpDir: string, language?: Language) { + const root = path.join(tmpDir, "codeql-action-temp-diagnostics"); + return getDiagnosticsPath(root, language); +} + +/** + * Gets a path relative to {@link basePath} where diagnostics for {@link language} + * should be stored at. If {@link language} is `undefined`, then {@link baseBath} + * is returned instead. + */ +export function getDiagnosticsPath(basePath: string, language?: Language) { + if (language !== undefined) { + return path.resolve(basePath, language, "diagnostic", "codeql-action"); + } + return basePath; +} + /** * Adds the given diagnostic to the database. If the database does not yet exist, * the diagnostic will be written to it once it has been created. @@ -155,7 +179,12 @@ export function addDiagnostic( `Writing a diagnostic for ${language}, but the database at ${databasePath} does not exist yet.`, ); - unwrittenDiagnostics.push({ diagnostic, language }); + // Write the diagnostic to a temporary location. + const tempDiagnosticsPath = getTempDiagnosticPath(config.tempDir, language); + const diagnosticPath = writeDiagnosticFile(tempDiagnosticsPath, diagnostic); + + // Track the temporary file. + temporaryDiagnostics.push({ path: diagnosticPath, language }); } } @@ -173,7 +202,11 @@ export function addNoLanguageDiagnostic( diagnostic, ); } else { - unwrittenDefaultLanguageDiagnostics.push(diagnostic); + const tempDiagnosticsPath = getTempDiagnosticPath(getTemporaryDirectory()); + const diagnosticPath = writeDiagnosticFile(tempDiagnosticsPath, diagnostic); + + // Track the temporary file. + temporaryDiagnostics.push({ path: diagnosticPath }); } } @@ -183,7 +216,7 @@ export function addNoLanguageDiagnostic( function writeDiagnosticFile( diagnosticsPath: string, diagnostic: DiagnosticMessage, -) { +): string { // Create the directory if it doesn't exist yet. mkdirSync(diagnosticsPath, { recursive: true }); @@ -202,6 +235,16 @@ function writeDiagnosticFile( ); writeFileSync(jsonPath, JSON.stringify(diagnostic)); + + return jsonPath; +} + +/** Gets the path where diagnostics for {@link language} should be stored in the database. */ +export function getDatabaseDiagnosticsPath( + config: Config, + language: Language | undefined, +) { + return getDiagnosticsPath(config.dbLocation, language); } /** @@ -217,14 +260,7 @@ function writeDiagnostic( diagnostic: DiagnosticMessage, ) { const logger = getActionsLogger(); - const databasePath = language - ? getCodeQLDatabasePath(config, language) - : config.dbLocation; - const diagnosticsPath = path.resolve( - databasePath, - "diagnostic", - "codeql-action", - ); + const diagnosticsPath = getDatabaseDiagnosticsPath(config, language); try { writeDiagnosticFile(diagnosticsPath, diagnostic); @@ -234,39 +270,47 @@ function writeDiagnostic( } } -/** Report if there are unwritten diagnostics and write them to the log. */ -export function logUnwrittenDiagnostics() { +/** Report if there are temporary diagnostics and write them to the log. */ +export function logTemporaryDiagnostics() { const logger = getActionsLogger(); - const num = unwrittenDiagnostics.length; + const num = temporaryDiagnostics.length; + if (num > 0) { logger.warning( `${num} diagnostic(s) could not be written to the database and will not appear on the Tool Status Page.`, ); - for (const unwritten of unwrittenDiagnostics) { - logger.debug(JSON.stringify(unwritten.diagnostic)); + for (const temporary of temporaryDiagnostics) { + logger.debug(readFileSync(temporary.path, "utf-8")); } } } -/** Writes all unwritten diagnostics to disk. */ +/** Relocates all temporary diagnostics to the respective databases. */ export function flushDiagnostics(config: Config) { const logger = getActionsLogger(); - const diagnosticsCount = - unwrittenDiagnostics.length + unwrittenDefaultLanguageDiagnostics.length; - logger.debug(`Writing ${diagnosticsCount} diagnostic(s) to database.`); + const diagnosticsCount = temporaryDiagnostics.length; + logger.debug( + `Moving ${diagnosticsCount} diagnostic(s) to their respective databases.`, + ); - for (const unwritten of unwrittenDiagnostics) { - writeDiagnostic(config, unwritten.language, unwritten.diagnostic); - } - for (const unwritten of unwrittenDefaultLanguageDiagnostics) { - addNoLanguageDiagnostic(config, unwritten); + for (const temporary of temporaryDiagnostics) { + // If `temporary.language` is `undefined`, then we didn't have a `config` at the time that + // `addNoLanguageDiagnostic` was called. In that case, we arbitrarily choose the first + // configured language here like we would have done in `addNoLanguageDiagnostic`. + const directory = getDatabaseDiagnosticsPath( + config, + temporary.language ?? config.languages[0], + ); + const filename = path.basename(temporary.path); + const destination = path.join(directory, filename); + + renameSync(temporary.path, destination); } - // Reset the unwritten diagnostics arrays. - unwrittenDiagnostics = []; - unwrittenDefaultLanguageDiagnostics = []; + // Reset the temporary diagnostics arrays. + temporaryDiagnostics = []; } /** diff --git a/src/init-action.ts b/src/init-action.ts index 5376763b52..73ffcd6f1c 100644 --- a/src/init-action.ts +++ b/src/init-action.ts @@ -40,7 +40,7 @@ import { addDiagnostic, addNoLanguageDiagnostic, flushDiagnostics, - logUnwrittenDiagnostics, + logTemporaryDiagnostics, makeDiagnostic, makeTelemetryDiagnostic, } from "./diagnostics"; @@ -484,6 +484,7 @@ async function run( if (statusReportBase !== undefined) { await sendStatusReport(statusReportBase); } + logTemporaryDiagnostics(); return; } @@ -766,8 +767,8 @@ async function run( ); } - // Write diagnostics to the database that we previously stored in memory because the database - // did not exist until now. + // Move diagnostics to the database that we previously kept in a temporary location + // because the database did not exist until now flushDiagnostics(config); // We save the config here instead of at the end of `initConfig` because we @@ -797,7 +798,7 @@ async function run( ); return; } finally { - logUnwrittenDiagnostics(); + logTemporaryDiagnostics(); } await sendCompletedStatusReport( startedAt, diff --git a/src/setup-codeql.test.ts b/src/setup-codeql.test.ts index 3fef11e63e..70a459d93b 100644 --- a/src/setup-codeql.test.ts +++ b/src/setup-codeql.test.ts @@ -1647,16 +1647,20 @@ test.serial( }, ]); - const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases( - overlayMatchEnabledVersions, - ["javascript"], - createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]), - getRunnerLogger(true), - ); - t.deepEqual(result, [ - { cliVersion: "2.20.1", tagName: "codeql-bundle-v2.20.1" }, - { cliVersion: "2.20.0", tagName: "codeql-bundle-v2.20.0" }, - ]); + await withTmpDir(async (tmpDir) => { + setupActionsVars(tmpDir, tmpDir); + const result = + await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases( + overlayMatchEnabledVersions, + ["javascript"], + createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]), + getRunnerLogger(true), + ); + t.deepEqual(result, [ + { cliVersion: "2.20.1", tagName: "codeql-bundle-v2.20.1" }, + { cliVersion: "2.20.0", tagName: "codeql-bundle-v2.20.0" }, + ]); + }); }, ); @@ -1773,21 +1777,25 @@ test.serial( }, ]); - const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases( - overlayMatchEnabledVersions, - ["javascript"], - createFeatures([Feature.OverlayAnalysisMatchCodeqlVersionDryRun]), - getRunnerLogger(true), - ); - t.deepEqual( - result, - [], - "Dry-run should return an empty list so the caller falls back.", - ); - t.assert( - listStub.calledOnce, - "Dry-run should still list Actions caches to populate the diagnostic.", - ); + await withTmpDir(async (tmpDir) => { + setupActionsVars(tmpDir, tmpDir); + const result = + await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases( + overlayMatchEnabledVersions, + ["javascript"], + createFeatures([Feature.OverlayAnalysisMatchCodeqlVersionDryRun]), + getRunnerLogger(true), + ); + t.deepEqual( + result, + [], + "Dry-run should return an empty list so the caller falls back.", + ); + t.assert( + listStub.calledOnce, + "Dry-run should still list Actions caches to populate the diagnostic.", + ); + }); }, ); @@ -1801,18 +1809,22 @@ test.serial( }, ]); - const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases( - overlayMatchEnabledVersions, - ["javascript"], - createFeatures([ - Feature.OverlayAnalysisMatchCodeqlVersion, - Feature.OverlayAnalysisMatchCodeqlVersionDryRun, - ]), - getRunnerLogger(true), - ); - t.deepEqual(result, [ - { cliVersion: "2.20.1", tagName: "codeql-bundle-v2.20.1" }, - ]); + await withTmpDir(async (tmpDir) => { + setupActionsVars(tmpDir, tmpDir); + const result = + await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases( + overlayMatchEnabledVersions, + ["javascript"], + createFeatures([ + Feature.OverlayAnalysisMatchCodeqlVersion, + Feature.OverlayAnalysisMatchCodeqlVersionDryRun, + ]), + getRunnerLogger(true), + ); + t.deepEqual(result, [ + { cliVersion: "2.20.1", tagName: "codeql-bundle-v2.20.1" }, + ]); + }); }, ); From bbeb45dd7a7aa964bac578512189eca2d7347581 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 9 Oct 2026 21:59:55 +0100 Subject: [PATCH 03/14] Make diagnostics less dependent on global state Add limited test coverage --- lib/entry-points.js | 809 ++++++++++++++++++++-------------------- src/diagnostics.test.ts | 111 ++++++ src/diagnostics.ts | 61 +-- src/init-action.ts | 6 +- 4 files changed, 556 insertions(+), 431 deletions(-) create mode 100644 src/diagnostics.test.ts diff --git a/lib/entry-points.js b/lib/entry-points.js index f58429a076..743fb27081 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -213,7 +213,7 @@ var require_file_command = __commonJS({ exports2.issueFileCommand = issueFileCommand; exports2.prepareKeyValueMessage = prepareKeyValueMessage; var crypto3 = __importStar2(require("crypto")); - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var os7 = __importStar2(require("os")); var utils_1 = require_utils(); function issueFileCommand(command, message) { @@ -221,10 +221,10 @@ var require_file_command = __commonJS({ if (!filePath) { throw new Error(`Unable to find environment variable for file command ${command}`); } - if (!fs32.existsSync(filePath)) { + if (!fs33.existsSync(filePath)) { throw new Error(`Missing file at path: ${filePath}`); } - fs32.appendFileSync(filePath, `${(0, utils_1.toCommandValue)(message)}${os7.EOL}`, { + fs33.appendFileSync(filePath, `${(0, utils_1.toCommandValue)(message)}${os7.EOL}`, { encoding: "utf8" }); } @@ -20443,13 +20443,13 @@ var require_io_util = __commonJS({ exports2.isRooted = isRooted; exports2.tryGetExecutablePath = tryGetExecutablePath; exports2.getCmdPath = getCmdPath; - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var path30 = __importStar2(require("path")); - _a6 = fs32.promises, exports2.chmod = _a6.chmod, exports2.copyFile = _a6.copyFile, exports2.lstat = _a6.lstat, exports2.mkdir = _a6.mkdir, exports2.open = _a6.open, exports2.readdir = _a6.readdir, exports2.rename = _a6.rename, exports2.rm = _a6.rm, exports2.rmdir = _a6.rmdir, exports2.stat = _a6.stat, exports2.symlink = _a6.symlink, exports2.unlink = _a6.unlink; + _a6 = fs33.promises, exports2.chmod = _a6.chmod, exports2.copyFile = _a6.copyFile, exports2.lstat = _a6.lstat, exports2.mkdir = _a6.mkdir, exports2.open = _a6.open, exports2.readdir = _a6.readdir, exports2.rename = _a6.rename, exports2.rm = _a6.rm, exports2.rmdir = _a6.rmdir, exports2.stat = _a6.stat, exports2.symlink = _a6.symlink, exports2.unlink = _a6.unlink; exports2.IS_WINDOWS = process.platform === "win32"; function readlink(fsPath) { return __awaiter2(this, void 0, void 0, function* () { - const result = yield fs32.promises.readlink(fsPath); + const result = yield fs33.promises.readlink(fsPath); if (exports2.IS_WINDOWS && !result.endsWith("\\")) { return `${result}\\`; } @@ -20457,7 +20457,7 @@ var require_io_util = __commonJS({ }); } exports2.UV_FS_O_EXLOCK = 268435456; - exports2.READONLY = fs32.constants.O_RDONLY; + exports2.READONLY = fs33.constants.O_RDONLY; function exists(fsPath) { return __awaiter2(this, void 0, void 0, function* () { try { @@ -33065,7 +33065,7 @@ var require_internal_globber = __commonJS({ Object.defineProperty(exports2, "__esModule", { value: true }); exports2.DefaultGlobber = void 0; var core29 = __importStar2(require_core()); - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var globOptionsHelper = __importStar2(require_internal_glob_options_helper()); var path30 = __importStar2(require("path")); var patternHelper = __importStar2(require_internal_pattern_helper()); @@ -33119,7 +33119,7 @@ var require_internal_globber = __commonJS({ for (const searchPath of patternHelper.getSearchPaths(patterns)) { core29.debug(`Search path '${searchPath}'`); try { - yield __await2(fs32.promises.lstat(searchPath)); + yield __await2(fs33.promises.lstat(searchPath)); } catch (err) { if (err.code === "ENOENT") { continue; @@ -33153,7 +33153,7 @@ var require_internal_globber = __commonJS({ continue; } const childLevel = item.level + 1; - const childItems = (yield __await2(fs32.promises.readdir(item.path))).map((x) => new internal_search_state_1.SearchState(path30.join(item.path, x), childLevel)); + const childItems = (yield __await2(fs33.promises.readdir(item.path))).map((x) => new internal_search_state_1.SearchState(path30.join(item.path, x), childLevel)); stack.push(...childItems.reverse()); } else if (match2 & internal_match_kind_1.MatchKind.File) { yield yield __await2(item.path); @@ -33188,7 +33188,7 @@ var require_internal_globber = __commonJS({ let stats; if (options.followSymbolicLinks) { try { - stats = yield fs32.promises.stat(item.path); + stats = yield fs33.promises.stat(item.path); } catch (err) { if (err.code === "ENOENT") { if (options.omitBrokenSymbolicLinks) { @@ -33200,10 +33200,10 @@ var require_internal_globber = __commonJS({ throw err; } } else { - stats = yield fs32.promises.lstat(item.path); + stats = yield fs33.promises.lstat(item.path); } if (stats.isDirectory() && options.followSymbolicLinks) { - const realPath = yield fs32.promises.realpath(item.path); + const realPath = yield fs33.promises.realpath(item.path); while (traversalChain.length >= item.level) { traversalChain.pop(); } @@ -33312,7 +33312,7 @@ var require_internal_hash_files = __commonJS({ exports2.hashFiles = hashFiles2; var crypto3 = __importStar2(require("crypto")); var core29 = __importStar2(require_core()); - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var stream2 = __importStar2(require("stream")); var util4 = __importStar2(require("util")); var path30 = __importStar2(require("path")); @@ -33335,13 +33335,13 @@ var require_internal_hash_files = __commonJS({ writeDelegate(`Ignore '${file}' since it is not under GITHUB_WORKSPACE.`); continue; } - if (fs32.statSync(file).isDirectory()) { + if (fs33.statSync(file).isDirectory()) { writeDelegate(`Skip directory '${file}'.`); continue; } const hash2 = crypto3.createHash("sha256"); const pipeline2 = util4.promisify(stream2.pipeline); - yield pipeline2(fs32.createReadStream(file), hash2); + yield pipeline2(fs33.createReadStream(file), hash2); result.write(hash2.digest()); count++; if (!hasMatch) { @@ -34717,7 +34717,7 @@ var require_cacheUtils = __commonJS({ var glob2 = __importStar2(require_glob()); var io9 = __importStar2(require_io()); var crypto3 = __importStar2(require("crypto")); - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var path30 = __importStar2(require("path")); var semver11 = __importStar2(require_semver3()); var util4 = __importStar2(require("util")); @@ -34746,7 +34746,7 @@ var require_cacheUtils = __commonJS({ }); } function getArchiveFileSizeInBytes(filePath) { - return fs32.statSync(filePath).size; + return fs33.statSync(filePath).size; } function resolvePaths(patterns) { return __awaiter2(this, void 0, void 0, function* () { @@ -34784,7 +34784,7 @@ var require_cacheUtils = __commonJS({ } function unlinkFile(filePath) { return __awaiter2(this, void 0, void 0, function* () { - return util4.promisify(fs32.unlink)(filePath); + return util4.promisify(fs33.unlink)(filePath); }); } function getVersion(app_1) { @@ -34826,7 +34826,7 @@ var require_cacheUtils = __commonJS({ } function getGnuTarPathOnWindows() { return __awaiter2(this, void 0, void 0, function* () { - if (fs32.existsSync(constants_1.GnuTarPathOnWindows)) { + if (fs33.existsSync(constants_1.GnuTarPathOnWindows)) { return constants_1.GnuTarPathOnWindows; } const versionOutput = yield getVersion("tar"); @@ -99550,7 +99550,7 @@ var require_downloadUtils = __commonJS({ var http_client_1 = require_lib(); var storage_blob_1 = require_commonjs15(); var buffer = __importStar2(require("buffer")); - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var stream2 = __importStar2(require("stream")); var util4 = __importStar2(require("util")); var utils = __importStar2(require_cacheUtils()); @@ -99661,7 +99661,7 @@ var require_downloadUtils = __commonJS({ exports2.DownloadProgress = DownloadProgress; function downloadCacheHttpClient(archiveLocation, archivePath) { return __awaiter2(this, void 0, void 0, function* () { - const writeStream = fs32.createWriteStream(archivePath); + const writeStream = fs33.createWriteStream(archivePath); const httpClient = new http_client_1.HttpClient("actions/cache"); const downloadResponse = yield (0, requestUtils_1.retryHttpClientResponse)("downloadCache", () => __awaiter2(this, void 0, void 0, function* () { return httpClient.get(archiveLocation); @@ -99686,7 +99686,7 @@ var require_downloadUtils = __commonJS({ function downloadCacheHttpClientConcurrent(archiveLocation, archivePath, options) { return __awaiter2(this, void 0, void 0, function* () { var _a6; - const archiveDescriptor = yield fs32.promises.open(archivePath, "w"); + const archiveDescriptor = yield fs33.promises.open(archivePath, "w"); const httpClient = new http_client_1.HttpClient("actions/cache", void 0, { socketTimeout: options.timeoutInMs, keepAlive: true @@ -99802,7 +99802,7 @@ var require_downloadUtils = __commonJS({ } else { const maxSegmentSize = Math.min(134217728, buffer.constants.MAX_LENGTH); const downloadProgress = new DownloadProgress(contentLength); - const fd = fs32.openSync(archivePath, "w"); + const fd = fs33.openSync(archivePath, "w"); try { downloadProgress.startDisplayTimer(); const controller = new abort_controller_1.AbortController(); @@ -99820,12 +99820,12 @@ var require_downloadUtils = __commonJS({ controller.abort(); throw new Error("Aborting cache download as the download time exceeded the timeout."); } else if (Buffer.isBuffer(result)) { - fs32.writeFileSync(fd, result); + fs33.writeFileSync(fd, result); } } } finally { downloadProgress.stopDisplayTimer(); - fs32.closeSync(fd); + fs33.closeSync(fd); } } }); @@ -100164,7 +100164,7 @@ var require_cacheHttpClient = __commonJS({ var core29 = __importStar2(require_core()); var http_client_1 = require_lib(); var auth_1 = require_auth(); - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var url_1 = require("url"); var utils = __importStar2(require_cacheUtils()); var uploadUtils_1 = require_uploadUtils(); @@ -100305,7 +100305,7 @@ Other caches with similar key:`); return __awaiter2(this, void 0, void 0, function* () { const fileSize = utils.getArchiveFileSizeInBytes(archivePath); const resourceUrl = getCacheApiUrl(`caches/${cacheId.toString()}`); - const fd = fs32.openSync(archivePath, "r"); + const fd = fs33.openSync(archivePath, "r"); const uploadOptions = (0, options_1.getUploadOptions)(options); const concurrency = utils.assertDefined("uploadConcurrency", uploadOptions.uploadConcurrency); const maxChunkSize = utils.assertDefined("uploadChunkSize", uploadOptions.uploadChunkSize); @@ -100319,7 +100319,7 @@ Other caches with similar key:`); const start = offset; const end = offset + chunkSize - 1; offset += maxChunkSize; - yield uploadChunk(httpClient, resourceUrl, () => fs32.createReadStream(archivePath, { + yield uploadChunk(httpClient, resourceUrl, () => fs33.createReadStream(archivePath, { fd, start, end, @@ -100330,7 +100330,7 @@ Other caches with similar key:`); } }))); } finally { - fs32.closeSync(fd); + fs33.closeSync(fd); } return; }); @@ -106350,7 +106350,7 @@ var require_manifest = __commonJS({ var core_1 = require_core(); var os7 = require("os"); var cp = require("child_process"); - var fs32 = require("fs"); + var fs33 = require("fs"); function _findMatch(versionSpec, stable, candidates, archFilter) { return __awaiter2(this, void 0, void 0, function* () { const platFilter = os7.platform(); @@ -106412,10 +106412,10 @@ var require_manifest = __commonJS({ const lsbReleaseFile = "/etc/lsb-release"; const osReleaseFile = "/etc/os-release"; let contents = ""; - if (fs32.existsSync(lsbReleaseFile)) { - contents = fs32.readFileSync(lsbReleaseFile).toString(); - } else if (fs32.existsSync(osReleaseFile)) { - contents = fs32.readFileSync(osReleaseFile).toString(); + if (fs33.existsSync(lsbReleaseFile)) { + contents = fs33.readFileSync(lsbReleaseFile).toString(); + } else if (fs33.existsSync(osReleaseFile)) { + contents = fs33.readFileSync(osReleaseFile).toString(); } return contents; } @@ -106624,7 +106624,7 @@ var require_tool_cache = __commonJS({ var core29 = __importStar2(require_core()); var io9 = __importStar2(require_io()); var crypto3 = __importStar2(require("crypto")); - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var mm = __importStar2(require_manifest()); var os7 = __importStar2(require("os")); var path30 = __importStar2(require("path")); @@ -106670,7 +106670,7 @@ var require_tool_cache = __commonJS({ } function downloadToolAttempt(url2, dest, auth2, headers) { return __awaiter2(this, void 0, void 0, function* () { - if (fs32.existsSync(dest)) { + if (fs33.existsSync(dest)) { throw new Error(`Destination file path ${dest} already exists`); } const http = new httpm.HttpClient(userAgent2, [], { @@ -106694,7 +106694,7 @@ var require_tool_cache = __commonJS({ const readStream = responseMessageFactory(); let succeeded = false; try { - yield pipeline2(readStream, fs32.createWriteStream(dest)); + yield pipeline2(readStream, fs33.createWriteStream(dest)); core29.debug("download complete"); succeeded = true; return dest; @@ -106906,11 +106906,11 @@ var require_tool_cache = __commonJS({ arch2 = arch2 || os7.arch(); core29.debug(`Caching tool ${tool} ${version} ${arch2}`); core29.debug(`source dir: ${sourceDir}`); - if (!fs32.statSync(sourceDir).isDirectory()) { + if (!fs33.statSync(sourceDir).isDirectory()) { throw new Error("sourceDir is not a directory"); } const destPath = yield _createToolPath(tool, version, arch2); - for (const itemName of fs32.readdirSync(sourceDir)) { + for (const itemName of fs33.readdirSync(sourceDir)) { const s = path30.join(sourceDir, itemName); yield io9.cp(s, destPath, { recursive: true }); } @@ -106924,7 +106924,7 @@ var require_tool_cache = __commonJS({ arch2 = arch2 || os7.arch(); core29.debug(`Caching tool ${tool} ${version} ${arch2}`); core29.debug(`source file: ${sourceFile}`); - if (!fs32.statSync(sourceFile).isFile()) { + if (!fs33.statSync(sourceFile).isFile()) { throw new Error("sourceFile is not a file"); } const destFolder = yield _createToolPath(tool, version, arch2); @@ -106953,7 +106953,7 @@ var require_tool_cache = __commonJS({ versionSpec = semver11.clean(versionSpec) || ""; const cachePath = path30.join(_getCacheDirectory(), toolName, versionSpec, arch2); core29.debug(`checking cache: ${cachePath}`); - if (fs32.existsSync(cachePath) && fs32.existsSync(`${cachePath}.complete`)) { + if (fs33.existsSync(cachePath) && fs33.existsSync(`${cachePath}.complete`)) { core29.debug(`Found tool in cache ${toolName} ${versionSpec} ${arch2}`); toolPath = cachePath; } else { @@ -106966,12 +106966,12 @@ var require_tool_cache = __commonJS({ const versions = []; arch2 = arch2 || os7.arch(); const toolPath = path30.join(_getCacheDirectory(), toolName); - if (fs32.existsSync(toolPath)) { - const children = fs32.readdirSync(toolPath); + if (fs33.existsSync(toolPath)) { + const children = fs33.readdirSync(toolPath); for (const child of children) { if (isExplicitVersion(child)) { const fullPath = path30.join(toolPath, child, arch2 || ""); - if (fs32.existsSync(fullPath) && fs32.existsSync(`${fullPath}.complete`)) { + if (fs33.existsSync(fullPath) && fs33.existsSync(`${fullPath}.complete`)) { versions.push(child); } } @@ -107042,7 +107042,7 @@ var require_tool_cache = __commonJS({ function _completeToolPath(tool, version, arch2) { const folderPath = path30.join(_getCacheDirectory(), tool, semver11.clean(version) || version, arch2 || ""); const markerPath = `${folderPath}.complete`; - fs32.writeFileSync(markerPath, ""); + fs33.writeFileSync(markerPath, ""); core29.debug("finished caching tool"); } function isExplicitVersion(versionSpec) { @@ -113284,15 +113284,15 @@ var require_upload_zip_specification = __commonJS({ }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.getUploadZipSpecification = exports2.validateRootDirectory = void 0; - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var core_1 = require_core(); var path_1 = require("path"); var path_and_artifact_name_validation_1 = require_path_and_artifact_name_validation(); function validateRootDirectory(rootDirectory) { - if (!fs32.existsSync(rootDirectory)) { + if (!fs33.existsSync(rootDirectory)) { throw new Error(`The provided rootDirectory ${rootDirectory} does not exist`); } - if (!fs32.statSync(rootDirectory).isDirectory()) { + if (!fs33.statSync(rootDirectory).isDirectory()) { throw new Error(`The provided rootDirectory ${rootDirectory} is not a valid directory`); } (0, core_1.info)(`Root directory input is valid!`); @@ -113303,7 +113303,7 @@ var require_upload_zip_specification = __commonJS({ rootDirectory = (0, path_1.normalize)(rootDirectory); rootDirectory = (0, path_1.resolve)(rootDirectory); for (let file of filesToZip) { - const stats = fs32.lstatSync(file, { throwIfNoEntry: false }); + const stats = fs33.lstatSync(file, { throwIfNoEntry: false }); if (!stats) { throw new Error(`File ${file} does not exist`); } @@ -114378,13 +114378,13 @@ var require_minimatch2 = __commonJS({ var require_readdir_glob = __commonJS({ "node_modules/@actions/artifact/node_modules/readdir-glob/index.js"(exports2, module2) { module2.exports = readdirGlob2; - var fs32 = require("fs"); + var fs33 = require("fs"); var { EventEmitter: EventEmitter2 } = require("events"); var { Minimatch: Minimatch2 } = require_minimatch2(); var { resolve: resolve14 } = require("path"); function readdir3(dir, strict) { return new Promise((resolve15, reject) => { - fs32.readdir(dir, { withFileTypes: true }, (err, files) => { + fs33.readdir(dir, { withFileTypes: true }, (err, files) => { if (err) { switch (err.code) { case "ENOTDIR": @@ -114417,7 +114417,7 @@ var require_readdir_glob = __commonJS({ } function stat2(file, followSymlinks) { return new Promise((resolve15, reject) => { - const statFunc = followSymlinks ? fs32.stat : fs32.lstat; + const statFunc = followSymlinks ? fs33.stat : fs33.lstat; statFunc(file, (err, stats) => { if (err) { switch (err.code) { @@ -116482,54 +116482,54 @@ var require_polyfills = __commonJS({ } var chdir; module2.exports = patch; - function patch(fs32) { + function patch(fs33) { if (constants.hasOwnProperty("O_SYMLINK") && process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) { - patchLchmod(fs32); - } - if (!fs32.lutimes) { - patchLutimes(fs32); - } - fs32.chown = chownFix(fs32.chown); - fs32.fchown = chownFix(fs32.fchown); - fs32.lchown = chownFix(fs32.lchown); - fs32.chmod = chmodFix(fs32.chmod); - fs32.fchmod = chmodFix(fs32.fchmod); - fs32.lchmod = chmodFix(fs32.lchmod); - fs32.chownSync = chownFixSync(fs32.chownSync); - fs32.fchownSync = chownFixSync(fs32.fchownSync); - fs32.lchownSync = chownFixSync(fs32.lchownSync); - fs32.chmodSync = chmodFixSync(fs32.chmodSync); - fs32.fchmodSync = chmodFixSync(fs32.fchmodSync); - fs32.lchmodSync = chmodFixSync(fs32.lchmodSync); - fs32.stat = statFix(fs32.stat); - fs32.fstat = statFix(fs32.fstat); - fs32.lstat = statFix(fs32.lstat); - fs32.statSync = statFixSync(fs32.statSync); - fs32.fstatSync = statFixSync(fs32.fstatSync); - fs32.lstatSync = statFixSync(fs32.lstatSync); - if (fs32.chmod && !fs32.lchmod) { - fs32.lchmod = function(path30, mode, cb) { + patchLchmod(fs33); + } + if (!fs33.lutimes) { + patchLutimes(fs33); + } + fs33.chown = chownFix(fs33.chown); + fs33.fchown = chownFix(fs33.fchown); + fs33.lchown = chownFix(fs33.lchown); + fs33.chmod = chmodFix(fs33.chmod); + fs33.fchmod = chmodFix(fs33.fchmod); + fs33.lchmod = chmodFix(fs33.lchmod); + fs33.chownSync = chownFixSync(fs33.chownSync); + fs33.fchownSync = chownFixSync(fs33.fchownSync); + fs33.lchownSync = chownFixSync(fs33.lchownSync); + fs33.chmodSync = chmodFixSync(fs33.chmodSync); + fs33.fchmodSync = chmodFixSync(fs33.fchmodSync); + fs33.lchmodSync = chmodFixSync(fs33.lchmodSync); + fs33.stat = statFix(fs33.stat); + fs33.fstat = statFix(fs33.fstat); + fs33.lstat = statFix(fs33.lstat); + fs33.statSync = statFixSync(fs33.statSync); + fs33.fstatSync = statFixSync(fs33.fstatSync); + fs33.lstatSync = statFixSync(fs33.lstatSync); + if (fs33.chmod && !fs33.lchmod) { + fs33.lchmod = function(path30, mode, cb) { if (cb) process.nextTick(cb); }; - fs32.lchmodSync = function() { + fs33.lchmodSync = function() { }; } - if (fs32.chown && !fs32.lchown) { - fs32.lchown = function(path30, uid, gid, cb) { + if (fs33.chown && !fs33.lchown) { + fs33.lchown = function(path30, uid, gid, cb) { if (cb) process.nextTick(cb); }; - fs32.lchownSync = function() { + fs33.lchownSync = function() { }; } if (platform2 === "win32") { - fs32.rename = typeof fs32.rename !== "function" ? fs32.rename : (function(fs$rename) { + fs33.rename = typeof fs33.rename !== "function" ? fs33.rename : (function(fs$rename) { function rename(from, to, cb) { var start = Date.now(); var backoff = 0; fs$rename(from, to, function CB(er) { if (er && (er.code === "EACCES" || er.code === "EPERM") && Date.now() - start < 6e4) { setTimeout(function() { - fs32.stat(to, function(stater, st) { + fs33.stat(to, function(stater, st) { if (stater && stater.code === "ENOENT") fs$rename(from, to, CB); else @@ -116545,9 +116545,9 @@ var require_polyfills = __commonJS({ } if (Object.setPrototypeOf) Object.setPrototypeOf(rename, fs$rename); return rename; - })(fs32.rename); + })(fs33.rename); } - fs32.read = typeof fs32.read !== "function" ? fs32.read : (function(fs$read) { + fs33.read = typeof fs33.read !== "function" ? fs33.read : (function(fs$read) { function read(fd, buffer, offset, length, position, callback_) { var callback; if (callback_ && typeof callback_ === "function") { @@ -116555,22 +116555,22 @@ var require_polyfills = __commonJS({ callback = function(er, _2, __) { if (er && er.code === "EAGAIN" && eagCounter < 10) { eagCounter++; - return fs$read.call(fs32, fd, buffer, offset, length, position, callback); + return fs$read.call(fs33, fd, buffer, offset, length, position, callback); } callback_.apply(this, arguments); }; } - return fs$read.call(fs32, fd, buffer, offset, length, position, callback); + return fs$read.call(fs33, fd, buffer, offset, length, position, callback); } if (Object.setPrototypeOf) Object.setPrototypeOf(read, fs$read); return read; - })(fs32.read); - fs32.readSync = typeof fs32.readSync !== "function" ? fs32.readSync : /* @__PURE__ */ (function(fs$readSync) { + })(fs33.read); + fs33.readSync = typeof fs33.readSync !== "function" ? fs33.readSync : /* @__PURE__ */ (function(fs$readSync) { return function(fd, buffer, offset, length, position) { var eagCounter = 0; while (true) { try { - return fs$readSync.call(fs32, fd, buffer, offset, length, position); + return fs$readSync.call(fs33, fd, buffer, offset, length, position); } catch (er) { if (er.code === "EAGAIN" && eagCounter < 10) { eagCounter++; @@ -116580,10 +116580,10 @@ var require_polyfills = __commonJS({ } } }; - })(fs32.readSync); - function patchLchmod(fs33) { - fs33.lchmod = function(path30, mode, callback) { - fs33.open( + })(fs33.readSync); + function patchLchmod(fs34) { + fs34.lchmod = function(path30, mode, callback) { + fs34.open( path30, constants.O_WRONLY | constants.O_SYMLINK, mode, @@ -116592,80 +116592,80 @@ var require_polyfills = __commonJS({ if (callback) callback(err); return; } - fs33.fchmod(fd, mode, function(err2) { - fs33.close(fd, function(err22) { + fs34.fchmod(fd, mode, function(err2) { + fs34.close(fd, function(err22) { if (callback) callback(err2 || err22); }); }); } ); }; - fs33.lchmodSync = function(path30, mode) { - var fd = fs33.openSync(path30, constants.O_WRONLY | constants.O_SYMLINK, mode); + fs34.lchmodSync = function(path30, mode) { + var fd = fs34.openSync(path30, constants.O_WRONLY | constants.O_SYMLINK, mode); var threw = true; var ret; try { - ret = fs33.fchmodSync(fd, mode); + ret = fs34.fchmodSync(fd, mode); threw = false; } finally { if (threw) { try { - fs33.closeSync(fd); + fs34.closeSync(fd); } catch (er) { } } else { - fs33.closeSync(fd); + fs34.closeSync(fd); } } return ret; }; } - function patchLutimes(fs33) { - if (constants.hasOwnProperty("O_SYMLINK") && fs33.futimes) { - fs33.lutimes = function(path30, at, mt, cb) { - fs33.open(path30, constants.O_SYMLINK, function(er, fd) { + function patchLutimes(fs34) { + if (constants.hasOwnProperty("O_SYMLINK") && fs34.futimes) { + fs34.lutimes = function(path30, at, mt, cb) { + fs34.open(path30, constants.O_SYMLINK, function(er, fd) { if (er) { if (cb) cb(er); return; } - fs33.futimes(fd, at, mt, function(er2) { - fs33.close(fd, function(er22) { + fs34.futimes(fd, at, mt, function(er2) { + fs34.close(fd, function(er22) { if (cb) cb(er2 || er22); }); }); }); }; - fs33.lutimesSync = function(path30, at, mt) { - var fd = fs33.openSync(path30, constants.O_SYMLINK); + fs34.lutimesSync = function(path30, at, mt) { + var fd = fs34.openSync(path30, constants.O_SYMLINK); var ret; var threw = true; try { - ret = fs33.futimesSync(fd, at, mt); + ret = fs34.futimesSync(fd, at, mt); threw = false; } finally { if (threw) { try { - fs33.closeSync(fd); + fs34.closeSync(fd); } catch (er) { } } else { - fs33.closeSync(fd); + fs34.closeSync(fd); } } return ret; }; - } else if (fs33.futimes) { - fs33.lutimes = function(_a6, _b2, _c2, cb) { + } else if (fs34.futimes) { + fs34.lutimes = function(_a6, _b2, _c2, cb) { if (cb) process.nextTick(cb); }; - fs33.lutimesSync = function() { + fs34.lutimesSync = function() { }; } } function chmodFix(orig) { if (!orig) return orig; return function(target, mode, cb) { - return orig.call(fs32, target, mode, function(er) { + return orig.call(fs33, target, mode, function(er) { if (chownErOk(er)) er = null; if (cb) cb.apply(this, arguments); }); @@ -116675,7 +116675,7 @@ var require_polyfills = __commonJS({ if (!orig) return orig; return function(target, mode) { try { - return orig.call(fs32, target, mode); + return orig.call(fs33, target, mode); } catch (er) { if (!chownErOk(er)) throw er; } @@ -116684,7 +116684,7 @@ var require_polyfills = __commonJS({ function chownFix(orig) { if (!orig) return orig; return function(target, uid, gid, cb) { - return orig.call(fs32, target, uid, gid, function(er) { + return orig.call(fs33, target, uid, gid, function(er) { if (chownErOk(er)) er = null; if (cb) cb.apply(this, arguments); }); @@ -116694,7 +116694,7 @@ var require_polyfills = __commonJS({ if (!orig) return orig; return function(target, uid, gid) { try { - return orig.call(fs32, target, uid, gid); + return orig.call(fs33, target, uid, gid); } catch (er) { if (!chownErOk(er)) throw er; } @@ -116714,13 +116714,13 @@ var require_polyfills = __commonJS({ } if (cb) cb.apply(this, arguments); } - return options ? orig.call(fs32, target, options, callback) : orig.call(fs32, target, callback); + return options ? orig.call(fs33, target, options, callback) : orig.call(fs33, target, callback); }; } function statFixSync(orig) { if (!orig) return orig; return function(target, options) { - var stats = options ? orig.call(fs32, target, options) : orig.call(fs32, target); + var stats = options ? orig.call(fs33, target, options) : orig.call(fs33, target); if (stats) { if (stats.uid < 0) stats.uid += 4294967296; if (stats.gid < 0) stats.gid += 4294967296; @@ -116749,7 +116749,7 @@ var require_legacy_streams = __commonJS({ "node_modules/graceful-fs/legacy-streams.js"(exports2, module2) { var Stream = require("stream").Stream; module2.exports = legacy; - function legacy(fs32) { + function legacy(fs33) { return { ReadStream, WriteStream @@ -116792,7 +116792,7 @@ var require_legacy_streams = __commonJS({ }); return; } - fs32.open(this.path, this.flags, this.mode, function(err, fd) { + fs33.open(this.path, this.flags, this.mode, function(err, fd) { if (err) { self2.emit("error", err); self2.readable = false; @@ -116831,7 +116831,7 @@ var require_legacy_streams = __commonJS({ this.busy = false; this._queue = []; if (this.fd === null) { - this._open = fs32.open; + this._open = fs33.open; this._queue.push([this._open, this.path, this.flags, this.mode, void 0]); this.flush(); } @@ -116866,7 +116866,7 @@ var require_clone = __commonJS({ // node_modules/graceful-fs/graceful-fs.js var require_graceful_fs = __commonJS({ "node_modules/graceful-fs/graceful-fs.js"(exports2, module2) { - var fs32 = require("fs"); + var fs33 = require("fs"); var polyfills = require_polyfills(); var legacy = require_legacy_streams(); var clone = require_clone(); @@ -116898,12 +116898,12 @@ var require_graceful_fs = __commonJS({ m = "GFS4: " + m.split(/\n/).join("\nGFS4: "); console.error(m); }; - if (!fs32[gracefulQueue]) { + if (!fs33[gracefulQueue]) { queue2 = global[gracefulQueue] || []; - publishQueue(fs32, queue2); - fs32.close = (function(fs$close) { + publishQueue(fs33, queue2); + fs33.close = (function(fs$close) { function close(fd, cb) { - return fs$close.call(fs32, fd, function(err) { + return fs$close.call(fs33, fd, function(err) { if (!err) { resetQueue(); } @@ -116915,40 +116915,40 @@ var require_graceful_fs = __commonJS({ value: fs$close }); return close; - })(fs32.close); - fs32.closeSync = (function(fs$closeSync) { + })(fs33.close); + fs33.closeSync = (function(fs$closeSync) { function closeSync(fd) { - fs$closeSync.apply(fs32, arguments); + fs$closeSync.apply(fs33, arguments); resetQueue(); } Object.defineProperty(closeSync, previousSymbol, { value: fs$closeSync }); return closeSync; - })(fs32.closeSync); + })(fs33.closeSync); if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || "")) { process.on("exit", function() { - debug6(fs32[gracefulQueue]); - require("assert").equal(fs32[gracefulQueue].length, 0); + debug6(fs33[gracefulQueue]); + require("assert").equal(fs33[gracefulQueue].length, 0); }); } } var queue2; if (!global[gracefulQueue]) { - publishQueue(global, fs32[gracefulQueue]); - } - module2.exports = patch(clone(fs32)); - if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !fs32.__patched) { - module2.exports = patch(fs32); - fs32.__patched = true; - } - function patch(fs33) { - polyfills(fs33); - fs33.gracefulify = patch; - fs33.createReadStream = createReadStream4; - fs33.createWriteStream = createWriteStream3; - var fs$readFile = fs33.readFile; - fs33.readFile = readFile; + publishQueue(global, fs33[gracefulQueue]); + } + module2.exports = patch(clone(fs33)); + if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !fs33.__patched) { + module2.exports = patch(fs33); + fs33.__patched = true; + } + function patch(fs34) { + polyfills(fs34); + fs34.gracefulify = patch; + fs34.createReadStream = createReadStream4; + fs34.createWriteStream = createWriteStream3; + var fs$readFile = fs34.readFile; + fs34.readFile = readFile; function readFile(path30, options, cb) { if (typeof options === "function") cb = options, options = null; @@ -116964,8 +116964,8 @@ var require_graceful_fs = __commonJS({ }); } } - var fs$writeFile = fs33.writeFile; - fs33.writeFile = writeFile; + var fs$writeFile = fs34.writeFile; + fs34.writeFile = writeFile; function writeFile(path30, data, options, cb) { if (typeof options === "function") cb = options, options = null; @@ -116981,9 +116981,9 @@ var require_graceful_fs = __commonJS({ }); } } - var fs$appendFile = fs33.appendFile; + var fs$appendFile = fs34.appendFile; if (fs$appendFile) - fs33.appendFile = appendFile; + fs34.appendFile = appendFile; function appendFile(path30, data, options, cb) { if (typeof options === "function") cb = options, options = null; @@ -116999,9 +116999,9 @@ var require_graceful_fs = __commonJS({ }); } } - var fs$copyFile = fs33.copyFile; + var fs$copyFile = fs34.copyFile; if (fs$copyFile) - fs33.copyFile = copyFile2; + fs34.copyFile = copyFile2; function copyFile2(src, dest, flags, cb) { if (typeof flags === "function") { cb = flags; @@ -117019,8 +117019,8 @@ var require_graceful_fs = __commonJS({ }); } } - var fs$readdir = fs33.readdir; - fs33.readdir = readdir3; + var fs$readdir = fs34.readdir; + fs34.readdir = readdir3; var noReaddirOptionVersions = /^v[0-5]\./; function readdir3(path30, options, cb) { if (typeof options === "function") @@ -117061,21 +117061,21 @@ var require_graceful_fs = __commonJS({ } } if (process.version.substr(0, 4) === "v0.8") { - var legStreams = legacy(fs33); + var legStreams = legacy(fs34); ReadStream = legStreams.ReadStream; WriteStream = legStreams.WriteStream; } - var fs$ReadStream = fs33.ReadStream; + var fs$ReadStream = fs34.ReadStream; if (fs$ReadStream) { ReadStream.prototype = Object.create(fs$ReadStream.prototype); ReadStream.prototype.open = ReadStream$open; } - var fs$WriteStream = fs33.WriteStream; + var fs$WriteStream = fs34.WriteStream; if (fs$WriteStream) { WriteStream.prototype = Object.create(fs$WriteStream.prototype); WriteStream.prototype.open = WriteStream$open; } - Object.defineProperty(fs33, "ReadStream", { + Object.defineProperty(fs34, "ReadStream", { get: function() { return ReadStream; }, @@ -117085,7 +117085,7 @@ var require_graceful_fs = __commonJS({ enumerable: true, configurable: true }); - Object.defineProperty(fs33, "WriteStream", { + Object.defineProperty(fs34, "WriteStream", { get: function() { return WriteStream; }, @@ -117096,7 +117096,7 @@ var require_graceful_fs = __commonJS({ configurable: true }); var FileReadStream = ReadStream; - Object.defineProperty(fs33, "FileReadStream", { + Object.defineProperty(fs34, "FileReadStream", { get: function() { return FileReadStream; }, @@ -117107,7 +117107,7 @@ var require_graceful_fs = __commonJS({ configurable: true }); var FileWriteStream = WriteStream; - Object.defineProperty(fs33, "FileWriteStream", { + Object.defineProperty(fs34, "FileWriteStream", { get: function() { return FileWriteStream; }, @@ -117156,13 +117156,13 @@ var require_graceful_fs = __commonJS({ }); } function createReadStream4(path30, options) { - return new fs33.ReadStream(path30, options); + return new fs34.ReadStream(path30, options); } function createWriteStream3(path30, options) { - return new fs33.WriteStream(path30, options); + return new fs34.WriteStream(path30, options); } - var fs$open = fs33.open; - fs33.open = open; + var fs$open = fs34.open; + fs34.open = open; function open(path30, flags, mode, cb) { if (typeof mode === "function") cb = mode, mode = null; @@ -117178,20 +117178,20 @@ var require_graceful_fs = __commonJS({ }); } } - return fs33; + return fs34; } function enqueue(elem) { debug6("ENQUEUE", elem[0].name, elem[1]); - fs32[gracefulQueue].push(elem); + fs33[gracefulQueue].push(elem); retry2(); } var retryTimer; function resetQueue() { var now = Date.now(); - for (var i = 0; i < fs32[gracefulQueue].length; ++i) { - if (fs32[gracefulQueue][i].length > 2) { - fs32[gracefulQueue][i][3] = now; - fs32[gracefulQueue][i][4] = now; + for (var i = 0; i < fs33[gracefulQueue].length; ++i) { + if (fs33[gracefulQueue][i].length > 2) { + fs33[gracefulQueue][i][3] = now; + fs33[gracefulQueue][i][4] = now; } } retry2(); @@ -117199,9 +117199,9 @@ var require_graceful_fs = __commonJS({ function retry2() { clearTimeout(retryTimer); retryTimer = void 0; - if (fs32[gracefulQueue].length === 0) + if (fs33[gracefulQueue].length === 0) return; - var elem = fs32[gracefulQueue].shift(); + var elem = fs33[gracefulQueue].shift(); var fn = elem[0]; var args = elem[1]; var err = elem[2]; @@ -117223,7 +117223,7 @@ var require_graceful_fs = __commonJS({ debug6("RETRY", fn.name, args); fn.apply(null, args.concat([startTime])); } else { - fs32[gracefulQueue].push(elem); + fs33[gracefulQueue].push(elem); } } if (retryTimer === void 0) { @@ -128046,7 +128046,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s)) retur g.minimatch.escape = vi.escape; g.minimatch.unescape = Ei.unescape; }); - var fs32 = R((Wt) => { + var fs33 = R((Wt) => { "use strict"; Object.defineProperty(Wt, "__esModule", { value: true }); Wt.LRUCache = void 0; @@ -128915,7 +128915,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s)) retur }; Object.defineProperty(_2, "__esModule", { value: true }); _2.PathScurry = _2.Path = _2.PathScurryDarwin = _2.PathScurryPosix = _2.PathScurryWin32 = _2.PathScurryBase = _2.PathPosix = _2.PathWin32 = _2.PathBase = _2.ChildrenCache = _2.ResolveCache = void 0; - var Qt = fs32(), Yt = require("node:path"), yr = require("node:url"), pt = require("fs"), Sr = br(require("node:fs")), vr = pt.realpathSync.native, Ht = require("node:fs/promises"), bs = Oe(), mt = { lstatSync: pt.lstatSync, readdir: pt.readdir, readdirSync: pt.readdirSync, readlinkSync: pt.readlinkSync, realpathSync: vr, promises: { lstat: Ht.lstat, readdir: Ht.readdir, readlink: Ht.readlink, realpath: Ht.realpath } }, _s2 = (n) => !n || n === mt || n === Sr ? mt : { ...mt, ...n, promises: { ...mt.promises, ...n.promises || {} } }, Os = /^\\\\\?\\([a-z]:)\\?$/i, Er = (n) => n.replace(/\//g, "\\").replace(Os, "$1\\"), _r2 = /[\\\/]/, N = 0, xs = 1, Ts = 2, G = 4, Cs = 6, Rs = 8, Q = 10, As = 12, j = 15, dt = ~j, xe = 16, ys = 32, gt = 64, W = 128, Vt = 256, Xt = 512, Ss = gt | W | Xt, Or = 1023, Te = (n) => n.isFile() ? Rs : n.isDirectory() ? G : n.isSymbolicLink() ? Q : n.isCharacterDevice() ? Ts : n.isBlockDevice() ? Cs : n.isSocket() ? As : n.isFIFO() ? xs : N, vs = new Qt.LRUCache({ max: 2 ** 12 }), wt = (n) => { + var Qt = fs33(), Yt = require("node:path"), yr = require("node:url"), pt = require("fs"), Sr = br(require("node:fs")), vr = pt.realpathSync.native, Ht = require("node:fs/promises"), bs = Oe(), mt = { lstatSync: pt.lstatSync, readdir: pt.readdir, readdirSync: pt.readdirSync, readlinkSync: pt.readlinkSync, realpathSync: vr, promises: { lstat: Ht.lstat, readdir: Ht.readdir, readlink: Ht.readlink, realpath: Ht.realpath } }, _s2 = (n) => !n || n === mt || n === Sr ? mt : { ...mt, ...n, promises: { ...mt.promises, ...n.promises || {} } }, Os = /^\\\\\?\\([a-z]:)\\?$/i, Er = (n) => n.replace(/\//g, "\\").replace(Os, "$1\\"), _r2 = /[\\\/]/, N = 0, xs = 1, Ts = 2, G = 4, Cs = 6, Rs = 8, Q = 10, As = 12, j = 15, dt = ~j, xe = 16, ys = 32, gt = 64, W = 128, Vt = 256, Xt = 512, Ss = gt | W | Xt, Or = 1023, Te = (n) => n.isFile() ? Rs : n.isDirectory() ? G : n.isSymbolicLink() ? Q : n.isCharacterDevice() ? Ts : n.isBlockDevice() ? Cs : n.isSocket() ? As : n.isFIFO() ? xs : N, vs = new Qt.LRUCache({ max: 2 ** 12 }), wt = (n) => { let t = vs.get(n); if (t) return t; let e = n.normalize("NFKD"); @@ -130293,7 +130293,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s)) retur // node_modules/archiver-utils/file.js var require_file3 = __commonJS({ "node_modules/archiver-utils/file.js"(exports2, module2) { - var fs32 = require_graceful_fs(); + var fs33 = require_graceful_fs(); var path30 = require("path"); var flatten = require_flatten(); var difference = require_difference(); @@ -130320,7 +130320,7 @@ var require_file3 = __commonJS({ }; file.exists = function() { var filepath = path30.join.apply(path30, arguments); - return fs32.existsSync(filepath); + return fs33.existsSync(filepath); }; file.expand = function(...args) { var options = isPlainObject4(args[0]) ? args.shift() : {}; @@ -130338,7 +130338,7 @@ var require_file3 = __commonJS({ if (typeof options.filter === "function") { return options.filter(filepath); } else { - return fs32.statSync(filepath)[options.filter](); + return fs33.statSync(filepath)[options.filter](); } } catch (e) { return false; @@ -130446,7 +130446,7 @@ var require_file3 = __commonJS({ // node_modules/archiver-utils/index.js var require_archiver_utils = __commonJS({ "node_modules/archiver-utils/index.js"(exports2, module2) { - var fs32 = require_graceful_fs(); + var fs33 = require_graceful_fs(); var path30 = require("path"); var isStream2 = require_is_stream(); var lazystream = require_lazystream(); @@ -130495,7 +130495,7 @@ var require_archiver_utils = __commonJS({ }; utils.lazyReadStream = function(filepath) { return new lazystream.Readable(function() { - return fs32.createReadStream(filepath); + return fs33.createReadStream(filepath); }); }; utils.normalizeInputSource = function(source) { @@ -130523,7 +130523,7 @@ var require_archiver_utils = __commonJS({ callback = base; base = dirpath; } - fs32.readdir(dirpath, function(err, list) { + fs33.readdir(dirpath, function(err, list) { var i = 0; var file; var filepath; @@ -130536,7 +130536,7 @@ var require_archiver_utils = __commonJS({ return callback(null, results); } filepath = path30.join(dirpath, file); - fs32.stat(filepath, function(err2, stats) { + fs33.stat(filepath, function(err2, stats) { results.push({ path: filepath, relative: path30.relative(base, filepath).replace(/\\/g, "/"), @@ -130598,7 +130598,7 @@ var require_error3 = __commonJS({ // node_modules/@actions/artifact/node_modules/archiver/lib/core.js var require_core2 = __commonJS({ "node_modules/@actions/artifact/node_modules/archiver/lib/core.js"(exports2, module2) { - var fs32 = require("fs"); + var fs33 = require("fs"); var glob2 = require_readdir_glob(); var async = require_async(); var path30 = require("path"); @@ -130662,7 +130662,7 @@ var require_core2 = __commonJS({ data.sourcePath = filepath; task.data = data; this._entriesCount++; - if (data.stats && data.stats instanceof fs32.Stats) { + if (data.stats && data.stats instanceof fs33.Stats) { task = this._updateQueueTaskWithStats(task, data.stats); if (task) { if (data.stats.size) { @@ -130833,7 +130833,7 @@ var require_core2 = __commonJS({ callback(); return; } - fs32.lstat(task.filepath, function(err, stats) { + fs33.lstat(task.filepath, function(err, stats) { if (this._state.aborted) { setImmediate(callback); return; @@ -130876,7 +130876,7 @@ var require_core2 = __commonJS({ task.data.sourceType = "buffer"; task.source = Buffer.concat([]); } else if (stats.isSymbolicLink() && this._moduleSupports("symlink")) { - var linkPath = fs32.readlinkSync(task.filepath); + var linkPath = fs33.readlinkSync(task.filepath); var dirName = path30.dirname(task.filepath); task.data.type = "symlink"; task.data.linkname = path30.relative(dirName, path30.resolve(dirName, linkPath)); @@ -141778,7 +141778,7 @@ var require_parser_stream = __commonJS({ var require_mkdirp = __commonJS({ "node_modules/mkdirp/index.js"(exports2, module2) { var path30 = require("path"); - var fs32 = require("fs"); + var fs33 = require("fs"); var _0777 = parseInt("0777", 8); module2.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; function mkdirP(p, opts, f, made) { @@ -141789,7 +141789,7 @@ var require_mkdirp = __commonJS({ opts = { mode: opts }; } var mode = opts.mode; - var xfs = opts.fs || fs32; + var xfs = opts.fs || fs33; if (mode === void 0) { mode = _0777; } @@ -141828,7 +141828,7 @@ var require_mkdirp = __commonJS({ opts = { mode: opts }; } var mode = opts.mode; - var xfs = opts.fs || fs32; + var xfs = opts.fs || fs33; if (mode === void 0) { mode = _0777; } @@ -141865,7 +141865,7 @@ var require_mkdirp = __commonJS({ // node_modules/unzip-stream/lib/extract.js var require_extract2 = __commonJS({ "node_modules/unzip-stream/lib/extract.js"(exports2, module2) { - var fs32 = require("fs"); + var fs33 = require("fs"); var path30 = require("path"); var util4 = require("util"); var mkdirp = require_mkdirp(); @@ -141912,7 +141912,7 @@ var require_extract2 = __commonJS({ var directory = entry.isDirectory ? destPath : path30.dirname(destPath); this.unfinishedEntries++; var writeFileFn = function() { - var pipedStream = fs32.createWriteStream(destPath); + var pipedStream = fs33.createWriteStream(destPath); pipedStream.on("close", function() { self2.unfinishedEntries--; self2._notifyAwaiter(); @@ -143110,7 +143110,7 @@ var require_file_command2 = __commonJS({ Object.defineProperty(exports2, "__esModule", { value: true }); exports2.prepareKeyValueMessage = exports2.issueFileCommand = void 0; var crypto3 = __importStar2(require("crypto")); - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var os7 = __importStar2(require("os")); var utils_1 = require_utils11(); function issueFileCommand(command, message) { @@ -143118,10 +143118,10 @@ var require_file_command2 = __commonJS({ if (!filePath) { throw new Error(`Unable to find environment variable for file command ${command}`); } - if (!fs32.existsSync(filePath)) { + if (!fs33.existsSync(filePath)) { throw new Error(`Missing file at path: ${filePath}`); } - fs32.appendFileSync(filePath, `${(0, utils_1.toCommandValue)(message)}${os7.EOL}`, { + fs33.appendFileSync(filePath, `${(0, utils_1.toCommandValue)(message)}${os7.EOL}`, { encoding: "utf8" }); } @@ -144444,12 +144444,12 @@ var require_io_util2 = __commonJS({ var _a6; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.getCmdPath = exports2.tryGetExecutablePath = exports2.isRooted = exports2.isDirectory = exports2.exists = exports2.READONLY = exports2.UV_FS_O_EXLOCK = exports2.IS_WINDOWS = exports2.unlink = exports2.symlink = exports2.stat = exports2.rmdir = exports2.rm = exports2.rename = exports2.readlink = exports2.readdir = exports2.open = exports2.mkdir = exports2.lstat = exports2.copyFile = exports2.chmod = void 0; - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var path30 = __importStar2(require("path")); - _a6 = fs32.promises, exports2.chmod = _a6.chmod, exports2.copyFile = _a6.copyFile, exports2.lstat = _a6.lstat, exports2.mkdir = _a6.mkdir, exports2.open = _a6.open, exports2.readdir = _a6.readdir, exports2.readlink = _a6.readlink, exports2.rename = _a6.rename, exports2.rm = _a6.rm, exports2.rmdir = _a6.rmdir, exports2.stat = _a6.stat, exports2.symlink = _a6.symlink, exports2.unlink = _a6.unlink; + _a6 = fs33.promises, exports2.chmod = _a6.chmod, exports2.copyFile = _a6.copyFile, exports2.lstat = _a6.lstat, exports2.mkdir = _a6.mkdir, exports2.open = _a6.open, exports2.readdir = _a6.readdir, exports2.readlink = _a6.readlink, exports2.rename = _a6.rename, exports2.rm = _a6.rm, exports2.rmdir = _a6.rmdir, exports2.stat = _a6.stat, exports2.symlink = _a6.symlink, exports2.unlink = _a6.unlink; exports2.IS_WINDOWS = process.platform === "win32"; exports2.UV_FS_O_EXLOCK = 268435456; - exports2.READONLY = fs32.constants.O_RDONLY; + exports2.READONLY = fs33.constants.O_RDONLY; function exists(fsPath) { return __awaiter2(this, void 0, void 0, function* () { try { @@ -145838,25 +145838,25 @@ var require_upload_specification = __commonJS({ }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.getUploadSpecification = void 0; - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var core_1 = require_core3(); var path_1 = require("path"); var path_and_artifact_name_validation_1 = require_path_and_artifact_name_validation2(); function getUploadSpecification(artifactName, rootDirectory, artifactFiles) { const specifications = []; - if (!fs32.existsSync(rootDirectory)) { + if (!fs33.existsSync(rootDirectory)) { throw new Error(`Provided rootDirectory ${rootDirectory} does not exist`); } - if (!fs32.statSync(rootDirectory).isDirectory()) { + if (!fs33.statSync(rootDirectory).isDirectory()) { throw new Error(`Provided rootDirectory ${rootDirectory} is not a valid directory`); } rootDirectory = (0, path_1.normalize)(rootDirectory); rootDirectory = (0, path_1.resolve)(rootDirectory); for (let file of artifactFiles) { - if (!fs32.existsSync(file)) { + if (!fs33.existsSync(file)) { throw new Error(`File ${file} does not exist`); } - if (!fs32.statSync(file).isDirectory()) { + if (!fs33.statSync(file).isDirectory()) { file = (0, path_1.normalize)(file); file = (0, path_1.resolve)(file); if (!file.startsWith(rootDirectory)) { @@ -145881,11 +145881,11 @@ var require_upload_specification = __commonJS({ // node_modules/tmp/lib/tmp.js var require_tmp = __commonJS({ "node_modules/tmp/lib/tmp.js"(exports2, module2) { - var fs32 = require("fs"); + var fs33 = require("fs"); var os7 = require("os"); var path30 = require("path"); var crypto3 = require("crypto"); - var _c2 = { fs: fs32.constants, os: os7.constants }; + var _c2 = { fs: fs33.constants, os: os7.constants }; var RANDOM_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; var TEMPLATE_PATTERN = /XXXXXX/; var DEFAULT_TRIES = 3; @@ -145897,13 +145897,13 @@ var require_tmp = __commonJS({ var FILE_MODE = 384; var EXIT = "exit"; var _removeObjects = []; - var FN_RMDIR_SYNC = fs32.rmdirSync.bind(fs32); + var FN_RMDIR_SYNC = fs33.rmdirSync.bind(fs33); var _gracefulCleanup = false; function rimraf(dirPath, callback) { - return fs32.rm(dirPath, { recursive: true }, callback); + return fs33.rm(dirPath, { recursive: true }, callback); } function FN_RIMRAF_SYNC(dirPath) { - return fs32.rmSync(dirPath, { recursive: true }); + return fs33.rmSync(dirPath, { recursive: true }); } function tmpName(options, callback) { const args = _parseArguments(options, callback), opts = args[0], cb = args[1]; @@ -145913,7 +145913,7 @@ var require_tmp = __commonJS({ (function _getUniqueName() { try { const name = _generateTmpName(sanitizedOptions); - fs32.stat(name, function(err2) { + fs33.stat(name, function(err2) { if (!err2) { if (tries-- > 0) return _getUniqueName(); return cb(new Error("Could not get a unique tmp filename, max tries reached " + name)); @@ -145933,7 +145933,7 @@ var require_tmp = __commonJS({ do { const name = _generateTmpName(sanitizedOptions); try { - fs32.statSync(name); + fs33.statSync(name); } catch (e) { return name; } @@ -145944,10 +145944,10 @@ var require_tmp = __commonJS({ const args = _parseArguments(options, callback), opts = args[0], cb = args[1]; tmpName(opts, function _tmpNameCreated(err, name) { if (err) return cb(err); - fs32.open(name, CREATE_FLAGS, opts.mode || FILE_MODE, function _fileCreated(err2, fd) { + fs33.open(name, CREATE_FLAGS, opts.mode || FILE_MODE, function _fileCreated(err2, fd) { if (err2) return cb(err2); if (opts.discardDescriptor) { - return fs32.close(fd, function _discardCallback(possibleErr) { + return fs33.close(fd, function _discardCallback(possibleErr) { return cb(possibleErr, name, void 0, _prepareTmpFileRemoveCallback(name, -1, opts, false)); }); } else { @@ -145961,9 +145961,9 @@ var require_tmp = __commonJS({ const args = _parseArguments(options), opts = args[0]; const discardOrDetachDescriptor = opts.discardDescriptor || opts.detachDescriptor; const name = tmpNameSync(opts); - let fd = fs32.openSync(name, CREATE_FLAGS, opts.mode || FILE_MODE); + let fd = fs33.openSync(name, CREATE_FLAGS, opts.mode || FILE_MODE); if (opts.discardDescriptor) { - fs32.closeSync(fd); + fs33.closeSync(fd); fd = void 0; } return { @@ -145976,7 +145976,7 @@ var require_tmp = __commonJS({ const args = _parseArguments(options, callback), opts = args[0], cb = args[1]; tmpName(opts, function _tmpNameCreated(err, name) { if (err) return cb(err); - fs32.mkdir(name, opts.mode || DIR_MODE, function _dirCreated(err2) { + fs33.mkdir(name, opts.mode || DIR_MODE, function _dirCreated(err2) { if (err2) return cb(err2); cb(null, name, _prepareTmpDirRemoveCallback(name, opts, false)); }); @@ -145985,7 +145985,7 @@ var require_tmp = __commonJS({ function dirSync(options) { const args = _parseArguments(options), opts = args[0]; const name = tmpNameSync(opts); - fs32.mkdirSync(name, opts.mode || DIR_MODE); + fs33.mkdirSync(name, opts.mode || DIR_MODE); return { name, removeCallback: _prepareTmpDirRemoveCallback(name, opts, true) @@ -145999,20 +145999,20 @@ var require_tmp = __commonJS({ next(); }; if (0 <= fdPath[0]) - fs32.close(fdPath[0], function() { - fs32.unlink(fdPath[1], _handler); + fs33.close(fdPath[0], function() { + fs33.unlink(fdPath[1], _handler); }); - else fs32.unlink(fdPath[1], _handler); + else fs33.unlink(fdPath[1], _handler); } function _removeFileSync(fdPath) { let rethrownException = null; try { - if (0 <= fdPath[0]) fs32.closeSync(fdPath[0]); + if (0 <= fdPath[0]) fs33.closeSync(fdPath[0]); } catch (e) { if (!_isEBADF(e) && !_isENOENT(e)) throw e; } finally { try { - fs32.unlinkSync(fdPath[1]); + fs33.unlinkSync(fdPath[1]); } catch (e) { if (!_isENOENT(e)) rethrownException = e; } @@ -146028,7 +146028,7 @@ var require_tmp = __commonJS({ return sync ? removeCallbackSync : removeCallback; } function _prepareTmpDirRemoveCallback(name, opts, sync) { - const removeFunction = opts.unsafeCleanup ? rimraf : fs32.rmdir.bind(fs32); + const removeFunction = opts.unsafeCleanup ? rimraf : fs33.rmdir.bind(fs33); const removeFunctionSync = opts.unsafeCleanup ? FN_RIMRAF_SYNC : FN_RMDIR_SYNC; const removeCallbackSync = _prepareRemoveCallback(removeFunctionSync, name, sync); const removeCallback = _prepareRemoveCallback(removeFunction, name, sync, removeCallbackSync); @@ -146091,24 +146091,24 @@ var require_tmp = __commonJS({ } function _resolvePath(name, tmpDir, cb) { const pathToResolve = path30.isAbsolute(name) ? name : path30.join(tmpDir, name); - fs32.stat(pathToResolve, function(err) { + fs33.stat(pathToResolve, function(err) { if (err) { - fs32.realpath(path30.dirname(pathToResolve), function(err2, parentDir) { + fs33.realpath(path30.dirname(pathToResolve), function(err2, parentDir) { if (err2) return cb(err2); cb(null, path30.join(parentDir, path30.basename(pathToResolve))); }); } else { - fs32.realpath(pathToResolve, cb); + fs33.realpath(pathToResolve, cb); } }); } function _resolvePathSync(name, tmpDir) { const pathToResolve = path30.isAbsolute(name) ? name : path30.join(tmpDir, name); try { - fs32.statSync(pathToResolve); - return fs32.realpathSync(pathToResolve); + fs33.statSync(pathToResolve); + return fs33.realpathSync(pathToResolve); } catch (_err) { - const parentDir = fs32.realpathSync(path30.dirname(pathToResolve)); + const parentDir = fs33.realpathSync(path30.dirname(pathToResolve)); return path30.join(parentDir, path30.basename(pathToResolve)); } } @@ -146229,10 +146229,10 @@ var require_tmp = __commonJS({ _gracefulCleanup = true; } function _getTmpDir(options, cb) { - return fs32.realpath(options && options.tmpdir || os7.tmpdir(), cb); + return fs33.realpath(options && options.tmpdir || os7.tmpdir(), cb); } function _getTmpDirSync(options) { - return fs32.realpathSync(options && options.tmpdir || os7.tmpdir()); + return fs33.realpathSync(options && options.tmpdir || os7.tmpdir()); } process.addListener(EXIT, _garbageCollector); Object.defineProperty(module2.exports, "tmpdir", { @@ -147086,10 +147086,10 @@ var require_upload_gzip = __commonJS({ }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.createGZipFileInBuffer = exports2.createGZipFileOnDisk = void 0; - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var zlib3 = __importStar2(require("zlib")); var util_1 = require("util"); - var stat2 = (0, util_1.promisify)(fs32.stat); + var stat2 = (0, util_1.promisify)(fs33.stat); var gzipExemptFileExtensions = [ ".gz", ".gzip", @@ -147122,9 +147122,9 @@ var require_upload_gzip = __commonJS({ } } return new Promise((resolve14, reject) => { - const inputStream = fs32.createReadStream(originalFilePath); + const inputStream = fs33.createReadStream(originalFilePath); const gzip = zlib3.createGzip(); - const outputStream = fs32.createWriteStream(tempFilePath); + const outputStream = fs33.createWriteStream(tempFilePath); inputStream.pipe(gzip).pipe(outputStream); outputStream.on("finish", () => __awaiter2(this, void 0, void 0, function* () { const size = (yield stat2(tempFilePath)).size; @@ -147142,7 +147142,7 @@ var require_upload_gzip = __commonJS({ return __awaiter2(this, void 0, void 0, function* () { return new Promise((resolve14) => __awaiter2(this, void 0, void 0, function* () { var _a6, e_1, _b2, _c2; - const inputStream = fs32.createReadStream(originalFilePath); + const inputStream = fs33.createReadStream(originalFilePath); const gzip = zlib3.createGzip(); inputStream.pipe(gzip); const chunks = []; @@ -147351,7 +147351,7 @@ var require_upload_http_client = __commonJS({ }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.UploadHttpClient = void 0; - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var core29 = __importStar2(require_core3()); var tmp2 = __importStar2(require_tmp_promise()); var stream2 = __importStar2(require("stream")); @@ -147365,7 +147365,7 @@ var require_upload_http_client = __commonJS({ var http_manager_1 = require_http_manager(); var upload_gzip_1 = require_upload_gzip(); var requestUtils_1 = require_requestUtils2(); - var stat2 = (0, util_1.promisify)(fs32.stat); + var stat2 = (0, util_1.promisify)(fs33.stat); var UploadHttpClient = class { constructor() { this.uploadHttpManager = new http_manager_1.HttpManager((0, config_variables_1.getUploadFileConcurrency)(), "@actions/artifact-upload"); @@ -147502,7 +147502,7 @@ var require_upload_http_client = __commonJS({ let openUploadStream; if (totalFileSize < buffer.byteLength) { core29.debug(`The gzip file created for ${parameters.file} did not help with reducing the size of the file. The original file will be uploaded as-is`); - openUploadStream = () => fs32.createReadStream(parameters.file); + openUploadStream = () => fs33.createReadStream(parameters.file); isGzip = false; uploadFileSize = totalFileSize; } else { @@ -147548,7 +147548,7 @@ var require_upload_http_client = __commonJS({ failedChunkSizes += chunkSize; continue; } - const result = yield this.uploadChunk(httpClientIndex, parameters.resourceUrl, () => fs32.createReadStream(uploadFilePath, { + const result = yield this.uploadChunk(httpClientIndex, parameters.resourceUrl, () => fs33.createReadStream(uploadFilePath, { start: startChunkIndex, end: endChunkIndex, autoClose: false @@ -147743,7 +147743,7 @@ var require_download_http_client = __commonJS({ }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.DownloadHttpClient = void 0; - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var core29 = __importStar2(require_core3()); var zlib3 = __importStar2(require("zlib")); var utils_1 = require_utils12(); @@ -147834,7 +147834,7 @@ var require_download_http_client = __commonJS({ return __awaiter2(this, void 0, void 0, function* () { let retryCount = 0; const retryLimit = (0, config_variables_1.getRetryLimit)(); - let destinationStream = fs32.createWriteStream(downloadPath); + let destinationStream = fs33.createWriteStream(downloadPath); const headers = (0, utils_1.getDownloadHeaders)("application/json", true, true); const makeDownloadRequest = () => __awaiter2(this, void 0, void 0, function* () { const client = this.downloadHttpManager.getClient(httpClientIndex); @@ -147876,7 +147876,7 @@ var require_download_http_client = __commonJS({ } }); yield (0, utils_1.rmFile)(fileDownloadPath); - destinationStream = fs32.createWriteStream(fileDownloadPath); + destinationStream = fs33.createWriteStream(fileDownloadPath); }); while (retryCount <= retryLimit) { let response; @@ -166082,7 +166082,7 @@ __export(entry_points_exports, { module.exports = __toCommonJS(entry_points_exports); // src/analyze-action.ts -var fs23 = __toESM(require("fs")); +var fs24 = __toESM(require("fs")); var import_path5 = __toESM(require("path")); var import_perf_hooks6 = require("perf_hooks"); var core14 = __toESM(require_core()); @@ -166227,21 +166227,21 @@ async function getFolderSize(itemPath, options) { getFolderSize.loose = async (itemPath, options) => await core2(itemPath, options); getFolderSize.strict = async (itemPath, options) => await core2(itemPath, options, { strict: true }); async function core2(rootItemPath, options = {}, returnType = {}) { - const fs32 = options.fs || await import("node:fs/promises"); + const fs33 = options.fs || await import("node:fs/promises"); let folderSize = 0n; const foundInos = /* @__PURE__ */ new Set(); const errors = []; await processItem(rootItemPath); async function processItem(itemPath) { if (options.ignore?.test(itemPath)) return; - const stats = returnType.strict ? await fs32.lstat(itemPath, { bigint: true }) : await fs32.lstat(itemPath, { bigint: true }).catch((error3) => errors.push(error3)); + const stats = returnType.strict ? await fs33.lstat(itemPath, { bigint: true }) : await fs33.lstat(itemPath, { bigint: true }).catch((error3) => errors.push(error3)); if (typeof stats !== "object") return; if (!foundInos.has(stats.ino)) { foundInos.add(stats.ino); folderSize += stats.size; } if (stats.isDirectory()) { - const directoryItems = returnType.strict ? await fs32.readdir(itemPath) : await fs32.readdir(itemPath).catch((error3) => errors.push(error3)); + const directoryItems = returnType.strict ? await fs33.readdir(itemPath) : await fs33.readdir(itemPath).catch((error3) => errors.push(error3)); if (typeof directoryItems !== "object") return; await Promise.all( directoryItems.map( @@ -172655,13 +172655,13 @@ var SarifScanOrder = [ ]; // src/analyze.ts -var fs17 = __toESM(require("fs")); +var fs18 = __toESM(require("fs")); var path16 = __toESM(require("path")); var import_perf_hooks5 = require("perf_hooks"); var io5 = __toESM(require_io()); // src/codeql.ts -var fs16 = __toESM(require("fs")); +var fs17 = __toESM(require("fs")); var path15 = __toESM(require("path")); var core12 = __toESM(require_core()); var toolrunner3 = __toESM(require_toolrunner()); @@ -172916,7 +172916,7 @@ function wrapCliConfigurationError(cliError) { } // src/config-utils.ts -var fs10 = __toESM(require("fs")); +var fs11 = __toESM(require("fs")); var path11 = __toESM(require("path")); var import_perf_hooks2 = require("perf_hooks"); @@ -172976,7 +172976,7 @@ var jsonschema = __toESM(require_lib2()); var semver4 = __toESM(require_semver2()); // src/diagnostics.ts -var import_fs = require("fs"); +var fs7 = __toESM(require("fs")); var import_path2 = __toESM(require("path")); var temporaryDiagnostics = []; var diagnosticCounter = 0; @@ -172997,11 +172997,10 @@ function getDiagnosticsPath(basePath, language) { } return basePath; } -function addDiagnostic(config, language, diagnostic) { - const logger2 = getActionsLogger(); +function addDiagnostic(config, language, diagnostic, logger2 = getActionsLogger()) { const databasePath = language ? getCodeQLDatabasePath(config, language) : config.dbLocation; - if ((0, import_fs.existsSync)(databasePath)) { - writeDiagnostic(config, language, diagnostic); + if (fs7.existsSync(databasePath)) { + writeDiagnostic({ logger: logger2 }, config, language, diagnostic); } else { logger2.debug( `Writing a diagnostic for ${language}, but the database at ${databasePath} does not exist yet.` @@ -173027,7 +173026,7 @@ function addNoLanguageDiagnostic(config, diagnostic) { } } function writeDiagnosticFile(diagnosticsPath, diagnostic) { - (0, import_fs.mkdirSync)(diagnosticsPath, { recursive: true }); + fs7.mkdirSync(diagnosticsPath, { recursive: true }); const uniqueSuffix = (diagnosticCounter++).toString(); const sanitizedTimestamp = diagnostic.timestamp.replace( /[^a-zA-Z0-9.-]/g, @@ -173037,40 +173036,40 @@ function writeDiagnosticFile(diagnosticsPath, diagnostic) { diagnosticsPath, `codeql-action-${sanitizedTimestamp}-${uniqueSuffix}.json` ); - (0, import_fs.writeFileSync)(jsonPath, JSON.stringify(diagnostic)); + fs7.writeFileSync(jsonPath, JSON.stringify(diagnostic)); return jsonPath; } function getDatabaseDiagnosticsPath(config, language) { return getDiagnosticsPath(config.dbLocation, language); } -function writeDiagnostic(config, language, diagnostic) { - const logger2 = getActionsLogger(); +function writeDiagnostic(action, config, language, diagnostic) { const diagnosticsPath = getDatabaseDiagnosticsPath(config, language); try { writeDiagnosticFile(diagnosticsPath, diagnostic); } catch (err) { - logger2.warning(`Unable to write diagnostic message to database: ${err}`); - logger2.debug(JSON.stringify(diagnostic)); + action.logger.warning( + `Unable to write diagnostic message to database: ${err}` + ); + action.logger.debug(JSON.stringify(diagnostic)); } } -function logTemporaryDiagnostics() { - const logger2 = getActionsLogger(); +function logTemporaryDiagnostics(action) { const num = temporaryDiagnostics.length; if (num > 0) { - logger2.warning( + action.logger.warning( `${num} diagnostic(s) could not be written to the database and will not appear on the Tool Status Page.` ); for (const temporary of temporaryDiagnostics) { - logger2.debug((0, import_fs.readFileSync)(temporary.path, "utf-8")); + action.logger.debug(fs7.readFileSync(temporary.path, "utf-8")); } } } -function flushDiagnostics(config) { - const logger2 = getActionsLogger(); +function flushDiagnostics(action, config) { const diagnosticsCount = temporaryDiagnostics.length; - logger2.debug( + action.logger.debug( `Moving ${diagnosticsCount} diagnostic(s) to their respective databases.` ); + const failedFlushDiagnostics = []; for (const temporary of temporaryDiagnostics) { const directory = getDatabaseDiagnosticsPath( config, @@ -173078,9 +173077,17 @@ function flushDiagnostics(config) { ); const filename = import_path2.default.basename(temporary.path); const destination = import_path2.default.join(directory, filename); - (0, import_fs.renameSync)(temporary.path, destination); + try { + fs7.mkdirSync(directory, { recursive: true }); + fs7.renameSync(temporary.path, destination); + } catch (err) { + action.logger.warning( + `Failed to rename '${temporary.path}' to '${destination}': ${getErrorMessage(err)}` + ); + failedFlushDiagnostics.push(temporary); + } } - temporaryDiagnostics = []; + temporaryDiagnostics = failedFlushDiagnostics; } function makeTelemetryDiagnostic(id, name, attributes, tags) { return makeDiagnostic(id, name, { @@ -173742,7 +173749,7 @@ async function getRemoteConfig(actionState, configFile, apiDetails) { } // src/diff-informed-analysis-utils.ts -var fs7 = __toESM(require("fs")); +var fs8 = __toESM(require("fs")); async function getDiffInformedAnalysisBranches(codeql, features, logger2) { if (!await features.getValue("diff_informed_queries" /* DiffInformedQueries */, codeql)) { return void 0; @@ -173784,7 +173791,7 @@ async function prepareDiffInformedAnalysis(codeql, features, logger2) { function writeDiffRangesJsonFile(logger2, ranges) { const jsonContents = JSON.stringify(ranges, null, 2); const jsonFilePath = getDiffRangesJsonFilePath(); - fs7.writeFileSync(jsonFilePath, jsonContents); + fs8.writeFileSync(jsonFilePath, jsonContents); logger2.debug( `Wrote pr-diff-range JSON file to ${jsonFilePath}: ${jsonContents}` @@ -173792,11 +173799,11 @@ ${jsonContents}` } function readDiffRangesJsonFile(logger2) { const jsonFilePath = getDiffRangesJsonFilePath(); - if (!fs7.existsSync(jsonFilePath)) { + if (!fs8.existsSync(jsonFilePath)) { logger2.debug(`Diff ranges JSON file does not exist at ${jsonFilePath}`); return void 0; } - const jsonContents = fs7.readFileSync(jsonFilePath, "utf8"); + const jsonContents = fs8.readFileSync(jsonFilePath, "utf8"); logger2.debug( `Read pr-diff-range JSON file from ${jsonFilePath}: ${jsonContents}` @@ -174047,7 +174054,7 @@ Improved incremental analysis will be automatically retried when the next versio } // src/overlay/status.ts -var fs8 = __toESM(require("fs")); +var fs9 = __toESM(require("fs")); var path9 = __toESM(require("path")); var actionsCache = __toESM(require_cache4()); var MAX_CACHE_OPERATION_MS = 3e4; @@ -174092,7 +174099,7 @@ async function getOverlayStatus(codeql, languages, diskUsage, logger2) { const cacheKey3 = await getCacheKey(codeql, languages, diskUsage); const statusFile = getStatusFilePath(languages); try { - await fs8.promises.mkdir(path9.dirname(statusFile), { recursive: true }); + await fs9.promises.mkdir(path9.dirname(statusFile), { recursive: true }); const foundKey = await waitForResultWithTimeLimit( MAX_CACHE_OPERATION_MS, actionsCache.restoreCache([statusFile], cacheKey3), @@ -174104,13 +174111,13 @@ async function getOverlayStatus(codeql, languages, diskUsage, logger2) { logger2.debug("No overlay status found in Actions cache."); return void 0; } - if (!fs8.existsSync(statusFile)) { + if (!fs9.existsSync(statusFile)) { logger2.debug( "Overlay status cache entry found but status file is missing." ); return void 0; } - const contents = await fs8.promises.readFile(statusFile, "utf-8"); + const contents = await fs9.promises.readFile(statusFile, "utf-8"); const parsed = JSON.parse(contents); if (!isObject2(parsed) || typeof parsed["attemptedToBuildOverlayBaseDatabase"] !== "boolean" || typeof parsed["builtOverlayBaseDatabase"] !== "boolean") { logger2.debug( @@ -174130,8 +174137,8 @@ async function saveOverlayStatus(codeql, languages, diskUsage, status, logger2) const cacheKey3 = await getCacheKey(codeql, languages, diskUsage); const statusFile = getStatusFilePath(languages); try { - await fs8.promises.mkdir(path9.dirname(statusFile), { recursive: true }); - await fs8.promises.writeFile(statusFile, JSON.stringify(status)); + await fs9.promises.mkdir(path9.dirname(statusFile), { recursive: true }); + await fs9.promises.writeFile(statusFile, JSON.stringify(status)); const cacheId = await waitForResultWithTimeLimit( MAX_CACHE_OPERATION_MS, actionsCache.saveCache([statusFile], cacheKey3), @@ -174157,7 +174164,7 @@ async function getCacheKey(codeql, languages, diskUsage) { } // src/trap-caching.ts -var fs9 = __toESM(require("fs")); +var fs10 = __toESM(require("fs")); var path10 = __toESM(require("path")); var actionsCache2 = __toESM(require_cache4()); var CACHE_VERSION = 1; @@ -174181,7 +174188,7 @@ async function downloadTrapCaches(codeql, languages, logger2) { ); for (const language of languagesSupportingCaching) { const cacheDir2 = path10.join(cachesDir, language); - fs9.mkdirSync(cacheDir2, { recursive: true }); + fs10.mkdirSync(cacheDir2, { recursive: true }); result[language] = cacheDir2; } if (await isAnalyzingDefaultBranch()) { @@ -174193,7 +174200,7 @@ async function downloadTrapCaches(codeql, languages, logger2) { let baseSha = "unknown"; const eventPath = process.env.GITHUB_EVENT_PATH; if (getWorkflowEventName() === "pull_request" && eventPath !== void 0) { - const event = JSON.parse(fs9.readFileSync(path10.resolve(eventPath), "utf-8")); + const event = JSON.parse(fs10.readFileSync(path10.resolve(eventPath), "utf-8")); baseSha = event.pull_request?.base?.sha || baseSha; } for (const language of languages) { @@ -174418,8 +174425,8 @@ async function getSupportedLanguageMap(codeql, logger2) { var baseWorkflowsPath = ".github/workflows"; function hasActionsWorkflows(sourceRoot) { const workflowsPath = path11.resolve(sourceRoot, baseWorkflowsPath); - const stats = fs10.lstatSync(workflowsPath, { throwIfNoEntry: false }); - return stats !== void 0 && stats.isDirectory() && fs10.readdirSync(workflowsPath).length > 0; + const stats = fs11.lstatSync(workflowsPath, { throwIfNoEntry: false }); + return stats !== void 0 && stats.isDirectory() && fs11.readdirSync(workflowsPath).length > 0; } async function getRawLanguagesInRepo(repository, sourceRoot, logger2) { logger2.debug( @@ -174904,7 +174911,7 @@ async function determineUserConfig(action, tempDir, inputs) { inputs.configInput, fromConfigFile ); - fs10.writeFileSync(computedConfigPath, dump(mergedConfig)); + fs11.writeFileSync(computedConfigPath, dump(mergedConfig)); action.logger.debug( `Using merged configurations from 'config' input with configuration from '${inputs.configFile}': ${computedConfigPath}` ); @@ -174916,7 +174923,7 @@ async function determineUserConfig(action, tempDir, inputs) { `Both a config file and config input were provided. Ignoring config file.` ); } - fs10.writeFileSync(computedConfigPath, dump(inputs.configInput)); + fs11.writeFileSync(computedConfigPath, dump(inputs.configInput)); inputs.configFile = computedConfigPath; action.logger.debug( `Using config from action input: ${inputs.configFile}` @@ -175070,7 +175077,7 @@ function isLocal(configPath) { return !containsAtRef(configPath); } function getLocalConfig(logger2, configFile, validateConfig) { - if (!fs10.existsSync(configFile)) { + if (!fs11.existsSync(configFile)) { throw new ConfigurationError( getConfigFileDoesNotExistErrorMessage(configFile) ); @@ -175078,7 +175085,7 @@ function getLocalConfig(logger2, configFile, validateConfig) { return parseUserConfig( logger2, configFile, - fs10.readFileSync(configFile, "utf-8"), + fs11.readFileSync(configFile, "utf-8"), validateConfig ); } @@ -175090,8 +175097,8 @@ function getPathToFailedConfigFile(tempDir) { } function saveConfigFile(logger2, configFilePath, config) { const configString = JSON.stringify(config); - fs10.mkdirSync(path11.dirname(configFilePath), { recursive: true }); - fs10.writeFileSync(configFilePath, configString, "utf8"); + fs11.mkdirSync(path11.dirname(configFilePath), { recursive: true }); + fs11.writeFileSync(configFilePath, configString, "utf8"); logger2.debug("Saved config:"); logger2.debug(configString); } @@ -175120,10 +175127,10 @@ function saveAbortedInitConfig(action, config) { ); } function readConfigFile(logger2, configFilePath) { - if (!fs10.existsSync(configFilePath)) { + if (!fs11.existsSync(configFilePath)) { return void 0; } - const configString = fs10.readFileSync(configFilePath, "utf8"); + const configString = fs11.readFileSync(configFilePath, "utf8"); logger2.debug("Loaded config:"); logger2.debug(configString); const config = JSON.parse(configString); @@ -175162,7 +175169,7 @@ async function generateRegistries(registriesInput, tempDir, logger2) { const qlconfig = createRegistriesBlock(registries); qlconfigFile = path11.join(tempDir, "qlconfig.yml"); const qlconfigContents = dump(qlconfig); - fs10.writeFileSync(qlconfigFile, qlconfigContents, "utf8"); + fs11.writeFileSync(qlconfigFile, qlconfigContents, "utf8"); logger2.debug("Generated qlconfig.yml:"); logger2.debug(qlconfigContents); registriesAuthTokens = registries.map((registry) => `${registry.url}=${registry.token}`).join(","); @@ -175291,7 +175298,7 @@ async function logGeneratedFilesTelemetry(config, duration, generatedFilesCount) } // src/setup-codeql.ts -var fs14 = __toESM(require("fs")); +var fs15 = __toESM(require("fs")); var path13 = __toESM(require("path")); var import_perf_hooks4 = require("perf_hooks"); var toolcache3 = __toESM(require_tool_cache()); @@ -175328,7 +175335,7 @@ function getCodeQLBundleFromUrl(url2) { } // src/overlay/caching.ts -var fs11 = __toESM(require("fs")); +var fs12 = __toESM(require("fs")); var actionsCache3 = __toESM(require_cache4()); var semver5 = __toESM(require_semver2()); var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 7500; @@ -175338,7 +175345,7 @@ var CACHE_PREFIX = "codeql-overlay-base-database"; var MAX_CACHE_OPERATION_MS3 = 6e5; async function checkOverlayBaseDatabase(codeql, config, logger2, warningPrefix) { const baseDatabaseOidsFilePath = getBaseDatabaseOidsFilePath(config); - if (!fs11.existsSync(baseDatabaseOidsFilePath)) { + if (!fs12.existsSync(baseDatabaseOidsFilePath)) { logger2.warning( `${warningPrefix}: ${baseDatabaseOidsFilePath} does not exist` ); @@ -175753,7 +175760,7 @@ function logPerLanguageBundleFallback({ logger: logger2 }, language, location) { // src/tar.ts var import_child_process = require("child_process"); -var fs12 = __toESM(require("fs")); +var fs13 = __toESM(require("fs")); var stream = __toESM(require("stream")); var import_toolrunner = __toESM(require_toolrunner()); var io4 = __toESM(require_io()); @@ -175826,7 +175833,7 @@ async function isZstdAvailable(logger2) { } } async function extract(tarPath, dest, compressionMethod, tarVersion, logger2) { - fs12.mkdirSync(dest, { recursive: true }); + fs13.mkdirSync(dest, { recursive: true }); switch (compressionMethod) { case "gzip": return await toolcache.extractTar(tarPath, dest); @@ -175912,7 +175919,7 @@ function inferCompressionMethod(tarPath) { } // src/tools-download.ts -var fs13 = __toESM(require("fs")); +var fs14 = __toESM(require("fs")); var os4 = __toESM(require("os")); var path12 = __toESM(require("path")); var import_perf_hooks3 = require("perf_hooks"); @@ -175998,7 +176005,7 @@ async function downloadAndExtract(codeqlURL, compressionMethod, dest, authorizat }; } async function downloadAndExtractZstdWithStreaming(codeqlURL, dest, authorization, headers, tarVersion, logger2) { - fs13.mkdirSync(dest, { recursive: true }); + fs14.mkdirSync(dest, { recursive: true }); const agent = new import_http_client.HttpClient().getAgent(codeqlURL); headers = Object.assign( { "User-Agent": "CodeQL Action" }, @@ -176057,7 +176064,7 @@ function getToolcacheDirectory(version) { } function isToolcacheOnWorkspaceFilesystem(logger2) { try { - return fs13.statSync(getRequiredEnvParam("RUNNER_TOOL_CACHE")).dev === fs13.statSync(getRequiredEnvParam("GITHUB_WORKSPACE")).dev; + return fs14.statSync(getRequiredEnvParam("RUNNER_TOOL_CACHE")).dev === fs14.statSync(getRequiredEnvParam("GITHUB_WORKSPACE")).dev; } catch (e) { logger2.debug( `Could not determine whether the toolcache is on the same filesystem as the workspace: ${getErrorMessage(e)}` @@ -176079,7 +176086,7 @@ async function deleteToolcacheBundles({ return { deletedVersions: [], failed: true }; } try { - if ((await fs13.promises.lstat(toolDirectory)).isSymbolicLink()) { + if ((await fs14.promises.lstat(toolDirectory)).isSymbolicLink()) { logger2.info( `Not deleting the CodeQL tools from the toolcache since '${toolDirectory}' is a symlink.` ); @@ -176098,7 +176105,7 @@ async function deleteToolcacheBundles({ return { deletedVersions: [], failed: true }; } try { - const entries = await fs13.promises.readdir(toolDirectory, { + const entries = await fs14.promises.readdir(toolDirectory, { withFileTypes: true }); const deletedVersions = []; @@ -176112,7 +176119,7 @@ async function deleteToolcacheBundles({ } const versionDirectory = path12.join(toolDirectory, entry.name); try { - await fs13.promises.rm(versionDirectory, { + await fs14.promises.rm(versionDirectory, { force: true, recursive: true }); @@ -176137,7 +176144,7 @@ async function deleteToolcacheBundles({ } function writeToolcacheMarkerFile(extractedPath, logger2) { const markerFilePath = `${extractedPath}.complete`; - fs13.writeFileSync(markerFilePath, ""); + fs14.writeFileSync(markerFilePath, ""); logger2.info(`Created toolcache marker file ${markerFilePath}`); } @@ -176242,7 +176249,7 @@ async function findOverridingToolsInCache(humanReadableVersion, logger2) { const candidates = toolcache3.findAllVersions("CodeQL").filter(isGoodVersion).map((version) => ({ folder: toolcache3.find("CodeQL", version), version - })).filter(({ folder }) => fs14.existsSync(path13.join(folder, "pinned-version"))); + })).filter(({ folder }) => fs15.existsSync(path13.join(folder, "pinned-version"))); if (candidates.length === 1) { const candidate = candidates[0]; logger2.debug( @@ -176904,7 +176911,7 @@ function isReservedToolsValue(tools) { } // src/tracer-config.ts -var fs15 = __toESM(require("fs")); +var fs16 = __toESM(require("fs")); var path14 = __toESM(require("path")); async function shouldEnableIndirectTracing(codeql, config) { if (config.buildMode === "none" /* None */) { @@ -176924,14 +176931,14 @@ async function endTracingForCluster(codeql, config, logger2) { config.dbLocation, "temp/tracingEnvironment/end-tracing.json" ); - if (!fs15.existsSync(envVariablesFile)) { + if (!fs16.existsSync(envVariablesFile)) { throw new Error( `Environment file for ending tracing not found: ${envVariablesFile}` ); } try { const endTracingEnvVariables = JSON.parse( - fs15.readFileSync(envVariablesFile, "utf8") + fs16.readFileSync(envVariablesFile, "utf8") ); for (const [key, value] of Object.entries(endTracingEnvVariables)) { if (value !== null) { @@ -176952,7 +176959,7 @@ async function getTracerConfigForCluster(logger2, config) { "temp/tracingEnvironment/start-tracing.json" ); try { - const tracingEnvVariables = JSON.parse(fs15.readFileSync(filePath, "utf8")); + const tracingEnvVariables = JSON.parse(fs16.readFileSync(filePath, "utf8")); return { env: tracingEnvVariables }; @@ -177074,7 +177081,7 @@ async function getCodeQLForCmd(logger2, cmd, checkVersion) { "tools", "tracing-config.lua" ); - return fs16.existsSync(tracingConfigPath); + return fs17.existsSync(tracingConfigPath); }, async isScannedLanguage(language) { return !await this.isTracedLanguage(language); @@ -177532,7 +177539,7 @@ async function writeCodeScanningConfigFile(config, logger2) { logger2.startGroup("Augmented user configuration file contents"); logger2.info(dump(augmentedConfig)); logger2.endGroup(); - fs16.writeFileSync(codeScanningConfigFile, dump(augmentedConfig)); + fs17.writeFileSync(codeScanningConfigFile, dump(augmentedConfig)); return codeScanningConfigFile; } var TRAP_CACHE_SIZE_MB = 1024; @@ -177992,7 +177999,7 @@ function dbIsFinalized(config, language, logger2) { const dbPath = getCodeQLDatabasePath(config, language); try { const dbInfo = load( - fs17.readFileSync(path16.resolve(dbPath, "codeql-database.yml"), "utf8") + fs18.readFileSync(path16.resolve(dbPath, "codeql-database.yml"), "utf8") ); return !("inProgress" in dbInfo); } catch { @@ -178076,8 +178083,8 @@ function writeDiffRangeDataExtensionPack(logger2, ranges, checkoutPath) { ranges = [{ path: "", startLine: 0, endLine: 0 }]; } const diffRangeDir = path16.join(getTemporaryDirectory(), "pr-diff-range"); - fs17.mkdirSync(diffRangeDir, { recursive: true }); - fs17.writeFileSync( + fs18.mkdirSync(diffRangeDir, { recursive: true }); + fs18.writeFileSync( path16.join(diffRangeDir, "qlpack.yml"), ` name: codeql-action/pr-diff-range @@ -178094,7 +178101,7 @@ dataExtensions: checkoutPath ); const extensionFilePath = path16.join(diffRangeDir, "pr-diff-range.yml"); - fs17.writeFileSync(extensionFilePath, extensionContents); + fs18.writeFileSync(extensionFilePath, extensionContents); logger2.debug( `Wrote pr-diff-range extension pack to ${extensionFilePath}: ${extensionContents}` @@ -178239,7 +178246,7 @@ async function runQueries(sarifFolder, memoryFlag, threadsFlag, diffRangePackDir } function getPerQueryAlertCounts(sarifPath) { const sarifObject = JSON.parse( - fs17.readFileSync(sarifPath, "utf8") + fs18.readFileSync(sarifPath, "utf8") ); const perQueryAlertCounts = {}; for (const sarifRun of sarifObject.runs) { @@ -178257,13 +178264,13 @@ async function runQueries(sarifFolder, memoryFlag, threadsFlag, diffRangePackDir } async function runFinalize(features, outputDir, threadsFlag, memoryFlag, codeql, config, logger2) { try { - await fs17.promises.rm(outputDir, { force: true, recursive: true }); + await fs18.promises.rm(outputDir, { force: true, recursive: true }); } catch (error3) { if (error3?.code !== "ENOENT") { throw error3; } } - await fs17.promises.mkdir(outputDir, { recursive: true }); + await fs18.promises.mkdir(outputDir, { recursive: true }); const timings = await finalizeDatabaseCreation( codeql, features, @@ -178307,7 +178314,7 @@ async function warnIfGoInstalledAfterInit(config, logger2) { } // src/database-upload.ts -var fs18 = __toESM(require("fs")); +var fs19 = __toESM(require("fs")); async function cleanupAndUploadDatabases(action, repositoryNwo, codeql, config, apiDetails, checkoutPath) { const logger2 = action.logger; if (getRequiredInput("upload-database") !== "true") { @@ -178344,7 +178351,7 @@ async function cleanupAndUploadDatabases(action, repositoryNwo, codeql, config, const bundledDb = await bundleDb(config, language, codeql, language, { includeDiagnostics: false }); - bundledDbSize = fs18.statSync(bundledDb).size; + bundledDbSize = fs19.statSync(bundledDb).size; const commitOid = await getCommitOid(checkoutPath); const maxAttempts = 4; let uploadDurationMs; @@ -178422,7 +178429,7 @@ async function recordClearCleanupSizes(codeql, config, reports, logger2) { const bundledDb = await bundleDb(config, language, codeql, language, { includeDiagnostics: false }); - report.clear_cleanup_zipped_size_bytes = fs18.statSync(bundledDb).size; + report.clear_cleanup_zipped_size_bytes = fs19.statSync(bundledDb).size; logger2.debug( `Database for ${language} is ${report.clear_cleanup_zipped_size_bytes} bytes zipped at the '${"clear" /* Clear */}' cleanup level (vs. ${report.zipped_upload_size_bytes ?? "unknown"} bytes at the '${"overlay" /* Overlay */}' level).` ); @@ -178445,7 +178452,7 @@ async function uploadBundledDatabase(repositoryNwo, language, commitOid, bundled if (uploadsBaseUrl.endsWith("/")) { uploadsBaseUrl = uploadsBaseUrl.slice(0, -1); } - const bundledDbReadStream = fs18.createReadStream(bundledDb); + const bundledDbReadStream = fs19.createReadStream(bundledDb); try { const startTime = performance.now(); await client.request( @@ -178497,7 +178504,7 @@ __export(upload_lib_exports, { waitForProcessing: () => waitForProcessing, writePostProcessedFiles: () => writePostProcessedFiles }); -var fs22 = __toESM(require("fs")); +var fs23 = __toESM(require("fs")); var path19 = __toESM(require("path")); var url = __toESM(require("url")); var import_zlib = __toESM(require("zlib")); @@ -178505,7 +178512,7 @@ var core13 = __toESM(require_core()); var jsonschema2 = __toESM(require_lib2()); // src/fingerprints.ts -var fs19 = __toESM(require("fs")); +var fs20 = __toESM(require("fs")); var import_path4 = __toESM(require("path")); // node_modules/long/index.js @@ -179493,7 +179500,7 @@ async function hash(callback, filepath) { } updateHash(current); }; - const readStream = fs19.createReadStream(filepath, "utf8"); + const readStream = fs20.createReadStream(filepath, "utf8"); for await (const data of readStream) { for (let i = 0; i < data.length; ++i) { processCharacter(data.charCodeAt(i)); @@ -179568,11 +179575,11 @@ function resolveUriToFile(location, artifacts, sourceRoot, logger2) { if (!import_path4.default.isAbsolute(uri)) { uri = srcRootPrefix + uri; } - if (!fs19.existsSync(uri)) { + if (!fs20.existsSync(uri)) { logger2.debug(`Unable to compute fingerprint for non-existent file: ${uri}`); return void 0; } - if (fs19.statSync(uri).isDirectory()) { + if (fs20.statSync(uri).isDirectory()) { logger2.debug(`Unable to compute fingerprint for directory: ${uri}`); return void 0; } @@ -179627,7 +179634,7 @@ async function addFingerprints(sarifLog, sourceRoot, logger2) { } // src/init.ts -var fs20 = __toESM(require("fs")); +var fs21 = __toESM(require("fs")); var path18 = __toESM(require("path")); var toolrunner4 = __toESM(require_toolrunner()); var github3 = __toESM(require_github()); @@ -179662,7 +179669,7 @@ async function initConfig2(actionState, inputs) { }); } async function runDatabaseInitCluster(databaseInitEnvironment, codeql, config, sourceRoot, processName, qlconfigFile) { - fs20.mkdirSync(config.dbLocation, { recursive: true }); + fs21.mkdirSync(config.dbLocation, { recursive: true }); await wrapEnvironment( databaseInitEnvironment, async () => await codeql.databaseInitCluster( @@ -179697,24 +179704,24 @@ async function checkPacksForOverlayCompatibility(codeql, config, logger2) { function checkPackForOverlayCompatibility(packDir, codeQlOverlayVersion, logger2) { try { let qlpackPath = path18.join(packDir, "qlpack.yml"); - if (!fs20.existsSync(qlpackPath)) { + if (!fs21.existsSync(qlpackPath)) { qlpackPath = path18.join(packDir, "codeql-pack.yml"); } const qlpackContents = load( - fs20.readFileSync(qlpackPath, "utf8") + fs21.readFileSync(qlpackPath, "utf8") ); if (!qlpackContents.buildMetadata) { return true; } const packInfoPath = path18.join(packDir, ".packinfo"); - if (!fs20.existsSync(packInfoPath)) { + if (!fs21.existsSync(packInfoPath)) { logger2.warning( `The query pack at ${packDir} does not have a .packinfo file, so it cannot support overlay analysis. Recompiling the query pack with the latest CodeQL CLI should solve this problem.` ); return false; } const packInfoFileContents = JSON.parse( - fs20.readFileSync(packInfoPath, "utf8") + fs21.readFileSync(packInfoPath, "utf8") ); const packOverlayVersion = packInfoFileContents.overlayVersion; if (typeof packOverlayVersion !== "number") { @@ -179749,8 +179756,8 @@ async function checkInstallPython311(languages, codeql) { ]).exec(); } } -function cleanupDatabaseClusterDirectory(config, logger2, options = {}, rmSync5 = fs20.rmSync) { - if (fs20.existsSync(config.dbLocation) && (fs20.statSync(config.dbLocation).isFile() || fs20.readdirSync(config.dbLocation).length > 0)) { +function cleanupDatabaseClusterDirectory(config, logger2, options = {}, rmSync5 = fs21.rmSync) { + if (fs21.existsSync(config.dbLocation) && (fs21.statSync(config.dbLocation).isFile() || fs21.readdirSync(config.dbLocation).length > 0)) { if (!options.disableExistingDirectoryWarning) { logger2.warning( `The database cluster directory ${config.dbLocation} must be empty. Attempting to clean it up.` @@ -179855,7 +179862,7 @@ To opt out of this change, ${envVarOptOut}`; } // src/sarif/index.ts -var fs21 = __toESM(require("fs")); +var fs22 = __toESM(require("fs")); var InvalidSarifUploadError = class extends Error { }; function getToolNames(sarifFile) { @@ -179871,7 +179878,7 @@ function getToolNames(sarifFile) { } function readSarifFile(sarifFilePath) { try { - return JSON.parse(fs21.readFileSync(sarifFilePath, "utf8")); + return JSON.parse(fs22.readFileSync(sarifFilePath, "utf8")); } catch (err) { throw new Error( `Parsing SARIF file at '${sarifFilePath}' failed: ${getErrorMessage(err)}` @@ -180013,8 +180020,8 @@ async function combineSarifFilesUsingCLI(sarifFiles, gitHubVersion, features, lo codeQL = initCodeQLResult.codeql; } const baseTempDir = path19.resolve(tempDir, "combined-sarif"); - fs22.mkdirSync(baseTempDir, { recursive: true }); - const outputDirectory = fs22.mkdtempSync(path19.resolve(baseTempDir, "output-")); + fs23.mkdirSync(baseTempDir, { recursive: true }); + const outputDirectory = fs23.mkdtempSync(path19.resolve(baseTempDir, "output-")); const outputFile = path19.resolve(outputDirectory, "combined-sarif.sarif"); await codeQL.mergeResults(sarifFiles, outputFile, { mergeRunsFromEqualCategory: true @@ -180056,7 +180063,7 @@ async function uploadPayload(payload, repositoryNwo, logger2, analysis) { `SARIF upload disabled by an environment variable. Saving to ${payloadSaveFile}` ); logger2.info(`Payload: ${JSON.stringify(payload, null, 2)}`); - fs22.writeFileSync(payloadSaveFile, JSON.stringify(payload, null, 2)); + fs23.writeFileSync(payloadSaveFile, JSON.stringify(payload, null, 2)); return "dummy-sarif-id"; } const client = getApiClient(); @@ -180090,7 +180097,7 @@ async function uploadPayload(payload, repositoryNwo, logger2, analysis) { function findSarifFilesInDir(sarifPath, isSarif) { const sarifFiles = []; const walkSarifFiles = (dir) => { - const entries = fs22.readdirSync(dir, { withFileTypes: true }); + const entries = fs23.readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { if (entry.isFile() && isSarif(entry.name)) { sarifFiles.push(path19.resolve(dir, entry.name)); @@ -180103,11 +180110,11 @@ function findSarifFilesInDir(sarifPath, isSarif) { return sarifFiles; } function getSarifFilePaths(sarifPath, isSarif) { - if (!fs22.existsSync(sarifPath)) { + if (!fs23.existsSync(sarifPath)) { throw new ConfigurationError(`Path does not exist: ${sarifPath}`); } let sarifFiles; - if (fs22.lstatSync(sarifPath).isDirectory()) { + if (fs23.lstatSync(sarifPath).isDirectory()) { sarifFiles = findSarifFilesInDir(sarifPath, isSarif); if (sarifFiles.length === 0) { throw new ConfigurationError( @@ -180120,7 +180127,7 @@ function getSarifFilePaths(sarifPath, isSarif) { return sarifFiles; } async function getGroupedSarifFilePaths(logger2, sarifPath) { - const stats = fs22.statSync(sarifPath, { throwIfNoEntry: false }); + const stats = fs23.statSync(sarifPath, { throwIfNoEntry: false }); if (stats === void 0) { throw new ConfigurationError(`Path does not exist: ${sarifPath}`); } @@ -180255,7 +180262,7 @@ function buildPayload(commitOid, ref, analysisKey, analysisName, zippedSarif, wo payloadObj.base_sha = mergeBaseCommitOid; } else if (process.env.GITHUB_EVENT_PATH) { const githubEvent = JSON.parse( - fs22.readFileSync(process.env.GITHUB_EVENT_PATH, "utf8") + fs23.readFileSync(process.env.GITHUB_EVENT_PATH, "utf8") ); payloadObj.base_ref = `refs/heads/${githubEvent.pull_request.base.ref}`; payloadObj.base_sha = githubEvent.pull_request.base.sha; @@ -180389,9 +180396,9 @@ async function uploadPostProcessedFiles(logger2, checkoutPath, uploadTarget, pos }; } function dumpSarifFile(sarifPayload, outputDir, logger2, uploadTarget) { - if (!fs22.existsSync(outputDir)) { - fs22.mkdirSync(outputDir, { recursive: true }); - } else if (!fs22.lstatSync(outputDir).isDirectory()) { + if (!fs23.existsSync(outputDir)) { + fs23.mkdirSync(outputDir, { recursive: true }); + } else if (!fs23.lstatSync(outputDir).isDirectory()) { throw new ConfigurationError( `The path that processed SARIF files should be written to exists, but is not a directory: ${outputDir}` ); @@ -180401,7 +180408,7 @@ function dumpSarifFile(sarifPayload, outputDir, logger2, uploadTarget) { `upload${uploadTarget.sarifExtension}` ); logger2.info(`Writing processed SARIF file to ${outputFile}`); - fs22.writeFileSync(outputFile, sarifPayload); + fs23.writeFileSync(outputFile, sarifPayload); } var STATUS_CHECK_INITIAL_BACKOFF_MILLISECONDS = 5 * 1e3; var STATUS_CHECK_BACKOFF_MULTIPLIER = 2; @@ -180641,7 +180648,7 @@ function doesGoExtractionOutputExist(config) { "trap", "go" /* go */ ); - return fs23.existsSync(trapDirectory) && fs23.readdirSync(trapDirectory).some( + return fs24.existsSync(trapDirectory) && fs24.readdirSync(trapDirectory).some( (fileName) => [ ".trap", ".trap.gz", @@ -180945,18 +180952,18 @@ async function runWrapper() { } // src/analyze-action-post.ts -var fs27 = __toESM(require("fs")); +var fs28 = __toESM(require("fs")); var core16 = __toESM(require_core()); // src/debug-artifacts.ts -var fs26 = __toESM(require("fs")); +var fs27 = __toESM(require("fs")); var path23 = __toESM(require("path")); var artifact = __toESM(require_artifact2()); var artifactLegacy = __toESM(require_artifact_client2()); var core15 = __toESM(require_core()); // node_modules/archiver/lib/core.js -var import_fs2 = require("fs"); +var import_fs = require("fs"); // node_modules/is-stream/index.js function isStream(stream2, { checkOpen = true } = {}) { @@ -180964,7 +180971,7 @@ function isStream(stream2, { checkOpen = true } = {}) { } // node_modules/readdir-glob/dist/index.mjs -var fs24 = __toESM(require("fs"), 1); +var fs25 = __toESM(require("fs"), 1); var import_events = require("events"); // node_modules/readdir-glob/node_modules/balanced-match/dist/esm/index.js @@ -182841,7 +182848,7 @@ minimatch.unescape = unescape2; var import_path6 = require("path"); function readdir2(dir, strict) { return new Promise((resolve$1, reject) => { - fs24.readdir(dir, { withFileTypes: true }, (err, files) => { + fs25.readdir(dir, { withFileTypes: true }, (err, files) => { if (err) switch (err.code) { case "ENOTDIR": if (strict) reject(err); @@ -182864,7 +182871,7 @@ function readdir2(dir, strict) { } function getStat(file, followSymlinks) { return new Promise((resolve$1) => { - const statFunc = followSymlinks ? fs24.stat : fs24.lstat; + const statFunc = followSymlinks ? fs25.stat : fs25.lstat; statFunc(file, (err, stats) => { if (err) switch (err.code) { case "ENOENT": @@ -183172,7 +183179,7 @@ var Archiver = class extends import_readable_stream2.Transform { data.sourcePath = filepath; task.data = data; this._entriesCount++; - if (data.stats && data.stats instanceof import_fs2.Stats) { + if (data.stats && data.stats instanceof import_fs.Stats) { task = this._updateQueueTaskWithStats(task, data.stats); if (task) { if (data.stats.size) { @@ -183420,7 +183427,7 @@ var Archiver = class extends import_readable_stream2.Transform { callback(); return; } - (0, import_fs2.lstat)( + (0, import_fs.lstat)( task.filepath, function(err, stats) { if (this._state.aborted) { @@ -183482,7 +183489,7 @@ var Archiver = class extends import_readable_stream2.Transform { task.data.type = "file"; task.data.sourceType = "stream"; task.source = new import_lazystream.Readable(function() { - return (0, import_fs2.createReadStream)(task.filepath); + return (0, import_fs.createReadStream)(task.filepath); }); } else if (stats.isDirectory() && this._supportsDirectory) { task.data.name = trailingSlashIt(task.data.name); @@ -183491,7 +183498,7 @@ var Archiver = class extends import_readable_stream2.Transform { task.data.sourceType = "buffer"; task.source = Buffer.concat([]); } else if (stats.isSymbolicLink() && this._supportsSymlink) { - const linkPath = (0, import_fs2.readlinkSync)(task.filepath); + const linkPath = (0, import_fs.readlinkSync)(task.filepath); const dirName = (0, import_path7.dirname)(task.filepath); task.data.type = "symlink"; task.data.linkname = (0, import_path7.relative)( @@ -185334,7 +185341,7 @@ var ZipArchive = class extends Archiver { }; // src/artifact-scanner.ts -var fs25 = __toESM(require("fs")); +var fs26 = __toESM(require("fs")); var os6 = __toESM(require("os")); var path22 = __toESM(require("path")); var exec = __toESM(require_exec()); @@ -185381,7 +185388,7 @@ function isAuthToken(value, patterns = GITHUB_TOKEN_PATTERNS) { function scanFileForTokens(filePath, relativePath2, logger2) { const findings = []; try { - const content = fs25.readFileSync(filePath, "utf8"); + const content = fs26.readFileSync(filePath, "utf8"); for (const { type, pattern } of GITHUB_TOKEN_PATTERNS) { const matches = content.match(pattern); if (matches) { @@ -185414,7 +185421,7 @@ async function scanArchiveFile(archivePath, relativeArchivePath, extractDir, log findings: [] }; try { - const tempExtractDir = fs25.mkdtempSync( + const tempExtractDir = fs26.mkdtempSync( path22.join(extractDir, `extract-${depth}-`) ); const fileName = path22.basename(archivePath).toLowerCase(); @@ -185448,7 +185455,7 @@ async function scanArchiveFile(archivePath, relativeArchivePath, extractDir, log path22.basename(archivePath, ".gz") ); await exec.exec("gunzip", ["-c", archivePath], { - outStream: fs25.createWriteStream(outputFile), + outStream: fs26.createWriteStream(outputFile), silent: true }); } else if (fileName.endsWith(".zip")) { @@ -185469,7 +185476,7 @@ async function scanArchiveFile(archivePath, relativeArchivePath, extractDir, log ); result.scannedFiles += scanResult.scannedFiles; result.findings.push(...scanResult.findings); - fs25.rmSync(tempExtractDir, { recursive: true, force: true }); + fs26.rmSync(tempExtractDir, { recursive: true, force: true }); } catch (e) { logger2.debug( `Could not extract or scan archive file ${archivePath}: ${getErrorMessage(e)}` @@ -185504,7 +185511,7 @@ async function scanDirectory(dirPath, baseRelativePath, logger2, depth = 0) { scannedFiles: 0, findings: [] }; - const entries = fs25.readdirSync(dirPath, { withFileTypes: true }); + const entries = fs26.readdirSync(dirPath, { withFileTypes: true }); for (const entry of entries) { const fullPath = path22.join(dirPath, entry.name); const relativePath2 = path22.join(baseRelativePath, entry.name); @@ -185539,10 +185546,10 @@ async function scanArtifactsForTokens(filesToScan, logger2) { scannedFiles: 0, findings: [] }; - const tempScanDir = fs25.mkdtempSync(path22.join(os6.tmpdir(), "artifact-scan-")); + const tempScanDir = fs26.mkdtempSync(path22.join(os6.tmpdir(), "artifact-scan-")); try { for (const filePath of filesToScan) { - const stats = fs25.statSync(filePath); + const stats = fs26.statSync(filePath); const fileName = path22.basename(filePath); if (stats.isDirectory()) { const dirResult = await scanDirectory(filePath, fileName, logger2); @@ -185580,7 +185587,7 @@ async function scanArtifactsForTokens(filesToScan, logger2) { } } finally { try { - fs25.rmSync(tempScanDir, { recursive: true, force: true }); + fs26.rmSync(tempScanDir, { recursive: true, force: true }); } catch (e) { logger2.debug( `Could not clean up temporary scan directory: ${getErrorMessage(e)}` @@ -185602,10 +185609,10 @@ async function uploadCombinedSarifArtifacts(logger2, gitHubVariant) { ); const baseTempDir = path23.resolve(tempDir, "combined-sarif"); const toUpload = []; - if (fs26.existsSync(baseTempDir)) { - const outputDirs = fs26.readdirSync(baseTempDir); + if (fs27.existsSync(baseTempDir)) { + const outputDirs = fs27.readdirSync(baseTempDir); for (const outputDir of outputDirs) { - const sarifFiles = fs26.readdirSync(path23.resolve(baseTempDir, outputDir)).filter((f) => path23.extname(f) === ".sarif"); + const sarifFiles = fs27.readdirSync(path23.resolve(baseTempDir, outputDir)).filter((f) => path23.extname(f) === ".sarif"); for (const sarifFile of sarifFiles) { toUpload.push(path23.resolve(baseTempDir, outputDir, sarifFile)); } @@ -185632,17 +185639,17 @@ async function uploadCombinedSarifArtifacts(logger2, gitHubVariant) { function tryPrepareSarifDebugArtifact(config, language, logger2) { try { const analyzeActionOutputDir = process.env["CODEQL_ACTION_SARIF_RESULTS_OUTPUT_DIR" /* SARIF_RESULTS_OUTPUT_DIR */]; - if (analyzeActionOutputDir !== void 0 && fs26.existsSync(analyzeActionOutputDir) && fs26.lstatSync(analyzeActionOutputDir).isDirectory()) { + if (analyzeActionOutputDir !== void 0 && fs27.existsSync(analyzeActionOutputDir) && fs27.lstatSync(analyzeActionOutputDir).isDirectory()) { const sarifFile = path23.resolve( analyzeActionOutputDir, `${language}.sarif` ); - if (fs26.existsSync(sarifFile)) { + if (fs27.existsSync(sarifFile)) { const sarifInDbLocation = path23.resolve( config.dbLocation, `${language}.sarif` ); - fs26.copyFileSync(sarifFile, sarifInDbLocation); + fs27.copyFileSync(sarifFile, sarifInDbLocation); return sarifInDbLocation; } } @@ -185810,10 +185817,10 @@ async function createPartialDatabaseBundle(config, language) { core15.info( `${config.debugDatabaseName}-${language} is not finalized. Uploading partial database bundle at ${databaseBundlePath}...` ); - if (fs26.existsSync(databaseBundlePath)) { - await fs26.promises.rm(databaseBundlePath, { force: true }); + if (fs27.existsSync(databaseBundlePath)) { + await fs27.promises.rm(databaseBundlePath, { force: true }); } - const output = fs26.createWriteStream(databaseBundlePath); + const output = fs27.createWriteStream(databaseBundlePath); const zip = new ZipArchive(); zip.on("error", (err) => { throw err; @@ -185863,9 +185870,9 @@ async function runWrapper2() { getCsharpTempDependencyDir() ]; for (const tempDependencyDir of tempDependencyDirs) { - if (fs27.existsSync(tempDependencyDir)) { + if (fs28.existsSync(tempDependencyDir)) { try { - fs27.rmSync(tempDependencyDir, { recursive: true }); + fs28.rmSync(tempDependencyDir, { recursive: true }); } catch (error3) { logger2.info( `Failed to remove temporary dependencies directory: ${getErrorMessage(error3)}` @@ -185976,7 +185983,7 @@ async function runWrapper3() { } // src/init-action.ts -var fs29 = __toESM(require("fs")); +var fs30 = __toESM(require("fs")); var path25 = __toESM(require("path")); var core19 = __toESM(require_core()); var io7 = __toESM(require_io()); @@ -186035,7 +186042,7 @@ function createInitToolsDownloadFields(report, toolsFeatureFlagsValid) { } // src/workflow.ts -var fs28 = __toESM(require("fs")); +var fs29 = __toESM(require("fs")); var path24 = __toESM(require("path")); var import_zlib3 = __toESM(require("zlib")); var core18 = __toESM(require_core()); @@ -186187,7 +186194,7 @@ async function getWorkflow(logger2) { ); } const workflowPath = await getWorkflowAbsolutePath(logger2); - return load(fs28.readFileSync(workflowPath, "utf-8")); + return load(fs29.readFileSync(workflowPath, "utf-8")); } async function getWorkflowAbsolutePath(logger2) { const relativePath2 = await getWorkflowRelativePath(); @@ -186195,7 +186202,7 @@ async function getWorkflowAbsolutePath(logger2) { getRequiredEnvParam("GITHUB_WORKSPACE"), relativePath2 ); - if (fs28.existsSync(absolutePath)) { + if (fs29.existsSync(absolutePath)) { logger2.debug( `Derived the following absolute path for the currently executing workflow: ${absolutePath}.` ); @@ -186586,7 +186593,7 @@ async function run3(actionState) { if (statusReportBase !== void 0) { await sendStatusReport(statusReportBase); } - logTemporaryDiagnostics(); + logTemporaryDiagnostics(actionState); return; } let overlayBaseDatabaseStats; @@ -186634,16 +186641,16 @@ async function run3(actionState) { "codeql-action-go-tracing", "bin" ); - fs29.mkdirSync(tempBinPath, { recursive: true }); + fs30.mkdirSync(tempBinPath, { recursive: true }); core19.addPath(tempBinPath); const goWrapperPath = path25.resolve(tempBinPath, "go"); - fs29.writeFileSync( + fs30.writeFileSync( goWrapperPath, `#!/bin/bash exec ${goBinaryPath} "$@"` ); - fs29.chmodSync(goWrapperPath, "755"); + fs30.chmodSync(goWrapperPath, "755"); actionState.env.export("CODEQL_ACTION_GO_BINARY" /* GO_BINARY_LOCATION */, goWrapperPath); } catch (e) { logger2.warning( @@ -186772,7 +186779,7 @@ exec ${goBinaryPath} "$@"` `${existingJavaToolOptions} -Djavax.net.debug=all` ); } - flushDiagnostics(config); + flushDiagnostics(actionState, config); await saveConfig(config, logger2); core19.setOutput("codeql-path", config.codeQLCmd); core19.setOutput("codeql-version", (await codeql.getVersion()).version); @@ -186796,7 +186803,7 @@ exec ${goBinaryPath} "$@"` ); return; } finally { - logTemporaryDiagnostics(); + logTemporaryDiagnostics(actionState); } await sendCompletedStatusReport2( startedAt, @@ -186825,7 +186832,7 @@ async function runWrapper4() { var core20 = __toESM(require_core()); // src/init-action-post-helper.ts -var fs30 = __toESM(require("fs")); +var fs31 = __toESM(require("fs")); var import_path8 = __toESM(require("path")); var github4 = __toESM(require_github()); function createFailedUploadFailedSarifResult(error3) { @@ -187009,7 +187016,7 @@ async function uploadFailureInfo(uploadAllAvailableDebugArtifacts, printDebugLog } if (isSelfHostedRunner() && config.dbLocation) { try { - fs30.rmSync(config.dbLocation, { + fs31.rmSync(config.dbLocation, { recursive: true, force: true, maxRetries: 3 @@ -187937,7 +187944,7 @@ function generateCertificateAuthority() { } // src/start-proxy/environment.ts -var fs31 = __toESM(require("fs")); +var fs32 = __toESM(require("fs")); var path28 = __toESM(require("path")); var toolrunner5 = __toESM(require_toolrunner()); var io8 = __toESM(require_io()); @@ -188010,9 +188017,9 @@ function checkJdkSettings(logger2, jdkHome) { for (const fileToCheck of filesToCheck) { const file = path28.join(jdkHome, fileToCheck); try { - if (fs31.existsSync(file)) { + if (fs32.existsSync(file)) { logger2.debug(`Found '${file}'.`); - const lines = String(fs31.readFileSync(file)).split("\n"); + const lines = String(fs32.readFileSync(file)).split("\n"); for (const line of lines) { for (const property of javaProperties) { if (line.startsWith(`${property}=`)) { diff --git a/src/diagnostics.test.ts b/src/diagnostics.test.ts new file mode 100644 index 0000000000..80bb1b32d9 --- /dev/null +++ b/src/diagnostics.test.ts @@ -0,0 +1,111 @@ +import * as fs from "fs"; +import path from "path"; + +import test, { type ExecutionContext } from "ava"; + +import * as diagnostics from "./diagnostics"; +import { BuiltInLanguage } from "./languages"; +import { + checkExpectedLogMessages, + createTestConfig, + initAllState, + RecordingLogger, + setupTests, +} from "./testing-utils"; +import { withTmpDir } from "./util"; + +setupTests(test); + +const id = "codeql-action/test-diagnostic"; +const name = "Test title"; + +test("makeDiagnostic - adds expected properties", (t) => { + const diagnostic = diagnostics.makeDiagnostic(id, name, {}); + + t.truthy(diagnostic.timestamp); + t.is(diagnostic.source.id, id); + t.is(diagnostic.source.name, name); +}); + +/** + * Diagnostic files that are written to disk don't have deterministic names. + * This function recursively finds all `.json` files at {@link rootPath}, + * asserts that there is exactly one, and that it matches {@link expected}. + * + * @param rootPath The path to start searching from. + */ +function assertSingleDiagnosticIn( + t: ExecutionContext, + rootPath: string, + expected: diagnostics.DiagnosticMessage, +) { + // Find all `.json` files in `rootPath` and check that there is exactly one. + const filePaths = fs + .readdirSync(rootPath, { + recursive: true, + encoding: "utf-8", + }) + .filter((p) => p.endsWith(".json")); + + t.is(filePaths.length, 1); + + // Check that the written diagnostic matches the one we added. + const contents = fs.readFileSync( + path.resolve(rootPath, filePaths[0]), + "utf-8", + ); + + const readDiagnostic = JSON.parse(contents) as diagnostics.DiagnosticMessage; + t.deepEqual(readDiagnostic, expected); +} + +test("addDiagnostic writes temporary diagnostics and flushDiagnostics moves them to the database", async (t) => + withTmpDir(async (tmpDir) => { + const logger = new RecordingLogger(); + + const tempDiagDir = path.resolve(tmpDir, "temp"); + const testDbDir = path.resolve(tmpDir, "test", "database"); + const diagnostic = diagnostics.makeDiagnostic(id, name, {}); + + // Write the diagnostic. The path provided for `dbLocation` doesn't exist, + // so the diagnostic will be written to a temporary location. + t.notThrows(() => { + diagnostics.addDiagnostic( + createTestConfig({ tempDir: tempDiagDir, dbLocation: testDbDir }), + BuiltInLanguage.actions, + diagnostic, + logger, + ); + }); + + // Check that `tempDiagDir` was created. + t.true(fs.existsSync(tempDiagDir)); + + // Check that the diagnostic exists under `tempDiagDir`. + assertSingleDiagnosticIn(t, tempDiagDir, diagnostic); + + // Flush the diagnostics. This will cause the diagnostic we wrote previously to + // be moved to a location under `dbLocation`. + t.notThrows(() => { + diagnostics.flushDiagnostics( + initAllState({ logger }), + createTestConfig({ + tempDir: tempDiagDir, + dbLocation: testDbDir, + }), + ); + }); + + // Check that `testDbDir` was created. + t.true(fs.existsSync(testDbDir)); + + // Check that the diagnostic exists under `testDbDir`. + assertSingleDiagnosticIn(t, testDbDir, diagnostic); + + // Check that we logged that the database directory doesn't exist yet + // and that we moved one diagnostic. + checkExpectedLogMessages(t, logger.messages, [ + `but the database at ${testDbDir}/actions does not exist yet`, + "Moving 1 diagnostic(s) to their respective databases.", + ]); + })); diff --git a/src/diagnostics.ts b/src/diagnostics.ts index f08c83c420..18996ca0be 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -1,17 +1,12 @@ -import { - existsSync, - mkdirSync, - readFileSync, - renameSync, - writeFileSync, -} from "fs"; +import * as fs from "fs"; import path from "path"; +import type { ActionState } from "./action-common"; import { getTemporaryDirectory } from "./actions-util"; import type { Config } from "./config-utils"; import { Language } from "./languages"; -import { getActionsLogger } from "./logging"; -import { getCodeQLDatabasePath } from "./util"; +import { getActionsLogger, type Logger } from "./logging"; +import { getCodeQLDatabasePath, getErrorMessage } from "./util"; /** * Known tags for diagnostics. There is currently only "internal-error", @@ -164,16 +159,16 @@ export function addDiagnostic( config: Config, language: Language, diagnostic: DiagnosticMessage, + logger: Logger = getActionsLogger(), ) { - const logger = getActionsLogger(); const databasePath = language ? getCodeQLDatabasePath(config, language) : config.dbLocation; // Check that the database exists before writing to it. If the database does not yet exist, // store the diagnostic in memory and write it later. - if (existsSync(databasePath)) { - writeDiagnostic(config, language, diagnostic); + if (fs.existsSync(databasePath)) { + writeDiagnostic({ logger }, config, language, diagnostic); } else { logger.debug( `Writing a diagnostic for ${language}, but the database at ${databasePath} does not exist yet.`, @@ -218,7 +213,7 @@ function writeDiagnosticFile( diagnostic: DiagnosticMessage, ): string { // Create the directory if it doesn't exist yet. - mkdirSync(diagnosticsPath, { recursive: true }); + fs.mkdirSync(diagnosticsPath, { recursive: true }); // Include a monotonically increasing suffix to avoid filename collisions // between diagnostics produced within the same millisecond. @@ -234,7 +229,7 @@ function writeDiagnosticFile( `codeql-action-${sanitizedTimestamp}-${uniqueSuffix}.json`, ); - writeFileSync(jsonPath, JSON.stringify(diagnostic)); + fs.writeFileSync(jsonPath, JSON.stringify(diagnostic)); return jsonPath; } @@ -255,46 +250,49 @@ export function getDatabaseDiagnosticsPath( * @param diagnostic The diagnostic message to add to the database. */ function writeDiagnostic( + action: ActionState<["Logger"]>, config: Config, language: Language | undefined, diagnostic: DiagnosticMessage, ) { - const logger = getActionsLogger(); const diagnosticsPath = getDatabaseDiagnosticsPath(config, language); try { writeDiagnosticFile(diagnosticsPath, diagnostic); } catch (err) { - logger.warning(`Unable to write diagnostic message to database: ${err}`); - logger.debug(JSON.stringify(diagnostic)); + action.logger.warning( + `Unable to write diagnostic message to database: ${err}`, + ); + action.logger.debug(JSON.stringify(diagnostic)); } } /** Report if there are temporary diagnostics and write them to the log. */ -export function logTemporaryDiagnostics() { - const logger = getActionsLogger(); +export function logTemporaryDiagnostics(action: ActionState<["Logger"]>) { const num = temporaryDiagnostics.length; if (num > 0) { - logger.warning( + action.logger.warning( `${num} diagnostic(s) could not be written to the database and will not appear on the Tool Status Page.`, ); for (const temporary of temporaryDiagnostics) { - logger.debug(readFileSync(temporary.path, "utf-8")); + action.logger.debug(fs.readFileSync(temporary.path, "utf-8")); } } } /** Relocates all temporary diagnostics to the respective databases. */ -export function flushDiagnostics(config: Config) { - const logger = getActionsLogger(); - +export function flushDiagnostics( + action: ActionState<["Logger"]>, + config: Config, +) { const diagnosticsCount = temporaryDiagnostics.length; - logger.debug( + action.logger.debug( `Moving ${diagnosticsCount} diagnostic(s) to their respective databases.`, ); + const failedFlushDiagnostics: TemporaryDiagnostic[] = []; for (const temporary of temporaryDiagnostics) { // If `temporary.language` is `undefined`, then we didn't have a `config` at the time that // `addNoLanguageDiagnostic` was called. In that case, we arbitrarily choose the first @@ -306,11 +304,20 @@ export function flushDiagnostics(config: Config) { const filename = path.basename(temporary.path); const destination = path.join(directory, filename); - renameSync(temporary.path, destination); + try { + fs.mkdirSync(directory, { recursive: true }); + fs.renameSync(temporary.path, destination); + } catch (err) { + action.logger.warning( + `Failed to rename '${temporary.path}' to '${destination}': ${getErrorMessage(err)}`, + ); + + failedFlushDiagnostics.push(temporary); + } } // Reset the temporary diagnostics arrays. - temporaryDiagnostics = []; + temporaryDiagnostics = failedFlushDiagnostics; } /** diff --git a/src/init-action.ts b/src/init-action.ts index 73ffcd6f1c..4f4c193e09 100644 --- a/src/init-action.ts +++ b/src/init-action.ts @@ -484,7 +484,7 @@ async function run( if (statusReportBase !== undefined) { await sendStatusReport(statusReportBase); } - logTemporaryDiagnostics(); + logTemporaryDiagnostics(actionState); return; } @@ -769,7 +769,7 @@ async function run( // Move diagnostics to the database that we previously kept in a temporary location // because the database did not exist until now - flushDiagnostics(config); + flushDiagnostics(actionState, config); // We save the config here instead of at the end of `initConfig` because we // may have updated the config returned from `initConfig`, e.g. to revert to @@ -798,7 +798,7 @@ async function run( ); return; } finally { - logTemporaryDiagnostics(); + logTemporaryDiagnostics(actionState); } await sendCompletedStatusReport( startedAt, From e0fedcf8772ca35260797185f062ef7509316141 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 9 Oct 2026 22:40:36 +0100 Subject: [PATCH 04/14] Use temporary diagnostic dir in `generateFailedSarif` --- lib/entry-points.js | 11 +++++++++-- src/codeql.ts | 5 +++++ src/init-action-post-helper.ts | 15 ++++++++++++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index 743fb27081..da2a3ce7db 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -177363,7 +177363,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", @@ -177372,6 +177372,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) { @@ -186902,7 +186903,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 68273d27b5..6a0440321f 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.ts b/src/init-action-post-helper.ts index 270bc0f079..9bae34654c 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"; @@ -189,7 +190,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); From 9eeab4681898c5de3a16f3b33c515a0581876343 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Mon, 12 Oct 2026 13:58:12 +0100 Subject: [PATCH 05/14] Add e2e test --- .../workflows/__diagnostics-without-db.yml | 86 +++++++++++++++++++ pr-checks/checks/diagnostics-without-db.yml | 27 ++++++ pr-checks/diagnostics-without-db.ts | 48 +++++++++++ 3 files changed, 161 insertions(+) create mode 100644 .github/workflows/__diagnostics-without-db.yml create mode 100644 pr-checks/checks/diagnostics-without-db.yml create mode 100644 pr-checks/diagnostics-without-db.ts diff --git a/.github/workflows/__diagnostics-without-db.yml b/.github/workflows/__diagnostics-without-db.yml new file mode 100644 index 0000000000..9c5b64d22e --- /dev/null +++ b/.github/workflows/__diagnostics-without-db.yml @@ -0,0 +1,86 @@ +# Warning: This file is generated automatically, and should not be modified. +# Instead, please modify the template in the pr-checks directory and run: +# pr-checks/sync.sh +# to regenerate this file. + +name: PR Check - Diagnostic export without database +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GO111MODULE: auto +on: + push: + branches: + - main + - releases/v* + pull_request: {} + merge_group: + types: + - checks_requested + schedule: + - cron: '0 5 * * *' + workflow_dispatch: + inputs: {} + workflow_call: + inputs: {} +defaults: + run: + shell: bash +concurrency: + cancel-in-progress: ${{ github.event_name == 'pull_request' || false }} + group: diagnostics-without-db-${{github.ref}} +jobs: + diagnostics-without-db: + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + version: linked + name: Diagnostic export without database + if: github.triggering_actor != 'dependabot[bot]' + permissions: + contents: read + security-events: read + timeout-minutes: 45 + runs-on: ${{ matrix.os }} + steps: + # We deliberately need to make this step fail before the database is initialised, but after + # some diagnostic has been produced. The simplest way to do that right now is to force the + # use of a nightly release and then fail because we are attempting to analyse Swift on + # a non-macOS runner. + - name: Check out repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Install Node.js + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 20.x + cache: npm + - name: Install newer npm + run: npm install -g npm@11.19.1 + - name: Install dependencies + run: npm ci + - name: Prepare test + id: prepare-test + uses: ./.github/actions/prepare-test + with: + version: ${{ matrix.version }} + use-all-platform-bundle: 'false' + setup-kotlin: 'true' + - name: Initialise failing analysis + id: init + uses: ./../action/init + continue-on-error: true + env: + CODEQL_ACTION_FORCE_NIGHTLY: true + with: + languages: swift + tools: ${{ steps.prepare-test.outputs.tools-url }} + + - name: Check diagnostics appear on disk + env: + SARIF_PATH: '${{ runner.temp }}/codeql-action-temp-diagnostics/' + run: | + cd ../action + npx tsx ./pr-checks/diagnostics-without-db.ts + env: + CODEQL_ACTION_TEST_MODE: true diff --git a/pr-checks/checks/diagnostics-without-db.yml b/pr-checks/checks/diagnostics-without-db.yml new file mode 100644 index 0000000000..603fdd04c5 --- /dev/null +++ b/pr-checks/checks/diagnostics-without-db.yml @@ -0,0 +1,27 @@ +name: "Diagnostic export without database" +description: "Tests that temporary diagnostics are correctly included in the failed SARIF." +versions: + # Overriden by the FF below. + - linked +installNode: true +steps: + # We deliberately need to make this step fail before the database is initialised, but after + # some diagnostic has been produced. The simplest way to do that right now is to force the + # use of a nightly release and then fail because we are attempting to analyse Swift on + # a non-macOS runner. + - name: Initialise failing analysis + id: init + uses: ./../action/init + continue-on-error: true + env: + CODEQL_ACTION_FORCE_NIGHTLY: true + with: + languages: swift + tools: ${{ steps.prepare-test.outputs.tools-url }} + + - name: Check diagnostics appear on disk + env: + SARIF_PATH: "${{ runner.temp }}/codeql-action-temp-diagnostics/" + run: | + cd ../action + npx tsx ./pr-checks/diagnostics-without-db.ts diff --git a/pr-checks/diagnostics-without-db.ts b/pr-checks/diagnostics-without-db.ts new file mode 100644 index 0000000000..b742c10d68 --- /dev/null +++ b/pr-checks/diagnostics-without-db.ts @@ -0,0 +1,48 @@ +import * as fs from "fs"; +import * as path from "path"; + +import * as core from "@actions/core"; + +import type * as diagnostics from "../src/diagnostics"; + +const DIAGNOSTIC_ID = "codeql-action/forced-nightly-cli"; +const sarifDirectory = process.env["SARIF_PATH"]; + +if (sarifDirectory === undefined) { + throw new Error("SARIF_PATH is undefined."); +} + +const paths = fs + .readdirSync(sarifDirectory, { + recursive: true, + encoding: "utf-8", + }) + .filter((p) => p.endsWith(".json")); + +core.info(`Found ${paths.length} paths in '${sarifDirectory}'.`); +core.info(`Searching for a diagnostic with id '${DIAGNOSTIC_ID}'...`); + +let found = false; +for (const diagnosticPath of paths) { + if (path.extname(diagnosticPath) !== ".json") { + core.info(`Skipping ${diagnosticPath}.`); + continue; + } + core.info(`Found ${diagnosticPath}.`); + + const contents = JSON.parse( + fs.readFileSync(path.resolve(sarifDirectory, diagnosticPath), "utf-8"), + ) as diagnostics.DiagnosticMessage; + + if (contents.source.id === DIAGNOSTIC_ID) { + core.info( + `Found '${DIAGNOSTIC_ID}' in '${diagnosticPath}'. Skipping remaining files.`, + ); + found = true; + break; + } +} + +if (!found) { + throw new Error(`Didn't find '${DIAGNOSTIC_ID}' diagnostic.`); +} From 7089ca74ea22f0bb2f2785c57e07d0946844aec9 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Mon, 12 Oct 2026 16:08:41 +0100 Subject: [PATCH 06/14] Fix hardcoded path separator --- src/diagnostics.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/diagnostics.test.ts b/src/diagnostics.test.ts index 80bb1b32d9..2ba98da606 100644 --- a/src/diagnostics.test.ts +++ b/src/diagnostics.test.ts @@ -105,7 +105,7 @@ test("addDiagnostic writes temporary diagnostics and flushDiagnostics moves them // Check that we logged that the database directory doesn't exist yet // and that we moved one diagnostic. checkExpectedLogMessages(t, logger.messages, [ - `but the database at ${testDbDir}/actions does not exist yet`, + `but the database at ${path.join(testDbDir, "actions")} does not exist yet`, "Moving 1 diagnostic(s) to their respective databases.", ]); })); From ac89bf9d4699576a8cabff4adc5e3fc7803741e5 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Mon, 12 Oct 2026 16:09:49 +0100 Subject: [PATCH 07/14] Fix typos --- pr-checks/checks/diagnostics-without-db.yml | 2 +- src/diagnostics.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pr-checks/checks/diagnostics-without-db.yml b/pr-checks/checks/diagnostics-without-db.yml index 603fdd04c5..2985142b9a 100644 --- a/pr-checks/checks/diagnostics-without-db.yml +++ b/pr-checks/checks/diagnostics-without-db.yml @@ -1,7 +1,7 @@ name: "Diagnostic export without database" description: "Tests that temporary diagnostics are correctly included in the failed SARIF." versions: - # Overriden by the FF below. + # Overridden by the FF below. - linked installNode: true steps: diff --git a/src/diagnostics.ts b/src/diagnostics.ts index 18996ca0be..5a4d310bc1 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -137,7 +137,7 @@ export function getTempDiagnosticPath(tmpDir: string, language?: Language) { /** * Gets a path relative to {@link basePath} where diagnostics for {@link language} - * should be stored at. If {@link language} is `undefined`, then {@link baseBath} + * should be stored at. If {@link language} is `undefined`, then {@link basePath} * is returned instead. */ export function getDiagnosticsPath(basePath: string, language?: Language) { From 104d981b765f44866c854b8e3307779fbc41548b Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Mon, 12 Oct 2026 16:15:55 +0100 Subject: [PATCH 08/14] Refactor `writeTemporaryDiagnosticFile` out of `add[NoLanguage]Diagnostic` --- lib/entry-points.js | 17 +++++++++++------ src/diagnostics.ts | 34 ++++++++++++++++++++++++---------- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index da2a3ce7db..d30e9705ac 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -173005,9 +173005,7 @@ function addDiagnostic(config, language, diagnostic, logger2 = getActionsLogger( logger2.debug( `Writing a diagnostic for ${language}, but the database at ${databasePath} does not exist yet.` ); - const tempDiagnosticsPath = getTempDiagnosticPath(config.tempDir, language); - const diagnosticPath = writeDiagnosticFile(tempDiagnosticsPath, diagnostic); - temporaryDiagnostics.push({ path: diagnosticPath, language }); + writeTemporaryDiagnosticFile(config.tempDir, language, diagnostic); } } function addNoLanguageDiagnostic(config, diagnostic) { @@ -173020,11 +173018,18 @@ function addNoLanguageDiagnostic(config, diagnostic) { diagnostic ); } else { - const tempDiagnosticsPath = getTempDiagnosticPath(getTemporaryDirectory()); - const diagnosticPath = writeDiagnosticFile(tempDiagnosticsPath, diagnostic); - temporaryDiagnostics.push({ path: diagnosticPath }); + writeTemporaryDiagnosticFile( + getTemporaryDirectory(), + void 0, + diagnostic + ); } } +function writeTemporaryDiagnosticFile(tempDir, language, diagnostic) { + const tempDiagnosticsPath = getTempDiagnosticPath(tempDir, language); + const diagnosticPath = writeDiagnosticFile(tempDiagnosticsPath, diagnostic); + temporaryDiagnostics.push({ path: diagnosticPath, language }); +} function writeDiagnosticFile(diagnosticsPath, diagnostic) { fs7.mkdirSync(diagnosticsPath, { recursive: true }); const uniqueSuffix = (diagnosticCounter++).toString(); diff --git a/src/diagnostics.ts b/src/diagnostics.ts index 5a4d310bc1..34a4e33551 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -175,11 +175,7 @@ export function addDiagnostic( ); // Write the diagnostic to a temporary location. - const tempDiagnosticsPath = getTempDiagnosticPath(config.tempDir, language); - const diagnosticPath = writeDiagnosticFile(tempDiagnosticsPath, diagnostic); - - // Track the temporary file. - temporaryDiagnostics.push({ path: diagnosticPath, language }); + writeTemporaryDiagnosticFile(config.tempDir, language, diagnostic); } } @@ -197,14 +193,32 @@ export function addNoLanguageDiagnostic( diagnostic, ); } else { - const tempDiagnosticsPath = getTempDiagnosticPath(getTemporaryDirectory()); - const diagnosticPath = writeDiagnosticFile(tempDiagnosticsPath, diagnostic); - - // Track the temporary file. - temporaryDiagnostics.push({ path: diagnosticPath }); + // Write the diagnostic to a temporary location. + writeTemporaryDiagnosticFile( + getTemporaryDirectory(), + undefined, + diagnostic, + ); } } +/** + * Writes {@link diagnostic} for an optional {@link language} to a subdirectory of {@link tempDir} + * and tracks it as a diagnostic that was written to a temporary location. + */ +function writeTemporaryDiagnosticFile( + tempDir: string, + language: Language | undefined, + diagnostic: DiagnosticMessage, +) { + // Write the diagnostic to a temporary location. + const tempDiagnosticsPath = getTempDiagnosticPath(tempDir, language); + const diagnosticPath = writeDiagnosticFile(tempDiagnosticsPath, diagnostic); + + // Track the temporary file. + temporaryDiagnostics.push({ path: diagnosticPath, language }); +} + /** * Writes {@link diagnostic} to a file in {@link diagnosticsPath}. */ From 93b4ae6c0e598eac99db9657dc8b87fef3cb7319 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Mon, 12 Oct 2026 16:20:25 +0100 Subject: [PATCH 09/14] Handle failures in `writeTemporaryDiagnosticFile` --- lib/entry-points.js | 22 +++++++++++++++++----- src/diagnostics.ts | 24 +++++++++++++++++++----- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index d30e9705ac..a917d71526 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -173005,10 +173005,10 @@ function addDiagnostic(config, language, diagnostic, logger2 = getActionsLogger( logger2.debug( `Writing a diagnostic for ${language}, but the database at ${databasePath} does not exist yet.` ); - writeTemporaryDiagnosticFile(config.tempDir, language, diagnostic); + writeTemporaryDiagnosticFile(logger2, config.tempDir, language, diagnostic); } } -function addNoLanguageDiagnostic(config, diagnostic) { +function addNoLanguageDiagnostic(config, diagnostic, logger2 = getActionsLogger()) { if (config !== void 0) { addDiagnostic( config, @@ -173018,17 +173018,29 @@ function addNoLanguageDiagnostic(config, diagnostic) { diagnostic ); } else { + logger2.debug( + `Writing a diagnostic not associated with any language, but we have no configuration to tell us where the database is yet.` + ); writeTemporaryDiagnosticFile( + logger2, getTemporaryDirectory(), void 0, diagnostic ); } } -function writeTemporaryDiagnosticFile(tempDir, language, diagnostic) { +function writeTemporaryDiagnosticFile(logger2, tempDir, language, diagnostic) { const tempDiagnosticsPath = getTempDiagnosticPath(tempDir, language); - const diagnosticPath = writeDiagnosticFile(tempDiagnosticsPath, diagnostic); - temporaryDiagnostics.push({ path: diagnosticPath, language }); + try { + const diagnosticPath = writeDiagnosticFile(tempDiagnosticsPath, diagnostic); + temporaryDiagnostics.push({ path: diagnosticPath, language }); + } catch (err) { + logger2.warning( + `Unable to write temporary diagnostic to '${tempDiagnosticsPath}': ${getErrorMessage(err)} + +${JSON.stringify(diagnostic)}` + ); + } } function writeDiagnosticFile(diagnosticsPath, diagnostic) { fs7.mkdirSync(diagnosticsPath, { recursive: true }); diff --git a/src/diagnostics.ts b/src/diagnostics.ts index 34a4e33551..d0083e30e8 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -175,7 +175,7 @@ export function addDiagnostic( ); // Write the diagnostic to a temporary location. - writeTemporaryDiagnosticFile(config.tempDir, language, diagnostic); + writeTemporaryDiagnosticFile(logger, config.tempDir, language, diagnostic); } } @@ -183,6 +183,7 @@ export function addDiagnostic( export function addNoLanguageDiagnostic( config: Config | undefined, diagnostic: DiagnosticMessage, + logger: Logger = getActionsLogger(), ) { if (config !== undefined) { addDiagnostic( @@ -193,8 +194,13 @@ export function addNoLanguageDiagnostic( diagnostic, ); } else { + logger.debug( + `Writing a diagnostic not associated with any language, but we have no configuration to tell us where the database is yet.`, + ); + // Write the diagnostic to a temporary location. writeTemporaryDiagnosticFile( + logger, getTemporaryDirectory(), undefined, diagnostic, @@ -207,16 +213,24 @@ export function addNoLanguageDiagnostic( * and tracks it as a diagnostic that was written to a temporary location. */ function writeTemporaryDiagnosticFile( + logger: Logger, tempDir: string, language: Language | undefined, diagnostic: DiagnosticMessage, ) { - // Write the diagnostic to a temporary location. const tempDiagnosticsPath = getTempDiagnosticPath(tempDir, language); - const diagnosticPath = writeDiagnosticFile(tempDiagnosticsPath, diagnostic); - // Track the temporary file. - temporaryDiagnostics.push({ path: diagnosticPath, language }); + try { + // Write the diagnostic to a temporary location. + const diagnosticPath = writeDiagnosticFile(tempDiagnosticsPath, diagnostic); + + // Track the temporary file. + temporaryDiagnostics.push({ path: diagnosticPath, language }); + } catch (err) { + logger.warning( + `Unable to write temporary diagnostic to '${tempDiagnosticsPath}': ${getErrorMessage(err)}\n\n${JSON.stringify(diagnostic)}`, + ); + } } /** From 61b1d55aa528516806ad7fe403212924ddecdef3 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Mon, 12 Oct 2026 16:37:06 +0100 Subject: [PATCH 10/14] Address EXDEV issue --- lib/entry-points.js | 15 +++++++++++++-- src/diagnostics.ts | 21 ++++++++++++++++++--- src/util.ts | 15 +++++++++++++++ 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index a917d71526..22b76915ff 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -170021,6 +170021,9 @@ function wrapError(error3) { function getErrorMessage(error3) { return error3 instanceof Error ? error3.message : String(error3); } +function isErrnoException(error3) { + return typeof error3 === "object" && error3 !== null && "code" in error3 && typeof error3.code === "string"; +} function prettyPrintPack(pack) { return `${pack.name}${pack.version ? `@${pack.version}` : ""}${pack.path ? `:${pack.path}` : ""}`; } @@ -173096,10 +173099,18 @@ function flushDiagnostics(action, config) { const destination = import_path2.default.join(directory, filename); try { fs7.mkdirSync(directory, { recursive: true }); - fs7.renameSync(temporary.path, destination); + try { + fs7.renameSync(temporary.path, destination); + } catch (renameErr) { + if (!isErrnoException(renameErr) || renameErr.code !== "EXDEV") { + throw renameErr; + } + fs7.copyFileSync(temporary.path, destination); + fs7.unlinkSync(temporary.path); + } } catch (err) { action.logger.warning( - `Failed to rename '${temporary.path}' to '${destination}': ${getErrorMessage(err)}` + `Failed to relocate '${temporary.path}' to '${destination}': ${getErrorMessage(err)}` ); failedFlushDiagnostics.push(temporary); } diff --git a/src/diagnostics.ts b/src/diagnostics.ts index d0083e30e8..a3b44fd760 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -6,7 +6,11 @@ import { getTemporaryDirectory } from "./actions-util"; import type { Config } from "./config-utils"; import { Language } from "./languages"; import { getActionsLogger, type Logger } from "./logging"; -import { getCodeQLDatabasePath, getErrorMessage } from "./util"; +import { + getCodeQLDatabasePath, + getErrorMessage, + isErrnoException, +} from "./util"; /** * Known tags for diagnostics. There is currently only "internal-error", @@ -334,10 +338,21 @@ export function flushDiagnostics( try { fs.mkdirSync(directory, { recursive: true }); - fs.renameSync(temporary.path, destination); + try { + fs.renameSync(temporary.path, destination); + } catch (renameErr: unknown) { + // Rethrow the error if it isn't an EXDEV one. + if (!isErrnoException(renameErr) || renameErr.code !== "EXDEV") { + throw renameErr; + } + + // The error was an EXDEV one: try to copy the file and delete the temporary one. + fs.copyFileSync(temporary.path, destination); + fs.unlinkSync(temporary.path); + } } catch (err) { action.logger.warning( - `Failed to rename '${temporary.path}' to '${destination}': ${getErrorMessage(err)}`, + `Failed to relocate '${temporary.path}' to '${destination}': ${getErrorMessage(err)}`, ); failedFlushDiagnostics.push(temporary); diff --git a/src/util.ts b/src/util.ts index 566386de62..2cacd4fbba 100644 --- a/src/util.ts +++ b/src/util.ts @@ -924,6 +924,21 @@ export function getErrorMessage(error: unknown): string { return error instanceof Error ? error.message : String(error); } +/** + * Approximates whether {@link error} is like a {@link NodeJS.ErrnoException}. + * We only typically care about the `code` property, so that's what we check for here. + */ +export function isErrnoException( + error: unknown, +): error is NodeJS.ErrnoException { + return ( + typeof error === "object" && + error !== null && + "code" in error && + typeof error.code === "string" + ); +} + export function prettyPrintPack(pack: Pack) { return `${pack.name}${pack.version ? `@${pack.version}` : ""}${ pack.path ? `:${pack.path}` : "" From 9cd21186b171597a3b0af4e6ccd4d96fedf398cb Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Mon, 12 Oct 2026 16:56:51 +0100 Subject: [PATCH 11/14] Add test for new `generateFailedSarif` behaviour --- src/init-action-post-helper.test.ts | 45 +++++++++++++++++++++++++++++ src/init-action-post-helper.ts | 11 ++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/src/init-action-post-helper.test.ts b/src/init-action-post-helper.test.ts index fd1f489f7a..4291361c2d 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 9bae34654c..873c0afc8e 100644 --- a/src/init-action-post-helper.ts +++ b/src/init-action-post-helper.ts @@ -169,7 +169,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">, From bf0c20744ab3de7056a5c02d424176b05bf6020f Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Mon, 12 Oct 2026 17:23:44 +0100 Subject: [PATCH 12/14] Upload failed SARIF as workflow artifact in test mode --- lib/entry-points.js | 25 ++++++++++++++++++++----- src/init-action-post-helper.ts | 25 +++++++++++++++++++++++-- src/upload-lib.ts | 13 +++++++++---- 3 files changed, 52 insertions(+), 11 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index 22b76915ff..d446825a6a 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -178518,6 +178518,7 @@ __export(upload_lib_exports, { filterAlertsByDiffRange: () => filterAlertsByDiffRange, findSarifFilesInDir: () => findSarifFilesInDir, getGroupedSarifFilePaths: () => getGroupedSarifFilePaths, + getSavedPayloadPath: () => getSavedPayloadPath, populateRunAutomationDetails: () => populateRunAutomationDetails, postProcessSarifFiles: () => postProcessSarifFiles, readSarifFileOrThrow: () => readSarifFileOrThrow, @@ -180081,13 +180082,16 @@ function getAutomationID2(category, analysis_key, environment) { } return computeAutomationID(analysis_key, environment); } +function getSavedPayloadPath(analysisKind) { + return path19.join( + getTemporaryDirectory(), + `payload-${analysisKind}.json` + ); +} async function uploadPayload(payload, repositoryNwo, logger2, analysis) { logger2.info("Uploading results"); if (shouldSkipSarifUpload()) { - const payloadSaveFile = path19.join( - getTemporaryDirectory(), - `payload-${analysis.kind}.json` - ); + const payloadSaveFile = getSavedPayloadPath(analysis.kind); logger2.info( `SARIF upload disabled by an environment variable. Saving to ${payloadSaveFile}` ); @@ -186964,6 +186968,14 @@ async function maybeUploadFailedSarif(config, repositoryNwo, features, logger2) logger2, { isUnsuccessfulExecution: true } ); + if (uploadResult.sarifID && isInTestMode()) { + await uploadSarifArtifact(logger2, "test-mode-sarif", { + // We defaulted to CodeScanning in the `uploadFiles` call above. + sarifFile: getSavedPayloadPath(CodeScanning.kind), + category: failedSarif.category, + checkoutPath: failedSarif.checkoutPath + }); + } return uploadResult ? { ...uploadResult.statusReport, sarifID: uploadResult.sarifID } : {}; } async function maybeUploadFailedSarifArtifact(config, features, logger2) { @@ -186972,13 +186984,16 @@ async function maybeUploadFailedSarifArtifact(config, features, logger2) { return failedSarifResult.value; } const failedSarif = failedSarifResult.value; + return uploadSarifArtifact(logger2, "sarif-artifact", failedSarif); +} +async function uploadSarifArtifact(logger2, prefix, failedSarif) { logger2.info( `Uploading failed SARIF file ${failedSarif.sarifFile} as artifact` ); const gitHubVersion = await getGitHubVersion(); const client = await getArtifactUploaderClient(logger2, gitHubVersion.type); const suffix = getArtifactSuffix(getOptionalInput("matrix")); - const name = sanitizeArtifactName(`sarif-artifact-${suffix}`); + const name = sanitizeArtifactName(`${prefix}-${suffix}`); await client.uploadArtifact( name, [import_path8.default.normalize(failedSarif.sarifFile)], diff --git a/src/init-action-post-helper.ts b/src/init-action-post-helper.ts index 873c0afc8e..e736eb77b4 100644 --- a/src/init-action-post-helper.ts +++ b/src/init-action-post-helper.ts @@ -25,7 +25,7 @@ import { } from "./debug-artifacts"; import * as dependencyCaching from "./dependency-caching"; import { getTempDiagnosticPath } from "./diagnostics"; -import { EnvVar, ReadOnlyEnv } from "./environment"; +import { EnvVar, isInTestMode, ReadOnlyEnv } from "./environment"; import { Feature, FeatureEnablement } from "./feature-flags"; import { Logger } from "./logging"; import { OverlayDatabaseMode } from "./overlay/overlay-database-mode"; @@ -253,6 +253,19 @@ async function maybeUploadFailedSarif( logger, { isUnsuccessfulExecution: true }, ); + + // If we are in test mode, `uploadFiles` will not have uploaded the failed SARIF file + // and instead will have written it to disk. Upload it as a workflow artifact so that + // we can inspect it in validation jobs. + if (uploadResult.sarifID && isInTestMode()) { + await uploadSarifArtifact(logger, "test-mode-sarif", { + // We defaulted to CodeScanning in the `uploadFiles` call above. + sarifFile: uploadLib.getSavedPayloadPath(CodeScanning.kind), + category: failedSarif.category, + checkoutPath: failedSarif.checkoutPath, + }); + } + return uploadResult ? { ...uploadResult.statusReport, sarifID: uploadResult.sarifID } : {}; @@ -271,7 +284,15 @@ async function maybeUploadFailedSarifArtifact( } const failedSarif = failedSarifResult.value; + return uploadSarifArtifact(logger, "sarif-artifact", failedSarif); +} +/** Uploads a failed SARIF file as workflow artifact. */ +async function uploadSarifArtifact( + logger: Logger, + prefix: string, + failedSarif: FailedSarifInfo, +): Promise { logger.info( `Uploading failed SARIF file ${failedSarif.sarifFile} as artifact`, ); @@ -280,7 +301,7 @@ async function maybeUploadFailedSarifArtifact( const client = await getArtifactUploaderClient(logger, gitHubVersion.type); const suffix = getArtifactSuffix(actionsUtil.getOptionalInput("matrix")); - const name = sanitizeArtifactName(`sarif-artifact-${suffix}`); + const name = sanitizeArtifactName(`${prefix}-${suffix}`); await client.uploadArtifact( name, [path.normalize(failedSarif.sarifFile)], diff --git a/src/upload-lib.ts b/src/upload-lib.ts index 9792e0225e..23b7b91ef7 100644 --- a/src/upload-lib.ts +++ b/src/upload-lib.ts @@ -227,6 +227,14 @@ function getAutomationID( return api.computeAutomationID(analysis_key, environment); } +/** Gets the path to a saved payload file for {@link analysisKind}. */ +export function getSavedPayloadPath(analysisKind: analyses.AnalysisKind) { + return path.join( + actionsUtil.getTemporaryDirectory(), + `payload-${analysisKind}.json`, + ); +} + /** * Upload the given payload. * If the request fails then this will retry a small number of times. @@ -241,10 +249,7 @@ export async function uploadPayload( logger.info("Uploading results"); if (util.shouldSkipSarifUpload()) { - const payloadSaveFile = path.join( - actionsUtil.getTemporaryDirectory(), - `payload-${analysis.kind}.json`, - ); + const payloadSaveFile = getSavedPayloadPath(analysis.kind); logger.info( `SARIF upload disabled by an environment variable. Saving to ${payloadSaveFile}`, ); From a4fe152ba90db20c14b34d72ace24c27b6b0aaae Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Mon, 12 Oct 2026 17:24:21 +0100 Subject: [PATCH 13/14] Log when we didn't get a normal configuration file --- lib/entry-points.js | 3 +++ src/init-action-post.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/lib/entry-points.js b/lib/entry-points.js index d446825a6a..2edb5fc42c 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -187215,6 +187215,9 @@ async function run4(action) { const tmpDir = getTemporaryDirectory(); config = await getConfig(tmpDir, logger2); if (config === void 0) { + logger2.debug( + "Will try to load the configuration file from an aborted 'init' step, since there is no regular one." + ); config = getAbortedInitConfig(action, tmpDir); } const gitHubVersion = config?.gitHubVersion ?? await getGitHubVersion(); diff --git a/src/init-action-post.ts b/src/init-action-post.ts index dd981c200a..1fc509ed05 100644 --- a/src/init-action-post.ts +++ b/src/init-action-post.ts @@ -76,6 +76,9 @@ async function run(action: ActionState<["Base", "Logger", "Env"]>) { // Try to load the "failed" configuration file instead which may have been written by the `init` step // with a minimal configuration for this post-action. if (config === undefined) { + logger.debug( + "Will try to load the configuration file from an aborted 'init' step, since there is no regular one.", + ); config = getAbortedInitConfig(action, tmpDir); } From 9104ff3a11b7f60ce02990e82d017c3107ff58b1 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Mon, 12 Oct 2026 17:35:02 +0100 Subject: [PATCH 14/14] Scaffold validation job --- .../workflows/__diagnostics-without-db.yml | 33 ++++++++++++++++ pr-checks/checks/diagnostics-without-db.yml | 22 +++++++++++ pr-checks/sync.ts | 38 +++++++++++++------ 3 files changed, 81 insertions(+), 12 deletions(-) diff --git a/.github/workflows/__diagnostics-without-db.yml b/.github/workflows/__diagnostics-without-db.yml index 9c5b64d22e..80bf232d06 100644 --- a/.github/workflows/__diagnostics-without-db.yml +++ b/.github/workflows/__diagnostics-without-db.yml @@ -82,5 +82,38 @@ jobs: run: | cd ../action npx tsx ./pr-checks/diagnostics-without-db.ts + env: + CODEQL_ACTION_TEST_MODE: false + CODEQL_ACTION_SKIP_SARIF_UPLOAD: true + CODEQL_ACTION_TESTING_ENVIRONMENT: codeql-action-pr-checks + diagnostics-without-db-validation: + if: github.triggering_actor != 'dependabot[bot]' + needs: + - diagnostics-without-db + permissions: + contents: read + security-events: read + timeout-minutes: 5 + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Install Node.js + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 20.x + cache: npm + - name: Install newer npm + run: npm install -g npm@11.19.1 + - name: Install dependencies + run: npm ci + - name: Download workflow artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + pattern: test-mode-sarif + path: sarif + + - name: List artifact contents + run: ls -laR ./sarif env: CODEQL_ACTION_TEST_MODE: true diff --git a/pr-checks/checks/diagnostics-without-db.yml b/pr-checks/checks/diagnostics-without-db.yml index 2985142b9a..197b40e361 100644 --- a/pr-checks/checks/diagnostics-without-db.yml +++ b/pr-checks/checks/diagnostics-without-db.yml @@ -4,6 +4,15 @@ versions: # Overridden by the FF below. - linked installNode: true + +env: + # Allow the failed SARIF file to be generated. + CODEQL_ACTION_TEST_MODE: false + # But don't actually upload it. + CODEQL_ACTION_SKIP_SARIF_UPLOAD: true + # Mark telemetry for this workflow so it can be treated separately. + CODEQL_ACTION_TESTING_ENVIRONMENT: codeql-action-pr-checks + steps: # We deliberately need to make this step fail before the database is initialised, but after # some diagnostic has been produced. The simplest way to do that right now is to force the @@ -25,3 +34,16 @@ steps: run: | cd ../action npx tsx ./pr-checks/diagnostics-without-db.ts +validationJobs: + diagnostics-without-db-validation: + checkout: true + installNode: true + steps: + - name: Download workflow artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + pattern: test-mode-sarif + path: sarif + + - name: List artifact contents + run: ls -laR ./sarif diff --git a/pr-checks/sync.ts b/pr-checks/sync.ts index 8dc1dd1d1d..1d70d5c7f9 100755 --- a/pr-checks/sync.ts +++ b/pr-checks/sync.ts @@ -90,7 +90,7 @@ interface Specification extends JobSpecification { services?: any; /** Additional jobs to run after the main PR check job. */ - validationJobs?: Record; + validationJobs?: Record; /** If set, this check is part of a named collection that gets its own caller workflow. */ collection?: string; @@ -122,6 +122,10 @@ interface JobSpecification { installYq?: boolean; } +type ValidationJobSpecification = JobSpecification & { + checkout?: boolean; +}; + /** Describes language/framework-specific steps and inputs. */ interface LanguageSetup { specProperty: keyof JobSpecification; @@ -462,6 +466,20 @@ function generateJobMatrix( return matrix; } +/** + * Gets the `actions/checkout` step. + */ +function getCheckoutStep(): Step { + return { + name: "Check out repository", + uses: pinnedUses( + "actions/checkout", + "3d3c42e5aac5ba805825da76410c181273ba90b1", + "v7.0.1", + ), + }; +} + /** * Retrieves setup steps and additional input definitions based on specific languages or frameworks * that are requested by the `checkSpecification`. @@ -531,14 +549,7 @@ function generateJob( // Construct the workflow steps needed for this check. const steps: Step[] = [ - { - name: "Check out repository", - uses: pinnedUses( - "actions/checkout", - "3d3c42e5aac5ba805825da76410c181273ba90b1", - "v7.0.1", - ), - }, + getCheckoutStep(), ...setupInfo.steps, { name: "Prepare test", @@ -609,13 +620,16 @@ function generateJob( /** Generates a validation job. */ function generateValidationJob( specDocument: yaml.Document, - jobSpecification: JobSpecification, + jobSpecification: ValidationJobSpecification, checkName: string, name: string, ) { // Determine which languages or frameworks have to be installed. const { inputs, steps } = getSetupSteps(jobSpecification); + // Construct a checkout step if requested. + const checkoutStep = jobSpecification.checkout ? [getCheckoutStep()] : []; + // Extract the sequence of steps from the YAML document to persist as much formatting as possible. const specSteps = specDocument.getIn([ "validationJobs", @@ -624,7 +638,7 @@ function generateValidationJob( ]) as yaml.YAMLSeq; // Add the generated steps in front of the ones from the specification. - specSteps.items.unshift(...steps); + specSteps.items.unshift(...checkoutStep, ...steps); const validationJob: Record = { name: jobSpecification.name, @@ -635,7 +649,7 @@ function generateValidationJob( "security-events": "read", }, "timeout-minutes": 5, - "runs-on": "ubuntu-slim", + "runs-on": "ubuntu-latest", steps: specSteps, };