Match CLI version to cached overlay-base database

This commit is contained in:
Henry Mercer
2026-05-06 17:42:31 +01:00
parent b0942116d7
commit 55d6319f96
19 changed files with 2437 additions and 1101 deletions

View File

@@ -72,6 +72,7 @@ async function installIntoToolcache({
cliVersion !== undefined
? { enabledVersions: [{ cliVersion, tagName }] }
: SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
createFeatures([]),
getRunnerLogger(true),
false,
@@ -143,6 +144,7 @@ test.serial(
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
features,
getRunnerLogger(true),
false,
@@ -175,6 +177,7 @@ test.serial(
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
features,
getRunnerLogger(true),
false,
@@ -214,6 +217,7 @@ test.serial(
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
features,
getRunnerLogger(true),
false,
@@ -264,6 +268,7 @@ for (const {
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
features,
getRunnerLogger(true),
false,
@@ -308,6 +313,7 @@ for (const toolcacheVersion of [
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
features,
getRunnerLogger(true),
false,
@@ -352,6 +358,7 @@ test.serial(
},
],
},
undefined, // rawLanguages
features,
getRunnerLogger(true),
false,
@@ -398,6 +405,7 @@ test.serial(
},
],
},
undefined, // rawLanguages
features,
getRunnerLogger(true),
false,
@@ -437,6 +445,7 @@ test.serial(
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
features,
getRunnerLogger(true),
false,
@@ -478,6 +487,7 @@ test.serial(
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
features,
getRunnerLogger(true),
false,

View File

@@ -305,6 +305,7 @@ const EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++";
* @param tempDir
* @param variant
* @param defaultCliVersion
* @param rawLanguages Raw set of languages.
* @param features Information about the features that are enabled.
* @param logger
* @param checkVersion Whether to check that CodeQL CLI meets the minimum
@@ -317,6 +318,7 @@ export async function setupCodeQL(
tempDir: string,
variant: util.GitHubVariant,
defaultCliVersion: CodeQLDefaultVersionInfo,
rawLanguages: string[] | undefined,
features: FeatureEnablement,
logger: Logger,
checkVersion: boolean,
@@ -340,6 +342,7 @@ export async function setupCodeQL(
tempDir,
variant,
defaultCliVersion,
rawLanguages,
features,
logger,
);

View File

@@ -301,12 +301,16 @@ async function run(startedAt: Date) {
const codeQLDefaultVersionInfo =
await features.getEnabledDefaultCliVersions(gitHubVersion.type);
toolsFeatureFlagsValid = codeQLDefaultVersionInfo.toolsFeatureFlagsValid;
const rawLanguages = configUtils.getRawLanguagesNoAutodetect(
getOptionalInput("languages"),
);
const initCodeQLResult = await initCodeQL(
getOptionalInput("tools"),
apiDetails,
getTemporaryDirectory(),
gitHubVersion.type,
codeQLDefaultVersionInfo,
rawLanguages,
features,
logger,
);

View File

@@ -39,6 +39,7 @@ export async function initCodeQL(
tempDir: string,
variant: util.GitHubVariant,
defaultCliVersion: CodeQLDefaultVersionInfo,
rawLanguages: string[] | undefined,
features: FeatureEnablement,
logger: Logger,
): Promise<{
@@ -61,6 +62,7 @@ export async function initCodeQL(
tempDir,
variant,
defaultCliVersion,
rawLanguages,
features,
logger,
true,

View File

@@ -145,6 +145,7 @@ async function run(startedAt: Date): Promise<void> {
getTemporaryDirectory(),
gitHubVersion.type,
codeQLDefaultVersionInfo,
undefined, // rawLanguages: currently, setup-codeql is not language aware
features,
logger,
);

View File

@@ -107,6 +107,7 @@ test.serial(
const source = await setupCodeql.getCodeQLSource(
`https://github.com/github/codeql-action/releases/download/${tagName}/codeql-bundle-linux64.tar.gz`,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
@@ -130,6 +131,7 @@ test.serial(
const source = await setupCodeql.getCodeQLSource(
"linked",
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
@@ -155,6 +157,7 @@ test.serial(
const source = await setupCodeql.getCodeQLSource(
"latest",
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
@@ -211,6 +214,7 @@ test.serial(
"tmp/codeql_action_test/",
GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
features,
logger,
);
@@ -266,6 +270,7 @@ test.serial(
"tmp/codeql_action_test/",
GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
features,
logger,
);
@@ -317,6 +322,7 @@ test.serial(
const source = await setupCodeql.getCodeQLSource(
"nightly",
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
@@ -378,6 +384,7 @@ test.serial(
const source = await setupCodeql.getCodeQLSource(
undefined,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
@@ -432,6 +439,7 @@ test.serial(
const source = await setupCodeql.getCodeQLSource(
"toolcache",
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
@@ -499,6 +507,7 @@ const toolcacheInputFallbackMacro = test.macro({
const source = await setupCodeql.getCodeQLSource(
"toolcache",
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
@@ -601,3 +610,155 @@ test.serial(
t.is(setupCodeql.getLatestToolcacheVersion(getRunnerLogger(true)), "3.2.1");
},
);
function makeOverlayMatchFeatures(
matchFlagEnabled: boolean,
): FeatureEnablement {
return {
getEnabledDefaultCliVersions: async () => {
throw new Error("not implemented");
},
getValue: async (feature) => {
if (feature === Feature.OverlayAnalysisMatchCodeqlVersion) {
return matchFlagEnabled;
}
return false;
},
};
}
const overlayMatchEnabledVersions = {
enabledVersions: [
{ cliVersion: "2.20.2", tagName: "codeql-bundle-v2.20.2" },
{ cliVersion: "2.20.1", tagName: "codeql-bundle-v2.20.1" },
{ cliVersion: "2.20.0", tagName: "codeql-bundle-v2.20.0" },
],
toolsFeatureFlagsValid: true,
};
test.serial(
"getEnabledVersionsWithOverlayBaseDatabases returns flag-enabled versions present in cache, sorted desc",
async (t) => {
sinon.stub(api, "getAutomationID").resolves("test/");
sinon.stub(api, "listActionsCaches").resolves([
// Newer than any flag-enabled version: should be filtered out.
{
key: "codeql-overlay-base-database-1-aaaaaaaaaaaaaaaa-javascript-2.21.0-abc-1-1",
},
// Flag-enabled versions present in the cache.
{
key: "codeql-overlay-base-database-1-aaaaaaaaaaaaaaaa-javascript-2.20.1-def-2-1",
},
{
key: "codeql-overlay-base-database-1-aaaaaaaaaaaaaaaa-javascript-2.20.0-ghi-3-1",
},
]);
const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases(
overlayMatchEnabledVersions,
["javascript"],
makeOverlayMatchFeatures(true),
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" },
]);
},
);
test.serial(
"getEnabledVersionsWithOverlayBaseDatabases returns empty when no cached version is flag-enabled",
async (t) => {
sinon.stub(api, "getAutomationID").resolves("test/");
sinon.stub(api, "listActionsCaches").resolves([
{
key: "codeql-overlay-base-database-1-aaaaaaaaaaaaaaaa-javascript-2.19.0-abc-1-1",
},
]);
const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases(
overlayMatchEnabledVersions,
["javascript"],
makeOverlayMatchFeatures(true),
getRunnerLogger(true),
);
t.deepEqual(result, []);
},
);
test.serial(
"getEnabledVersionsWithOverlayBaseDatabases does not list caches when gate is off",
async (t) => {
const listStub = sinon.stub(api, "listActionsCaches").resolves([]);
const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases(
overlayMatchEnabledVersions,
["javascript"],
makeOverlayMatchFeatures(false),
getRunnerLogger(true),
);
t.deepEqual(result, []);
t.assert(
listStub.notCalled,
"Should not list Actions caches when the gating feature flag is off.",
);
},
);
test.serial(
"getEnabledVersionsWithOverlayBaseDatabases does not list caches when rawLanguages is empty",
async (t) => {
const listStub = sinon.stub(api, "listActionsCaches").resolves([]);
const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases(
overlayMatchEnabledVersions,
undefined,
makeOverlayMatchFeatures(true),
getRunnerLogger(true),
);
t.deepEqual(result, []);
t.assert(
listStub.notCalled,
"Should not list Actions caches without rawLanguages.",
);
},
);
test.serial(
"getEnabledVersionsWithOverlayBaseDatabases returns empty when listing caches throws",
async (t) => {
sinon.stub(api, "getAutomationID").resolves("test/");
sinon.stub(api, "listActionsCaches").rejects(new Error("listing failed"));
const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases(
overlayMatchEnabledVersions,
["javascript"],
makeOverlayMatchFeatures(true),
getRunnerLogger(true),
);
t.deepEqual(result, []);
},
);
test.serial(
"getEnabledVersionsWithOverlayBaseDatabases includes the highest version when it is cached",
async (t) => {
sinon.stub(api, "getAutomationID").resolves("test/");
sinon.stub(api, "listActionsCaches").resolves([
{
key: "codeql-overlay-base-database-1-aaaaaaaaaaaaaaaa-javascript-2.20.2-abc-1-1",
},
]);
const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases(
overlayMatchEnabledVersions,
["javascript"],
makeOverlayMatchFeatures(true),
getRunnerLogger(true),
);
t.deepEqual(result, [
{ cliVersion: "2.20.2", tagName: "codeql-bundle-v2.20.2" },
]);
},
);

View File

@@ -7,17 +7,23 @@ import { default as deepEqual } from "fast-deep-equal";
import * as semver from "semver";
import { v4 as uuidV4 } from "uuid";
import { isDynamicWorkflow, isRunningLocalAction } from "./actions-util";
import {
isAnalyzingPullRequest,
isDynamicWorkflow,
isRunningLocalAction,
} from "./actions-util";
import * as api from "./api-client";
import * as defaults from "./defaults.json";
import { addNoLanguageDiagnostic, makeDiagnostic } from "./diagnostics";
import {
CODEQL_VERSION_ZSTD_BUNDLE,
CodeQLDefaultVersionInfo,
CodeQLVersionInfo,
Feature,
FeatureEnablement,
} from "./feature-flags";
import { Logger } from "./logging";
import { getCodeQlVersionsForOverlayBaseDatabases } from "./overlay/caching";
import * as tar from "./tar";
import {
downloadAndExtract,
@@ -264,12 +270,84 @@ async function findOverridingToolsInCache(
return undefined;
}
/** Returns the sorted set of enabled versions that have cached overlay-base databases. */
export async function getEnabledVersionsWithOverlayBaseDatabases(
defaultCliVersion: CodeQLDefaultVersionInfo,
rawLanguages: string[] | undefined,
features: FeatureEnablement,
logger: Logger,
): Promise<CodeQLVersionInfo[]> {
if (rawLanguages === undefined || rawLanguages.length === 0) {
return [];
}
if (!(await features.getValue(Feature.OverlayAnalysisMatchCodeqlVersion))) {
return [];
}
let cachedVersions: string[] | undefined;
try {
cachedVersions = await getCodeQlVersionsForOverlayBaseDatabases(
rawLanguages,
logger,
);
} catch (e) {
logger.warning(
`While setting up CodeQL, was unable to list overlay-base databases in the Actions cache. Details: ${e}`,
);
return [];
}
if (cachedVersions === undefined || cachedVersions.length === 0) {
return [];
}
const cachedVersionsSet = new Set(cachedVersions);
return defaultCliVersion.enabledVersions.filter((v) =>
cachedVersionsSet.has(v.cliVersion),
);
}
/**
* Resolves the newest enabled default CLI version that has a cached overlay-base database for the
* relevant languages, if analyzing a pull request and one exists. Otherwise, falls back to the
* newest enabled default CLI version.
*/
async function resolveDefaultCliVersion(
defaultCliVersion: CodeQLDefaultVersionInfo,
rawLanguages: string[] | undefined,
features: FeatureEnablement,
logger: Logger,
): Promise<CodeQLVersionInfo> {
if (!isAnalyzingPullRequest()) {
return defaultCliVersion.enabledVersions[0];
}
const overlayVersions = await getEnabledVersionsWithOverlayBaseDatabases(
defaultCliVersion,
rawLanguages,
features,
logger,
);
if (overlayVersions.length > 0) {
logger.info(
`Using CodeQL version ${overlayVersions[0].cliVersion} since this is the ` +
`highest enabled version that has a cached overlay-base database.`,
);
return overlayVersions[0];
}
logger.info(
`Using CodeQL version ${defaultCliVersion.enabledVersions[0].cliVersion} since no enabled ` +
`versions with cached overlay-base databases were found.`,
);
return defaultCliVersion.enabledVersions[0];
}
/**
* Determines where the CodeQL CLI we want to use comes from. This can be from a local file,
* the Actions toolcache, or a download.
*
* @param toolsInput The argument provided for the `tools` input, if any.
* @param defaultCliVersion The default CLI version that's linked to the CodeQL Action.
* @param rawLanguages Raw set of languages.
* @param apiDetails Information about the GitHub API.
* @param variant The GitHub variant we are running on.
* @param tarSupportsZstd Whether zstd is supported by `tar`.
@@ -281,6 +359,7 @@ async function findOverridingToolsInCache(
export async function getCodeQLSource(
toolsInput: string | undefined,
defaultCliVersion: CodeQLDefaultVersionInfo,
rawLanguages: string[] | undefined,
apiDetails: api.GitHubApiDetails,
variant: util.GitHubVariant,
tarSupportsZstd: boolean,
@@ -438,8 +517,14 @@ export async function getCodeQLSource(
}
}
cliVersion = defaultCliVersion.enabledVersions[0].cliVersion;
tagName = defaultCliVersion.enabledVersions[0].tagName;
const version = await resolveDefaultCliVersion(
defaultCliVersion,
rawLanguages,
features,
logger,
);
cliVersion = version.cliVersion;
tagName = version.tagName;
}
} else if (toolsInput !== undefined) {
// If a tools URL was provided, then use that.
@@ -454,9 +539,14 @@ export async function getCodeQLSource(
}
}
} else {
// Otherwise, use the default CLI version passed in.
cliVersion = defaultCliVersion.enabledVersions[0].cliVersion;
tagName = defaultCliVersion.enabledVersions[0].tagName;
const version = await resolveDefaultCliVersion(
defaultCliVersion,
rawLanguages,
features,
logger,
);
cliVersion = version.cliVersion;
tagName = version.tagName;
}
const bundleVersion =
@@ -791,6 +881,7 @@ export async function setupCodeQLBundle(
tempDir: string,
variant: util.GitHubVariant,
defaultCliVersion: CodeQLDefaultVersionInfo,
rawLanguages: string[] | undefined,
features: FeatureEnablement,
logger: Logger,
): Promise<SetupCodeQLResult> {
@@ -804,6 +895,7 @@ export async function setupCodeQLBundle(
const source = await getCodeQLSource(
toolsInput,
defaultCliVersion,
rawLanguages,
apiDetails,
variant,
zstdAvailability.available,

View File

@@ -165,6 +165,7 @@ async function combineSarifFilesUsingCLI(
tempDir,
gitHubVersion.type,
codeQLDefaultVersionInfo,
undefined, // rawLanguages: upload-lib does not run analysis
features,
logger,
);