From 93d793c5fed140067c0ea5d5744725ec2a9ad05d Mon Sep 17 00:00:00 2001 From: Mario Campos Date: Thu, 13 Aug 2026 23:14:33 -0500 Subject: [PATCH 1/9] Use `json` module for JSON validation in `output-cache` --- lib/entry-points.js | 19 +++++++++++++++---- src/cli/output-cache.ts | 35 ++++++++++++++++++++--------------- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index 56914e8848..627d51c28b 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -146774,12 +146774,23 @@ function getCachedCodeQlVersion(logger, env, cmd) { return cachedCodeQlVersion; } function isVersionInfo(x) { - const candidate = x; - return typeof candidate === "object" && candidate !== null && typeof candidate.version === "string" && (candidate.features === void 0 || typeof candidate.features === "object" && candidate.features !== null) && (candidate.overlayVersion === void 0 || typeof candidate.overlayVersion === "number"); + return isObject(x) && validateSchema( + { + version: string, + features: optional(object({})), + overlayVersion: optional(number) + }, + x + ); } function isOutputCache(x) { - const candidate = x; - return typeof candidate === "object" && candidate !== null && typeof candidate.cmd === "string" && candidate.entries !== void 0 && isVersionInfo(candidate.entries.version); + return isObject(x) && validateSchema( + { + cmd: string, + entries: object({}) + }, + x + ) && isObject(x.entries) && isVersionInfo(x.entries.version); } // src/config/pack-registries.ts diff --git a/src/cli/output-cache.ts b/src/cli/output-cache.ts index 8bf8c27abe..f27167c427 100644 --- a/src/cli/output-cache.ts +++ b/src/cli/output-cache.ts @@ -3,6 +3,7 @@ import path from "path"; import { getTemporaryDirectory } from "../actions-util"; import { Env } from "../environment"; +import * as json from "../json"; import { Logger } from "../logging"; import type { VersionInfo } from "./types"; @@ -127,16 +128,16 @@ export function getCachedCodeQlVersion( * @param x The value to test */ function isVersionInfo(x: unknown): x is VersionInfo { - const candidate = x as Partial | null; return ( - typeof candidate === "object" && - candidate !== null && - typeof candidate.version === "string" && - (candidate.features === undefined || - (typeof candidate.features === "object" && - candidate.features !== null)) && - (candidate.overlayVersion === undefined || - typeof candidate.overlayVersion === "number") + json.isObject(x) && + json.validateSchema( + { + version: json.string, + features: json.optional(json.object({})), + overlayVersion: json.optional(json.number), + } as const satisfies json.Schema, + x, + ) ); } @@ -145,12 +146,16 @@ function isVersionInfo(x: unknown): x is VersionInfo { * @param x The value to test */ function isOutputCache(x: unknown): x is OutputCache { - const candidate = x as Partial | null; return ( - typeof candidate === "object" && - candidate !== null && - typeof candidate.cmd === "string" && - candidate.entries !== undefined && - isVersionInfo(candidate.entries.version) + json.isObject(x) && + json.validateSchema( + { + cmd: json.string, + entries: json.object({}), + } as const satisfies json.Schema, + x, + ) && + json.isObject<{ version: unknown }>(x.entries) && + isVersionInfo(x.entries.version) ); } From cb9d39fcc1d38819ade4855aac0022c8125d1f62 Mon Sep 17 00:00:00 2001 From: Mario Campos Date: Fri, 14 Aug 2026 10:55:33 -0500 Subject: [PATCH 2/9] Export `getCommandCacheFilePath` function for use in tests --- src/cli/output-cache.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/output-cache.ts b/src/cli/output-cache.ts index f27167c427..2fbbeb0f51 100644 --- a/src/cli/output-cache.ts +++ b/src/cli/output-cache.ts @@ -44,7 +44,7 @@ export function resetCachedCodeQlVersion(): void { * Returns the path to the temporary file that backs the * on-disk cache of CLI responses between workflow steps. */ -function getCommandCacheFilePath(env: Env): string { +export function getCommandCacheFilePath(env: Env): string { return path.join(getTemporaryDirectory(env), COMMAND_CACHE_FILENAME); } From d7d901a925f541b44083ef27ad5d63ce47bef846 Mon Sep 17 00:00:00 2001 From: Mario Campos Date: Fri, 14 Aug 2026 10:56:07 -0500 Subject: [PATCH 3/9] Refactor tests to use `getCommandCacheFilePath` As well as fixup a few other problems. --- src/cli/output-cache.test.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/cli/output-cache.test.ts b/src/cli/output-cache.test.ts index d8d8629303..21f2163505 100644 --- a/src/cli/output-cache.test.ts +++ b/src/cli/output-cache.test.ts @@ -1,5 +1,4 @@ import * as fs from "fs"; -import path from "path"; import test from "ava"; @@ -18,7 +17,8 @@ test.serial( "getCachedCodeQlVersion reuses a version persisted by an earlier step", async (t) => { await util.withTmpDir(async (tmpDir: string) => { - const cacheFile = path.join(tmpDir, "codeql-action-command-cache.json"); + const env = getTestEnv({ [EnvVar.TEMP]: tmpDir }); + const cacheFile = outputCache.getCommandCacheFilePath(env); fs.writeFileSync( cacheFile, JSON.stringify({ @@ -27,7 +27,6 @@ test.serial( }), "utf8", ); - const env = getTestEnv({ [EnvVar.TEMP]: tmpDir }); t.deepEqual( outputCache.getCachedCodeQlVersion(logger, env, "/path/to/codeql"), { @@ -42,16 +41,16 @@ test.serial( "getCachedCodeQlVersion ignores a persisted version from a different CLI", async (t) => { await util.withTmpDir(async (tmpDir: string) => { - const cacheFile = path.join(tmpDir, "version.json"); + const env = getTestEnv({ [EnvVar.TEMP]: tmpDir }); + const cacheFile = outputCache.getCommandCacheFilePath(env); fs.writeFileSync( cacheFile, JSON.stringify({ cmd: "/path/to/other-codeql", - version: { version: "2.20.0" }, + entries: { version: { version: "2.20.0" } }, }), "utf8", ); - const env = getTestEnv({ [EnvVar.TEMP]: tmpDir }); t.is( outputCache.getCachedCodeQlVersion(logger, env, "/path/to/codeql"), undefined, @@ -64,9 +63,9 @@ test.serial( "getCachedCodeQlVersion ignores a malformed persisted value", async (t) => { await util.withTmpDir(async (tmpDir: string) => { - const cacheFile = path.join(tmpDir, "version.json"); - fs.writeFileSync(cacheFile, "not valid json", "utf8"); const env = getTestEnv({ [EnvVar.TEMP]: tmpDir }); + const cacheFile = outputCache.getCommandCacheFilePath(env); + fs.writeFileSync(cacheFile, "not valid json", "utf8"); t.is( outputCache.getCachedCodeQlVersion(logger, env, "/path/to/codeql"), undefined, @@ -79,9 +78,8 @@ test.serial( "getCachedCodeQlVersion ignores a persisted value with the wrong structure", async (t) => { await util.withTmpDir(async (tmpDir: string) => { - const cacheFile = path.join(tmpDir, "version.json"); const env = getTestEnv({ [EnvVar.TEMP]: tmpDir }); - + const cacheFile = outputCache.getCommandCacheFilePath(env); const testValues = [ { cmd: "/path/to/codeql" }, { entries: { version: { version: "2.20.0" } } }, From c8ba2d2a051d51ff20e2e0e1f499cac0c30f7976 Mon Sep 17 00:00:00 2001 From: Mario Campos Date: Fri, 14 Aug 2026 11:28:36 -0500 Subject: [PATCH 4/9] Refactor `isVersionInfo` with `json` module --- lib/entry-points.js | 26 ++++++++++++++++++-------- src/cli/output-cache.ts | 14 ++------------ src/cli/types.ts | 31 ++++++++++++++++++++++++++----- 3 files changed, 46 insertions(+), 25 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index 627d51c28b..335aa39053 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -146727,6 +146727,23 @@ function wrapApiConfigurationError(e) { // src/cli/output-cache.ts var fs3 = __toESM(require("fs")); var import_path = __toESM(require("path")); + +// src/cli/types.ts +var versionInfoBaseSchema = { + version: string, + features: optional(object({})), + /** + * The overlay version helps deal with backward incompatible changes for + * overlay analysis. When a precompiled query pack reports the same overlay + * version as the CodeQL CLI, we can use the CodeQL CLI to perform overlay + * analysis with that pack. Otherwise, if the overlay versions are different, + * or if either the pack or the CLI does not report an overlay version, + * we need to revert to non-overlay analysis. + */ + overlayVersion: optional(number) +}; + +// src/cli/output-cache.ts var COMMAND_CACHE_FILENAME = "codeql-action-command-cache.json"; var cachedCodeQlVersion = void 0; function getCommandCacheFilePath(env) { @@ -146774,14 +146791,7 @@ function getCachedCodeQlVersion(logger, env, cmd) { return cachedCodeQlVersion; } function isVersionInfo(x) { - return isObject(x) && validateSchema( - { - version: string, - features: optional(object({})), - overlayVersion: optional(number) - }, - x - ); + return isObject(x) && validateSchema(versionInfoBaseSchema, x); } function isOutputCache(x) { return isObject(x) && validateSchema( diff --git a/src/cli/output-cache.ts b/src/cli/output-cache.ts index 2fbbeb0f51..bc91e88bd1 100644 --- a/src/cli/output-cache.ts +++ b/src/cli/output-cache.ts @@ -6,7 +6,7 @@ import { Env } from "../environment"; import * as json from "../json"; import { Logger } from "../logging"; -import type { VersionInfo } from "./types"; +import { VersionInfo, versionInfoBaseSchema } from "./types"; /** * The keys of the command cache. Each key corresponds to a command whose output we cache. @@ -128,17 +128,7 @@ export function getCachedCodeQlVersion( * @param x The value to test */ function isVersionInfo(x: unknown): x is VersionInfo { - return ( - json.isObject(x) && - json.validateSchema( - { - version: json.string, - features: json.optional(json.object({})), - overlayVersion: json.optional(json.number), - } as const satisfies json.Schema, - x, - ) - ); + return json.isObject(x) && json.validateSchema(versionInfoBaseSchema, x); } /** diff --git a/src/cli/types.ts b/src/cli/types.ts index ad48ff29b4..19aa76b91b 100644 --- a/src/cli/types.ts +++ b/src/cli/types.ts @@ -1,6 +1,11 @@ -export interface VersionInfo { - version: string; - features?: { [name: string]: boolean }; +import * as json from "../json"; + +/** + * The JSON schema of the expected output of the `codeql version` command. + */ +export const versionInfoBaseSchema = { + version: json.string, + features: json.optional(json.object({})), /** * The overlay version helps deal with backward incompatible changes for * overlay analysis. When a precompiled query pack reports the same overlay @@ -9,5 +14,21 @@ export interface VersionInfo { * or if either the pack or the CLI does not report an overlay version, * we need to revert to non-overlay analysis. */ - overlayVersion?: number; -} + overlayVersion: json.optional(json.number), +} as const satisfies json.Schema; + +/** + * The base type that describes the expected output of the `codeql version` command. + * This type is partially derived from {@link versionInfoBaseSchema}. + */ +export type VersionInfoBase = json.FromSchema; + +/** + * The full type that describes the expected output of the `codeql version` command. + * This type is partially derived from {@link VersionInfoBase}. + */ +export type VersionInfo = VersionInfoBase & { + // `features` remains optional, but the more specific type takes precedence + // over the `any` type derived by `FromSchema`. + features?: { [name: string]: boolean }; +}; From f3deecb42da34b56edc2fe889712eab8c0e2b600 Mon Sep 17 00:00:00 2001 From: Mario Campos Date: Fri, 14 Aug 2026 11:38:38 -0500 Subject: [PATCH 5/9] Refactor `OutputCache` with `json` module --- lib/entry-points.js | 12 +++++------- src/cli/output-cache.ts | 26 ++++++++++++++------------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index 335aa39053..277472778d 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -146744,6 +146744,10 @@ var versionInfoBaseSchema = { }; // src/cli/output-cache.ts +var outputCacheSchema = { + cmd: string, + entries: object({}) +}; var COMMAND_CACHE_FILENAME = "codeql-action-command-cache.json"; var cachedCodeQlVersion = void 0; function getCommandCacheFilePath(env) { @@ -146794,13 +146798,7 @@ function isVersionInfo(x) { return isObject(x) && validateSchema(versionInfoBaseSchema, x); } function isOutputCache(x) { - return isObject(x) && validateSchema( - { - cmd: string, - entries: object({}) - }, - x - ) && isObject(x.entries) && isVersionInfo(x.entries.version); + return isObject(x) && validateSchema(outputCacheSchema, x) && isObject(x.entries) && isVersionInfo(x.entries.version); } // src/config/pack-registries.ts diff --git a/src/cli/output-cache.ts b/src/cli/output-cache.ts index bc91e88bd1..51e74e8a46 100644 --- a/src/cli/output-cache.ts +++ b/src/cli/output-cache.ts @@ -14,12 +14,20 @@ import { VersionInfo, versionInfoBaseSchema } from "./types"; export type CommandCacheKey = string; /** - * The type of the command cache that is persisted to disk. + * The JSON schema of the command cache that is persisted to disk. */ -export interface OutputCache { - cmd: string; - entries: Record; -} +const outputCacheSchema = { + cmd: json.string, + entries: json.object({}), +} as const satisfies json.Schema; + +/** + * The type that describes the command cache that is persisted to disk. This type + * is partially derived from {@link outputCacheSchema}. + */ +export type OutputCache = json.FromSchema & { + entries: { version: VersionInfo }; +}; /** * The name of the temporary file that backs the on-disk cache of @@ -138,13 +146,7 @@ function isVersionInfo(x: unknown): x is VersionInfo { function isOutputCache(x: unknown): x is OutputCache { return ( json.isObject(x) && - json.validateSchema( - { - cmd: json.string, - entries: json.object({}), - } as const satisfies json.Schema, - x, - ) && + json.validateSchema(outputCacheSchema, x) && json.isObject<{ version: unknown }>(x.entries) && isVersionInfo(x.entries.version) ); From b60777a21583aa71de05f7a860e4b65db83a55ad Mon Sep 17 00:00:00 2001 From: Mario Campos Date: Fri, 14 Aug 2026 14:05:42 -0500 Subject: [PATCH 6/9] Delete unnecessary statement from JSDoc comments --- src/cli/output-cache.ts | 3 +-- src/cli/types.ts | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/cli/output-cache.ts b/src/cli/output-cache.ts index 51e74e8a46..0ed5929973 100644 --- a/src/cli/output-cache.ts +++ b/src/cli/output-cache.ts @@ -22,8 +22,7 @@ const outputCacheSchema = { } as const satisfies json.Schema; /** - * The type that describes the command cache that is persisted to disk. This type - * is partially derived from {@link outputCacheSchema}. + * The type that describes the command cache that is persisted to disk. */ export type OutputCache = json.FromSchema & { entries: { version: VersionInfo }; diff --git a/src/cli/types.ts b/src/cli/types.ts index 19aa76b91b..d33f705645 100644 --- a/src/cli/types.ts +++ b/src/cli/types.ts @@ -19,13 +19,11 @@ export const versionInfoBaseSchema = { /** * The base type that describes the expected output of the `codeql version` command. - * This type is partially derived from {@link versionInfoBaseSchema}. */ export type VersionInfoBase = json.FromSchema; /** * The full type that describes the expected output of the `codeql version` command. - * This type is partially derived from {@link VersionInfoBase}. */ export type VersionInfo = VersionInfoBase & { // `features` remains optional, but the more specific type takes precedence From 1f46830a1f6fa9bbd14eaa67e6f4392d3f0322ba Mon Sep 17 00:00:00 2001 From: Mario Campos Date: Fri, 14 Aug 2026 14:24:37 -0500 Subject: [PATCH 7/9] Fix `VersionInfo` derived type to pass linter --- src/cli/types.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/cli/types.ts b/src/cli/types.ts index d33f705645..71d8d14c2f 100644 --- a/src/cli/types.ts +++ b/src/cli/types.ts @@ -25,8 +25,6 @@ export type VersionInfoBase = json.FromSchema; /** * The full type that describes the expected output of the `codeql version` command. */ -export type VersionInfo = VersionInfoBase & { - // `features` remains optional, but the more specific type takes precedence - // over the `any` type derived by `FromSchema`. +export type VersionInfo = Omit & { features?: { [name: string]: boolean }; }; From b5d34388b76a52b59a974c6217b807d809c40ed7 Mon Sep 17 00:00:00 2001 From: Mario Campos Date: Fri, 14 Aug 2026 14:28:17 -0500 Subject: [PATCH 8/9] Refactor tests in `output-cache.test.ts` to use named imports from `output-cache` --- src/cli/output-cache.test.ts | 39 ++++++++++++++---------------------- 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/src/cli/output-cache.test.ts b/src/cli/output-cache.test.ts index 21f2163505..d5475f9cb2 100644 --- a/src/cli/output-cache.test.ts +++ b/src/cli/output-cache.test.ts @@ -7,7 +7,10 @@ import { getRunnerLogger } from "../logging"; import { getTestEnv, setupTests } from "../testing-utils"; import * as util from "../util"; -import * as outputCache from "./output-cache"; +import { + getCachedCodeQlVersion, + getCommandCacheFilePath, +} from "./output-cache"; setupTests(test); @@ -18,7 +21,7 @@ test.serial( async (t) => { await util.withTmpDir(async (tmpDir: string) => { const env = getTestEnv({ [EnvVar.TEMP]: tmpDir }); - const cacheFile = outputCache.getCommandCacheFilePath(env); + const cacheFile = getCommandCacheFilePath(env); fs.writeFileSync( cacheFile, JSON.stringify({ @@ -27,12 +30,9 @@ test.serial( }), "utf8", ); - t.deepEqual( - outputCache.getCachedCodeQlVersion(logger, env, "/path/to/codeql"), - { - version: "2.20.0", - }, - ); + t.deepEqual(getCachedCodeQlVersion(logger, env, "/path/to/codeql"), { + version: "2.20.0", + }); }); }, ); @@ -42,7 +42,7 @@ test.serial( async (t) => { await util.withTmpDir(async (tmpDir: string) => { const env = getTestEnv({ [EnvVar.TEMP]: tmpDir }); - const cacheFile = outputCache.getCommandCacheFilePath(env); + const cacheFile = getCommandCacheFilePath(env); fs.writeFileSync( cacheFile, JSON.stringify({ @@ -51,10 +51,7 @@ test.serial( }), "utf8", ); - t.is( - outputCache.getCachedCodeQlVersion(logger, env, "/path/to/codeql"), - undefined, - ); + t.is(getCachedCodeQlVersion(logger, env, "/path/to/codeql"), undefined); }); }, ); @@ -64,12 +61,9 @@ test.serial( async (t) => { await util.withTmpDir(async (tmpDir: string) => { const env = getTestEnv({ [EnvVar.TEMP]: tmpDir }); - const cacheFile = outputCache.getCommandCacheFilePath(env); + const cacheFile = getCommandCacheFilePath(env); fs.writeFileSync(cacheFile, "not valid json", "utf8"); - t.is( - outputCache.getCachedCodeQlVersion(logger, env, "/path/to/codeql"), - undefined, - ); + t.is(getCachedCodeQlVersion(logger, env, "/path/to/codeql"), undefined); }); }, ); @@ -79,7 +73,7 @@ test.serial( async (t) => { await util.withTmpDir(async (tmpDir: string) => { const env = getTestEnv({ [EnvVar.TEMP]: tmpDir }); - const cacheFile = outputCache.getCommandCacheFilePath(env); + const cacheFile = getCommandCacheFilePath(env); const testValues = [ { cmd: "/path/to/codeql" }, { entries: { version: { version: "2.20.0" } } }, @@ -104,7 +98,7 @@ test.serial( for (const value of testValues) { fs.writeFileSync(cacheFile, value, "utf8"); t.is( - outputCache.getCachedCodeQlVersion(logger, env, "/path/to/codeql"), + getCachedCodeQlVersion(logger, env, "/path/to/codeql"), undefined, value, ); @@ -117,10 +111,7 @@ test.serial("getCachedCodeQlVersion ignores non-existent file", async (t) => { await util.withTmpDir(async (tmpDir: string) => { const env = getTestEnv({ [EnvVar.TEMP]: tmpDir }); t.notThrows(() => { - t.is( - outputCache.getCachedCodeQlVersion(logger, env, "/path/to/codeql"), - undefined, - ); + t.is(getCachedCodeQlVersion(logger, env, "/path/to/codeql"), undefined); }); }); }); From fcd8d74cda72c98da18b8990874bb47f19b9634e Mon Sep 17 00:00:00 2001 From: Mario Campos Date: Fri, 14 Aug 2026 14:41:04 -0500 Subject: [PATCH 9/9] Refactor output-caching functions to accept file path dependency This makes it easier to test. Credit to @mbg. --- lib/entry-points.js | 30 ++++++++++--------- src/cli/output-cache.test.ts | 58 ++++++++++++++++++++---------------- src/cli/output-cache.ts | 20 +++++-------- src/codeql.ts | 9 ++++-- src/status-report.ts | 10 +++++-- 5 files changed, 70 insertions(+), 57 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index 277472778d..ab6b83dab9 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -146753,7 +146753,7 @@ var cachedCodeQlVersion = void 0; function getCommandCacheFilePath(env) { return import_path.default.join(getTemporaryDirectory(env), COMMAND_CACHE_FILENAME); } -function cacheCodeQlVersion(env, cmd, version) { +function cacheCodeQlVersion(cacheFilePath, cmd, version) { if (cachedCodeQlVersion !== void 0) { throw new Error("cacheCodeQlVersion() should be called only once"); } @@ -146762,23 +146762,17 @@ function cacheCodeQlVersion(env, cmd, version) { cmd, entries: { version } }; - fs3.writeFileSync( - getCommandCacheFilePath(env), - JSON.stringify(outputCache), - "utf8" - ); + fs3.writeFileSync(cacheFilePath, JSON.stringify(outputCache), "utf8"); } -function getCachedCodeQlVersion(logger, env, cmd) { +function getCachedCodeQlVersion(logger, cacheFilePath, cmd) { if (cachedCodeQlVersion !== void 0) { return cachedCodeQlVersion; } let serialized; try { - serialized = fs3.readFileSync(getCommandCacheFilePath(env), "utf8"); + serialized = fs3.readFileSync(cacheFilePath, "utf8"); } catch (e) { - logger.debug( - `Cannot read CLI-cache file ${getCommandCacheFilePath(env)}: ${e}` - ); + logger.debug(`Cannot read CLI-cache file ${cacheFilePath}: ${e}`); return void 0; } let persisted; @@ -147342,7 +147336,10 @@ async function createStatusReportBase(actionName, status, actionStartedAt, confi core7.exportVariable("CODEQL_WORKFLOW_STARTED_AT" /* WORKFLOW_STARTED_AT */, workflowStartedAt); } const runnerOs = getRequiredEnvParam("RUNNER_OS"); - const codeQlCliVersion = getCachedCodeQlVersion(logger, getEnv()); + const codeQlCliVersion = getCachedCodeQlVersion( + logger, + getCommandCacheFilePath(getEnv()) + ); const actionRef = process.env["GITHUB_ACTION_REF"] || ""; const testingEnvironment = getTestingEnvironment(); if (testingEnvironment) { @@ -152362,7 +152359,12 @@ async function getCodeQLForCmd(logger, cmd, checkVersion) { return cmd; }, async getVersion() { - let result = getCachedCodeQlVersion(logger, getEnv(), cmd); + const cacheFilePath = getCommandCacheFilePath(getEnv()); + let result = getCachedCodeQlVersion( + logger, + cacheFilePath, + cmd + ); if (result === void 0) { result = await runCliJson( cmd, @@ -152371,7 +152373,7 @@ async function getCodeQLForCmd(logger, cmd, checkVersion) { noStreamStdout: true } ); - cacheCodeQlVersion(getEnv(), cmd, result); + cacheCodeQlVersion(cacheFilePath, cmd, result); } return result; }, diff --git a/src/cli/output-cache.test.ts b/src/cli/output-cache.test.ts index d5475f9cb2..656cfd8201 100644 --- a/src/cli/output-cache.test.ts +++ b/src/cli/output-cache.test.ts @@ -1,16 +1,13 @@ import * as fs from "fs"; +import path from "path"; import test from "ava"; -import { EnvVar } from "../environment"; import { getRunnerLogger } from "../logging"; -import { getTestEnv, setupTests } from "../testing-utils"; +import { setupTests } from "../testing-utils"; import * as util from "../util"; -import { - getCachedCodeQlVersion, - getCommandCacheFilePath, -} from "./output-cache"; +import { getCachedCodeQlVersion } from "./output-cache"; setupTests(test); @@ -20,19 +17,22 @@ test.serial( "getCachedCodeQlVersion reuses a version persisted by an earlier step", async (t) => { await util.withTmpDir(async (tmpDir: string) => { - const env = getTestEnv({ [EnvVar.TEMP]: tmpDir }); - const cacheFile = getCommandCacheFilePath(env); + const cacheFilePath = path.join(tmpDir, "cache.json"); + fs.writeFileSync( - cacheFile, + cacheFilePath, JSON.stringify({ cmd: "/path/to/codeql", entries: { version: { version: "2.20.0" } }, }), "utf8", ); - t.deepEqual(getCachedCodeQlVersion(logger, env, "/path/to/codeql"), { - version: "2.20.0", - }); + t.deepEqual( + getCachedCodeQlVersion(logger, cacheFilePath, "/path/to/codeql"), + { + version: "2.20.0", + }, + ); }); }, ); @@ -41,17 +41,19 @@ test.serial( "getCachedCodeQlVersion ignores a persisted version from a different CLI", async (t) => { await util.withTmpDir(async (tmpDir: string) => { - const env = getTestEnv({ [EnvVar.TEMP]: tmpDir }); - const cacheFile = getCommandCacheFilePath(env); + const cacheFilePath = path.join(tmpDir, "cache.json"); fs.writeFileSync( - cacheFile, + cacheFilePath, JSON.stringify({ cmd: "/path/to/other-codeql", entries: { version: { version: "2.20.0" } }, }), "utf8", ); - t.is(getCachedCodeQlVersion(logger, env, "/path/to/codeql"), undefined); + t.is( + getCachedCodeQlVersion(logger, cacheFilePath, "/path/to/codeql"), + undefined, + ); }); }, ); @@ -60,10 +62,12 @@ test.serial( "getCachedCodeQlVersion ignores a malformed persisted value", async (t) => { await util.withTmpDir(async (tmpDir: string) => { - const env = getTestEnv({ [EnvVar.TEMP]: tmpDir }); - const cacheFile = getCommandCacheFilePath(env); - fs.writeFileSync(cacheFile, "not valid json", "utf8"); - t.is(getCachedCodeQlVersion(logger, env, "/path/to/codeql"), undefined); + const cacheFilePath = path.join(tmpDir, "cache.json"); + fs.writeFileSync(cacheFilePath, "not valid json", "utf8"); + t.is( + getCachedCodeQlVersion(logger, cacheFilePath, "/path/to/codeql"), + undefined, + ); }); }, ); @@ -72,8 +76,7 @@ test.serial( "getCachedCodeQlVersion ignores a persisted value with the wrong structure", async (t) => { await util.withTmpDir(async (tmpDir: string) => { - const env = getTestEnv({ [EnvVar.TEMP]: tmpDir }); - const cacheFile = getCommandCacheFilePath(env); + const cacheFilePath = path.join(tmpDir, "cache.json"); const testValues = [ { cmd: "/path/to/codeql" }, { entries: { version: { version: "2.20.0" } } }, @@ -96,9 +99,9 @@ test.serial( ].map((v) => JSON.stringify(v)); for (const value of testValues) { - fs.writeFileSync(cacheFile, value, "utf8"); + fs.writeFileSync(cacheFilePath, value, "utf8"); t.is( - getCachedCodeQlVersion(logger, env, "/path/to/codeql"), + getCachedCodeQlVersion(logger, cacheFilePath, "/path/to/codeql"), undefined, value, ); @@ -109,9 +112,12 @@ test.serial( test.serial("getCachedCodeQlVersion ignores non-existent file", async (t) => { await util.withTmpDir(async (tmpDir: string) => { - const env = getTestEnv({ [EnvVar.TEMP]: tmpDir }); + const cacheFilePath = path.join(tmpDir, "cache.json"); t.notThrows(() => { - t.is(getCachedCodeQlVersion(logger, env, "/path/to/codeql"), undefined); + t.is( + getCachedCodeQlVersion(logger, cacheFilePath, "/path/to/codeql"), + undefined, + ); }); }); }); diff --git a/src/cli/output-cache.ts b/src/cli/output-cache.ts index 0ed5929973..fb5deb1ad6 100644 --- a/src/cli/output-cache.ts +++ b/src/cli/output-cache.ts @@ -57,12 +57,12 @@ export function getCommandCacheFilePath(env: Env): string { /** * Caches the CodeQL CLI version both in-memory and on disk. - * @param env The environment variables to use. + * @param cacheFilePath The path to the cache file. * @param cmd The path to the CodeQL CLI. * @param version The version information to cache. */ export function cacheCodeQlVersion( - env: Env, + cacheFilePath: string, cmd: string, version: VersionInfo, ): void { @@ -78,22 +78,18 @@ export function cacheCodeQlVersion( // processes, can reuse it rather than invoking `codeql version` again. We // record the CLI path so that a different step using a different CodeQL bundle // doesn't pick up a stale version. - fs.writeFileSync( - getCommandCacheFilePath(env), - JSON.stringify(outputCache), - "utf8", - ); + fs.writeFileSync(cacheFilePath, JSON.stringify(outputCache), "utf8"); } /** * Returns the cached CodeQL CLI version, if any. * @param logger The logger to use for logging messages. - * @param env The environment variables to use. + * @param cacheFilePath The path to the cache file. * @param cmd The path to the CodeQL CLI. */ export function getCachedCodeQlVersion( logger: Logger, - env: Env, + cacheFilePath: string, cmd?: string, ): undefined | VersionInfo { if (cachedCodeQlVersion !== undefined) { @@ -104,11 +100,9 @@ export function getCachedCodeQlVersion( // invokes `codeql version` instead. let serialized: string; try { - serialized = fs.readFileSync(getCommandCacheFilePath(env), "utf8"); + serialized = fs.readFileSync(cacheFilePath, "utf8"); } catch (e) { - logger.debug( - `Cannot read CLI-cache file ${getCommandCacheFilePath(env)}: ${e}`, - ); + logger.debug(`Cannot read CLI-cache file ${cacheFilePath}: ${e}`); return undefined; } let persisted: unknown; diff --git a/src/codeql.ts b/src/codeql.ts index 8f7e9e7445..bfa52d52d1 100644 --- a/src/codeql.ts +++ b/src/codeql.ts @@ -491,7 +491,12 @@ async function getCodeQLForCmd( return cmd; }, async getVersion() { - let result = outputCache.getCachedCodeQlVersion(logger, getEnv(), cmd); + const cacheFilePath = outputCache.getCommandCacheFilePath(getEnv()); + let result = outputCache.getCachedCodeQlVersion( + logger, + cacheFilePath, + cmd, + ); if (result === undefined) { result = await runCliJson( cmd, @@ -500,7 +505,7 @@ async function getCodeQLForCmd( noStreamStdout: true, }, ); - outputCache.cacheCodeQlVersion(getEnv(), cmd, result); + outputCache.cacheCodeQlVersion(cacheFilePath, cmd, result); } return result; }, diff --git a/src/status-report.ts b/src/status-report.ts index e61b04f9dd..c5d15e1f16 100644 --- a/src/status-report.ts +++ b/src/status-report.ts @@ -14,7 +14,10 @@ import { isSelfHostedRunner, } from "./actions-util"; import { getAnalysisKey, getApiClient } from "./api-client"; -import { getCachedCodeQlVersion } from "./cli/output-cache"; +import { + getCachedCodeQlVersion, + getCommandCacheFilePath, +} from "./cli/output-cache"; import type { Config } from "./config/action-config"; import type { ComputedInput, InputName } from "./config/inputs"; import { parseRegistriesWithoutCredentials } from "./config/pack-registries"; @@ -376,7 +379,10 @@ export async function createStatusReportBase( core.exportVariable(EnvVar.WORKFLOW_STARTED_AT, workflowStartedAt); } const runnerOs = getRequiredEnvParam("RUNNER_OS"); - const codeQlCliVersion = getCachedCodeQlVersion(logger, getEnv()); + const codeQlCliVersion = getCachedCodeQlVersion( + logger, + getCommandCacheFilePath(getEnv()), + ); const actionRef = process.env["GITHUB_ACTION_REF"] || ""; const testingEnvironment = getTestingEnvironment(); // re-export the testing environment variable so that it is available to subsequent steps,