diff --git a/lib/entry-points.js b/lib/entry-points.js index 348e213bd..e713e0143 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -149863,7 +149863,7 @@ var path15 = __toESM(require("path")); var core10 = __toESM(require_core()); var toolrunner3 = __toESM(require_toolrunner()); -// src/cache.ts +// src/cli/output-cache.ts var fs6 = __toESM(require("fs")); var path6 = __toESM(require("path")); @@ -149908,11 +149908,28 @@ function formatDuration(durationMs) { return `${minutes}m${seconds}s`; } -// src/cache.ts +// src/cli/output-cache.ts var COMMAND_CACHE_FILENAME = "codeql-action-command-cache.json"; -var CommandCacheKey = { - Version: "version", - ResolveLanguages: "resolveLanguages" +function isVersionInfo(x) { + return isObject(x) && validateSchema( + { + version: string, + features: optional(object), + overlayVersion: optional(number) + }, + x + ); +} +function isResolveLanguagesOutput(x) { + return isObject(x) && isObject(x.extractors) && Object.values(x.extractors).every( + (extractorList) => isArray(extractorList) && extractorList.every( + (extractor) => isObject(extractor) && isString(extractor.extractor_root) + ) + ) && (x.aliases === void 0 || isObject(x.aliases) && Object.values(x.aliases).every((alias) => isString(alias))); +} +var commandCacheValidators = { + ["version" /* Version */]: isVersionInfo, + ["resolve languages" /* ResolveLanguages */]: isResolveLanguagesOutput }; var inMemoryCache = /* @__PURE__ */ new Map(); var logger = getActionsLogger(); @@ -149948,18 +149965,20 @@ function cacheCommandOutput(key, cmd, output) { const entry = { cmd, output }; inMemoryCache.set(key, entry); } -function getCachedCommandOutput(key, cmd, validate) { +function getCachedCommandOutput(key, cmd) { const memoized = inMemoryCache.get(key); if (memoized !== void 0) { return memoized.output; } const entry = readCommandCacheFile()[key]; - if (!isObject(entry) || !isString(entry.cmd) || cmd !== void 0 && entry.cmd !== cmd || validate !== void 0 && !validate(entry.output)) { + if (!isObject(entry) || !isString(entry.cmd) || cmd !== void 0 && entry.cmd !== cmd || !commandCacheValidators[key](entry.output)) { logger.warning("Received invalid data from the command-cache file."); return void 0; } - cacheCommandOutput(key, entry.cmd, entry.output); - return entry.output; + const cachedEntry = entry; + const output = cachedEntry.output; + cacheCommandOutput(key, cachedEntry.cmd, output); + return output; } // src/cli-errors.ts @@ -153676,23 +153695,6 @@ async function getCombinedTracerConfig(codeql, config) { } // src/codeql.ts -function isVersionInfo(x) { - return isObject(x) && validateSchema( - { - version: string, - features: optional(object), - overlayVersion: optional(number) - }, - x - ); -} -function isResolveLanguagesOutput(x) { - return isObject(x) && isObject(x.extractors) && Object.values(x.extractors).every( - (extractorList) => isArray(extractorList) && extractorList.every( - (extractor) => isObject(extractor) && isString(extractor.extractor_root) - ) - ) && (x.aliases === void 0 || isObject(x.aliases) && Object.values(x.aliases).every((alias) => isString(alias))); -} var cachedCodeQL = void 0; var CODEQL_MINIMUM_VERSION = "2.19.4"; var CODEQL_NEXT_MINIMUM_VERSION = "2.19.4"; @@ -153755,8 +153757,8 @@ async function getCodeQL(cmd) { } return cachedCodeQL; } -async function getCachedOrRun(key, cmd, run9, validate) { - let result = getCachedCommandOutput(key, cmd, validate); +async function getCachedOrRun(key, cmd, run9) { + let result = getCachedCommandOutput(key, cmd); if (result === void 0) { result = await run9(); cacheCommandOutput(key, cmd, result); @@ -153770,12 +153772,11 @@ async function getCodeQLForCmd(cmd, checkVersion) { }, async getVersion() { return getCachedOrRun( - CommandCacheKey.Version, + "version" /* Version */, cmd, () => runCliJson(cmd, ["version", "--format=json"], { noStreamStdout: true - }), - isVersionInfo + }) ); }, async printVersion() { @@ -153931,28 +153932,23 @@ async function getCodeQLForCmd(cmd, checkVersion) { await runCli(cmd, args); }, async resolveLanguages() { - return getCachedOrRun( - CommandCacheKey.ResolveLanguages, - cmd, - async () => { - const isFilterToLanguagesWithQueriesSupported = await this.supportsFeature( - "builtinExtractorsSpecifyDefaultQueries" /* BuiltinExtractorsSpecifyDefaultQueries */ - ); - return runCliJson(cmd, [ - "resolve", - "languages", - "--format=betterjson", - "--extractor-options-verbosity=4", - "--extractor-include-aliases", - // TODO: Unconditionally include `--filter-to-languages-with-queries` - // once CODEQL_MINIMUM_VERSION is at least v2.23.0 - // — the first version to support this flag. - ...isFilterToLanguagesWithQueriesSupported ? ["--filter-to-languages-with-queries"] : [], - ...getExtraOptionsFromEnv(["resolve", "languages"]) - ]); - }, - isResolveLanguagesOutput - ); + return getCachedOrRun("resolve languages" /* ResolveLanguages */, cmd, async () => { + const isFilterToLanguagesWithQueriesSupported = await this.supportsFeature( + "builtinExtractorsSpecifyDefaultQueries" /* BuiltinExtractorsSpecifyDefaultQueries */ + ); + return runCliJson(cmd, [ + "resolve", + "languages", + "--format=betterjson", + "--extractor-options-verbosity=4", + "--extractor-include-aliases", + // TODO: Unconditionally include `--filter-to-languages-with-queries` + // once CODEQL_MINIMUM_VERSION is at least v2.23.0 + // — the first version to support this flag. + ...isFilterToLanguagesWithQueriesSupported ? ["--filter-to-languages-with-queries"] : [], + ...getExtraOptionsFromEnv(["resolve", "languages"]) + ]); + }); }, async resolveBuildEnvironment(workingDir, language) { const codeqlArgs = [ @@ -155213,9 +155209,8 @@ async function createStatusReportBase(actionName, status, actionStartedAt, confi } const runnerOs = getRequiredEnvParam("RUNNER_OS"); const codeQlCliVersion = getCachedCommandOutput( - CommandCacheKey.Version, - void 0, - isVersionInfo + "version" /* Version */, + void 0 ); const actionRef = process.env["GITHUB_ACTION_REF"] || ""; const testingEnvironment = getTestingEnvironment(); diff --git a/src/cli/output-cache.test.ts b/src/cli/output-cache.test.ts index d81c613c3..c66af1491 100644 --- a/src/cli/output-cache.test.ts +++ b/src/cli/output-cache.test.ts @@ -8,6 +8,7 @@ import { setupTests } from "../testing-utils"; import { cacheCommandOutput, + CommandCacheKey, getCachedCommandOutput, resetCachedCommandOutputs, type VersionInfo, @@ -51,16 +52,13 @@ test.serial( async (t) => { await withCacheDir((cacheFilePath) => { writeCacheFile(cacheFilePath, { - version: { + [CommandCacheKey.Version]: { cmd: "/path/to/codeql", output: { version: "2.20.0" }, }, }); t.deepEqual( - getCachedCommandOutput( - "version", - "/path/to/codeql", - ), + getCachedCommandOutput(CommandCacheKey.Version, "/path/to/codeql"), { version: "2.20.0" }, ); }); @@ -72,16 +70,13 @@ test.serial( async (t) => { await withCacheDir((cacheFilePath) => { writeCacheFile(cacheFilePath, { - version: { + [CommandCacheKey.Version]: { cmd: "/path/to/other-codeql", output: { version: "2.20.0" }, }, }); t.is( - getCachedCommandOutput( - "version", - "/path/to/codeql", - ), + getCachedCommandOutput(CommandCacheKey.Version, "/path/to/codeql"), undefined, ); }); @@ -94,10 +89,7 @@ test.serial( await withCacheDir((cacheFilePath) => { fs.writeFileSync(cacheFilePath, "not valid json"); t.is( - getCachedCommandOutput( - "version", - "/path/to/codeql", - ), + getCachedCommandOutput(CommandCacheKey.Version, "/path/to/codeql"), undefined, ); }); @@ -109,10 +101,7 @@ test.serial( async (t) => { await withCacheDir(() => { t.is( - getCachedCommandOutput( - "version", - "/path/to/codeql", - ), + getCachedCommandOutput(CommandCacheKey.Version, "/path/to/codeql"), undefined, ); }); @@ -131,13 +120,10 @@ test.serial( ]) { resetCachedCommandOutputs(); writeCacheFile(cacheFilePath, { - version: { cmd: "/path/to/codeql", output }, + [CommandCacheKey.Version]: { cmd: "/path/to/codeql", output }, }); t.is( - getCachedCommandOutput( - "version", - "/path/to/codeql", - ), + getCachedCommandOutput(CommandCacheKey.Version, "/path/to/codeql"), undefined, JSON.stringify(output), ); @@ -151,13 +137,10 @@ test.serial( async (t) => { await withCacheDir((cacheFilePath) => { writeCacheFile(cacheFilePath, { - version: { output: { version: "2.20.0" } }, + [CommandCacheKey.Version]: { output: { version: "2.20.0" } }, }); t.is( - getCachedCommandOutput( - "version", - "/path/to/codeql", - ), + getCachedCommandOutput(CommandCacheKey.Version, "/path/to/codeql"), undefined, ); }); @@ -167,10 +150,13 @@ test.serial( test.serial("cacheCommandOutput persists the output to the memo", async (t) => { await withCacheDir(() => { const output: VersionInfo = { version: "2.20.0" }; - cacheCommandOutput("version", "/path/to/codeql", output); + cacheCommandOutput(CommandCacheKey.Version, "/path/to/codeql", output); // Tier 1: the value is immediately available from the memo. - t.deepEqual(getCachedCommandOutput("version", "/path/to/codeql"), output); + t.deepEqual( + getCachedCommandOutput(CommandCacheKey.Version, "/path/to/codeql"), + output, + ); }); }); @@ -179,16 +165,19 @@ test.serial( async (t) => { await withCacheDir((cacheFilePath) => { const output: VersionInfo = { version: "2.20.0", overlayVersion: 1 }; - cacheCommandOutput("version", "/path/to/codeql", output); + cacheCommandOutput(CommandCacheKey.Version, "/path/to/codeql", output); // Overwrite the file with a different value; the memo (tier 1) should win. writeCacheFile(cacheFilePath, { - version: { + [CommandCacheKey.Version]: { cmd: "/path/to/codeql", output: { version: "2.21.0" }, }, }); - t.deepEqual(getCachedCommandOutput("version", "/path/to/codeql"), output); + t.deepEqual( + getCachedCommandOutput(CommandCacheKey.Version, "/path/to/codeql"), + output, + ); }); }, ); diff --git a/src/cli/output-cache.ts b/src/cli/output-cache.ts index 78dbbe4f2..69ec39743 100644 --- a/src/cli/output-cache.ts +++ b/src/cli/output-cache.ts @@ -12,7 +12,10 @@ import { getActionsLogger } from "../logging"; const COMMAND_CACHE_FILENAME = "codeql-action-command-cache.json"; /** A key used to identify cached command output. */ -export type CommandCacheKey = "version" | "resolve languages"; +export enum CommandCacheKey { + Version = "version", + ResolveLanguages = "resolve languages", +} export interface VersionInfo { version: string; @@ -78,8 +81,8 @@ export function isResolveLanguagesOutput( } export type CommandCacheKeyOutputMap = { - version: VersionInfo; - "resolve languages": ResolveLanguagesOutput; + [CommandCacheKey.Version]: VersionInfo; + [CommandCacheKey.ResolveLanguages]: ResolveLanguagesOutput; }; const commandCacheValidators: { @@ -87,8 +90,8 @@ const commandCacheValidators: { output: unknown, ) => output is CommandCacheKeyOutputMap[K]; } = { - version: isVersionInfo, - "resolve languages": isResolveLanguagesOutput, + [CommandCacheKey.Version]: isVersionInfo, + [CommandCacheKey.ResolveLanguages]: isResolveLanguagesOutput, }; interface StoredCommandCacheEntry { @@ -174,8 +177,8 @@ export function cacheCommandOutput( * * Resolves tier 1 (in-memory memo) first, then tier 2 (temporary file). A value * loaded from the file is ignored unless its `cmd` matches the optional `cmd` - * argument, and it satisfies the optional `validate` type guard; valid values - * are memoized into tier 1 before being returned. + * argument and its output satisfies the internal validator for `key`; valid + * values are memoized into tier 1 before being returned. * * A return value of `undefined` signals the caller to fall back to tier 3 (the * CLI). diff --git a/src/codeql.ts b/src/codeql.ts index b93078286..129437d65 100644 --- a/src/codeql.ts +++ b/src/codeql.ts @@ -14,9 +14,9 @@ import { import * as api from "./api-client"; import { cacheCommandOutput, + CommandCacheKey, getCachedCommandOutput, type CommandCacheKeyOutputMap, - type CommandCacheKey, type ResolveLanguagesOutput, type VersionInfo, } from "./cli/output-cache"; @@ -515,7 +515,7 @@ async function getCodeQLForCmd( return cmd; }, async getVersion() { - return getCachedOrRun("version", cmd, () => + return getCachedOrRun(CommandCacheKey.Version, cmd, () => runCliJson(cmd, ["version", "--format=json"], { noStreamStdout: true, }), @@ -718,7 +718,7 @@ async function getCodeQLForCmd( await runCli(cmd, args); }, async resolveLanguages() { - return getCachedOrRun("resolve languages", cmd, async () => { + return getCachedOrRun(CommandCacheKey.ResolveLanguages, cmd, async () => { const isFilterToLanguagesWithQueriesSupported = await this.supportsFeature( ToolsFeature.BuiltinExtractorsSpecifyDefaultQueries, diff --git a/src/status-report.ts b/src/status-report.ts index 7cf152ce1..8e401ffff 100644 --- a/src/status-report.ts +++ b/src/status-report.ts @@ -12,7 +12,7 @@ import { isSelfHostedRunner, } from "./actions-util"; import { getAnalysisKey, getApiClient } from "./api-client"; -import { getCachedCommandOutput } from "./cli/output-cache"; +import { CommandCacheKey, getCachedCommandOutput } from "./cli/output-cache"; import { parseRegistriesWithoutCredentials, type Config } from "./config-utils"; import { DependencyCacheRestoreStatusReport } from "./dependency-caching"; import { DocUrl } from "./doc-url"; @@ -283,7 +283,10 @@ export async function createStatusReportBase( core.exportVariable(EnvVar.WORKFLOW_STARTED_AT, workflowStartedAt); } const runnerOs = getRequiredEnvParam("RUNNER_OS"); - const codeQlCliVersion = getCachedCommandOutput("version", undefined); + const codeQlCliVersion = getCachedCommandOutput( + CommandCacheKey.Version, + undefined, + ); const actionRef = process.env["GITHUB_ACTION_REF"] || ""; const testingEnvironment = getTestingEnvironment(); // re-export the testing environment variable so that it is available to subsequent steps,