Skip to content
27 changes: 23 additions & 4 deletions lib/entry-points.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 8 additions & 10 deletions src/cli/output-cache.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as fs from "fs";
import path from "path";

import test from "ava";

Expand All @@ -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);
Comment thread
mario-campos marked this conversation as resolved.
Outdated
fs.writeFileSync(
cacheFile,
JSON.stringify({
Expand All @@ -27,7 +27,6 @@ test.serial(
}),
"utf8",
);
const env = getTestEnv({ [EnvVar.TEMP]: tmpDir });
t.deepEqual(
outputCache.getCachedCodeQlVersion(logger, env, "/path/to/codeql"),
{
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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" } } },
Expand Down
45 changes: 21 additions & 24 deletions src/cli/output-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,31 @@ 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";
import { VersionInfo, versionInfoBaseSchema } from "./types";

/**
* The keys of the command cache. Each key corresponds to a command whose output we cache.
*/
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<CommandCacheKey, unknown>;
}
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<typeof outputCacheSchema> & {
entries: { version: VersionInfo };
};

/**
* The name of the temporary file that backs the on-disk cache of
Expand All @@ -43,7 +52,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);
}

Expand Down Expand Up @@ -127,30 +136,18 @@ export function getCachedCodeQlVersion(
* @param x The value to test
*/
function isVersionInfo(x: unknown): x is VersionInfo {
const candidate = x as Partial<VersionInfo> | 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")
);
return json.isObject(x) && json.validateSchema(versionInfoBaseSchema, x);
}

/**
* Determines whether a value is a `OutputCache` object.
* @param x The value to test
*/
function isOutputCache(x: unknown): x is OutputCache {
const candidate = x as Partial<OutputCache> | null;
return (
typeof candidate === "object" &&
candidate !== null &&
typeof candidate.cmd === "string" &&
candidate.entries !== undefined &&
isVersionInfo(candidate.entries.version)
json.isObject(x) &&
json.validateSchema(outputCacheSchema, x) &&
json.isObject<{ version: unknown }>(x.entries) &&
isVersionInfo(x.entries.version)
);
}
31 changes: 26 additions & 5 deletions src/cli/types.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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}.
Comment thread
mario-campos marked this conversation as resolved.
Outdated
*/
export type VersionInfoBase = json.FromSchema<typeof versionInfoBaseSchema>;

/**
* 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 };
};
Loading