diff --git a/lib/entry-points.js b/lib/entry-points.js index 07b48863f..e3ad62295 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -149934,9 +149934,12 @@ function readCommandCacheFile() { } return {}; } -function writeCommandCacheFile(data) { +function writeCommandCacheFile() { try { - fs6.writeFileSync(getCommandCacheFilePath(), JSON.stringify(data)); + fs6.writeFileSync( + getCommandCacheFilePath(), + JSON.stringify(Object.fromEntries(inMemoryCache)) + ); } catch (e) { logger.warning(`Failed to write command cache file: ${e}`); } @@ -149944,9 +149947,6 @@ function writeCommandCacheFile(data) { function cacheCommandOutput(key, cmd, output) { const entry = { cmd, output }; inMemoryCache.set(key, entry); - const data = readCommandCacheFile(); - data[key] = entry; - writeCommandCacheFile(data); } function getCachedCommandOutput(key, cmd, validate) { const memoized = inMemoryCache.get(key); @@ -159013,6 +159013,8 @@ async function run3(startedAt) { toolsVersion = initCodeQLResult.toolsVersion; toolsSource = initCodeQLResult.toolsSource; zstdAvailability = initCodeQLResult.zstdAvailability; + await codeql.getVersion(); + await codeql.resolveLanguages(); await checkWorkflow(logger2, codeql); if ( // Only enable the experimental features env variable for Rust analysis if the user has explicitly @@ -159322,6 +159324,7 @@ exec ${goBinaryPath} "$@"` await saveConfig(config, logger2); core20.setOutput("codeql-path", config.codeQLCmd); core20.setOutput("codeql-version", (await codeql.getVersion()).version); + writeCommandCacheFile(); } catch (unwrappedError) { const error3 = wrapError(unwrappedError); core20.setFailed(error3.message); diff --git a/src/cache.test.ts b/src/cache.test.ts index c76e4719f..71a07b4c4 100644 --- a/src/cache.test.ts +++ b/src/cache.test.ts @@ -170,32 +170,19 @@ test.serial( }, ); -test.serial( - "cacheCommandOutput persists the output to both the memo and the file", - async (t) => { - await withCacheDir((cacheFilePath) => { - cacheCommandOutput(CommandCacheKey.Version, "/path/to/codeql", { - hello: "world", - }); - - // Tier 2: the temporary file contains the entry. - const onDisk = JSON.parse( - fs.readFileSync(cacheFilePath, "utf8"), - ) as Record; - t.deepEqual(onDisk[CommandCacheKey.Version], { - cmd: "/path/to/codeql", - output: { hello: "world" }, - }); - - // Tier 1: the value is served from the memo even after the file is gone. - fs.rmSync(cacheFilePath); - t.deepEqual( - getCachedCommandOutput(CommandCacheKey.Version, "/path/to/codeql"), - { hello: "world" }, - ); +test.serial("cacheCommandOutput persists the output to the memo", async (t) => { + await withCacheDir(() => { + cacheCommandOutput(CommandCacheKey.Version, "/path/to/codeql", { + hello: "world", }); - }, -); + + // Tier 1: the value is immediately available from the memo. + t.deepEqual( + getCachedCommandOutput(CommandCacheKey.Version, "/path/to/codeql"), + { hello: "world" }, + ); + }); +}); test.serial( "getCachedCommandOutput prefers the in-memory memo over the file", @@ -207,7 +194,10 @@ test.serial( // Overwrite the file with a different value; the memo (tier 1) should win. writeCacheFile(cacheFilePath, { - [CommandCacheKey.Version]: { cmd: "/path/to/codeql", output: { value: 2 } }, + [CommandCacheKey.Version]: { + cmd: "/path/to/codeql", + output: { value: 2 }, + }, }); t.deepEqual( getCachedCommandOutput(CommandCacheKey.Version, "/path/to/codeql"), diff --git a/src/cache.ts b/src/cache.ts index 660a0eb9c..32ec7f986 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -65,20 +65,22 @@ function readCommandCacheFile(): Record { } /** - * Persists the cache to the temporary file. Best-effort: a failure to write + * Persists the in-memory cache to the temporary file. Best-effort: a failure to write * just means a later step re-runs the CLI. */ -function writeCommandCacheFile(data: Record): void { +export function writeCommandCacheFile(): void { try { - fs.writeFileSync(getCommandCacheFilePath(), JSON.stringify(data)); + fs.writeFileSync( + getCommandCacheFilePath(), + JSON.stringify(Object.fromEntries(inMemoryCache)), + ); } catch (e) { logger.warning(`Failed to write command cache file: ${e}`); } } /** - * Stores the output of a command under `key`, writing it to both the in-memory - * memo (tier 1) and the temporary file (tier 2). + * Stores the output of a CLI command under `key` in a module-global object. */ export function cacheCommandOutput( key: CommandCacheKey, @@ -87,10 +89,6 @@ export function cacheCommandOutput( ): void { const entry: CommandCacheEntry = { cmd, output }; inMemoryCache.set(key, entry); - - const data = readCommandCacheFile(); - data[key] = entry; - writeCommandCacheFile(data); } /** diff --git a/src/init-action.ts b/src/init-action.ts index 9c9b45556..dbcb2f054 100644 --- a/src/init-action.ts +++ b/src/init-action.ts @@ -19,6 +19,7 @@ import { } from "./actions-util"; import { AnalysisKind, getAnalysisKinds } from "./analyses"; import { getGitHubVersion, GitHubApiCombinedDetails } from "./api-client"; +import { writeCommandCacheFile } from "./cache"; import { getDependencyCachingEnabled, getTotalCacheSize, @@ -323,6 +324,11 @@ async function run(startedAt: Date) { toolsSource = initCodeQLResult.toolsSource; zstdAvailability = initCodeQLResult.zstdAvailability; + // Populate the in-memory command cache with CLI version and languages. + // These results will be persisted to disk at the end of the init action. + await codeql.getVersion(); + await codeql.resolveLanguages(); + // Check the workflow for problems. If there are any problems, they are reported // to the workflow log. No exceptions are thrown. await checkWorkflow(logger, codeql); @@ -766,6 +772,9 @@ async function run(startedAt: Date) { core.setOutput("codeql-path", config.codeQLCmd); core.setOutput("codeql-version", (await codeql.getVersion()).version); + + // Persist the command cache to disk at the end of a successful init. + writeCommandCacheFile(); } catch (unwrappedError) { const error = wrapError(unwrappedError); core.setFailed(error.message);