Merge pull request #4246 from github/henrymercer/graduate-overlay-version-match

Graduate overlay-aware CodeQL version selection
This commit is contained in:
Henry Mercer
2026-10-22 12:52:48 +00:00
committed by GitHub
4 changed files with 110 additions and 215 deletions

57
lib/entry-points.js generated
View File

@@ -172091,16 +172091,6 @@ var featureConfig = {
minimumVersion: CODEQL_OVERLAY_MINIMUM_VERSION_RUBY
},
// Other overlay-related feature flags
["overlay_analysis_match_codeql_version" /* OverlayAnalysisMatchCodeqlVersion */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_MATCH_CODEQL_VERSION",
minimumVersion: void 0
},
["overlay_analysis_match_codeql_version_dry_run" /* OverlayAnalysisMatchCodeqlVersionDryRun */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_MATCH_CODEQL_VERSION_DRY_RUN",
minimumVersion: void 0
},
["overlay_analysis_min_disk_8_gb" /* OverlayAnalysisMinDisk8Gb */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_MIN_DISK_8_GB",
@@ -176223,17 +176213,10 @@ async function findOverridingToolsInCache(humanReadableVersion, logger2) {
}
return void 0;
}
async function getEnabledVersionsWithOverlayBaseDatabases(defaultCliVersion, rawLanguages, features, logger2) {
async function getEnabledVersionsWithOverlayBaseDatabases(defaultCliVersion, rawLanguages, logger2) {
if (rawLanguages === void 0 || rawLanguages.length === 0) {
return [];
}
const isEnabled = await features.getValue(
"overlay_analysis_match_codeql_version" /* OverlayAnalysisMatchCodeqlVersion */
);
const isDryRun = !isEnabled && await features.getValue("overlay_analysis_match_codeql_version_dry_run" /* OverlayAnalysisMatchCodeqlVersionDryRun */);
if (!isEnabled && !isDryRun) {
return [];
}
let cachedVersions;
try {
cachedVersions = await getCodeQlVersionsForOverlayBaseDatabases(
@@ -176250,46 +176233,18 @@ async function getEnabledVersionsWithOverlayBaseDatabases(defaultCliVersion, raw
return [];
}
const cachedVersionsSet = new Set(cachedVersions);
const overlayVersions = defaultCliVersion.enabledVersions.filter(
return defaultCliVersion.enabledVersions.filter(
(v) => cachedVersionsSet.has(v.cliVersion)
);
if (overlayVersions.length === 0) {
return [];
}
const isCachedVersionDifferent = overlayVersions[0].cliVersion !== defaultCliVersion.enabledVersions[0].cliVersion;
if (isCachedVersionDifferent) {
addNoLanguageDiagnostic(
void 0,
makeTelemetryDiagnostic(
"codeql-action/overlay-aware-default-codeql-version",
"Overlay-aware default CodeQL version selection",
{
cachedVersions,
enabledVersions: defaultCliVersion.enabledVersions.map(
(v) => v.cliVersion
),
isDryRun,
overlayAwareVersion: overlayVersions[0].cliVersion
}
)
);
}
if (isDryRun) {
logger2.debug(
`Overlay-aware default CodeQL version selection is running in dry-run mode. Would have used version ${overlayVersions[0].cliVersion}.`
);
return [];
}
return overlayVersions;
}
async function resolveDefaultCliVersion(defaultCliVersion, rawLanguages, useOverlayAwareDefaultCliVersion, features, logger2) {
if (!useOverlayAwareDefaultCliVersion || !isAnalyzingPullRequest()) {
async function resolveDefaultCliVersion(defaultCliVersion, rawLanguages, useOverlayAwareDefaultCliVersion, logger2) {
if (!useOverlayAwareDefaultCliVersion || // Listing the Actions cache can't change which version we use if only one version is enabled.
defaultCliVersion.enabledVersions.length === 1 || !isAnalyzingPullRequest()) {
return defaultCliVersion.enabledVersions[0];
}
const overlayVersions = await getEnabledVersionsWithOverlayBaseDatabases(
defaultCliVersion,
rawLanguages,
features,
logger2
);
if (overlayVersions.length > 0) {
@@ -176396,7 +176351,6 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, othe
defaultCliVersion,
rawLanguages,
useOverlayAwareDefaultCliVersion,
features,
logger2
);
cliVersion2 = version.cliVersion;
@@ -176416,7 +176370,6 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, othe
defaultCliVersion,
rawLanguages,
useOverlayAwareDefaultCliVersion,
features,
logger2
);
cliVersion2 = version.cliVersion;

View File

@@ -105,19 +105,6 @@ export enum Feature {
OverlayAnalysisGo = "overlay_analysis_go",
OverlayAnalysisJava = "overlay_analysis_java",
OverlayAnalysisJavascript = "overlay_analysis_javascript",
/**
* When set, chooses the default CodeQL CLI version as the highest version that is both enabled by
* feature flags and present as an overlay-base database in the Actions cache for the configured
* languages. Falls back to the highest feature flagged version if no intersecting overlay-base
* database exists in the cache.
*/
OverlayAnalysisMatchCodeqlVersion = "overlay_analysis_match_codeql_version",
/**
* Like `OverlayAnalysisMatchCodeqlVersion`, but only logs a diagnostic with the version that
* would have been chosen instead of actually changing the default CodeQL CLI version.
* `OverlayAnalysisMatchCodeqlVersion` overrides this flag.
*/
OverlayAnalysisMatchCodeqlVersionDryRun = "overlay_analysis_match_codeql_version_dry_run",
/**
* Lowers the overlay minimum available disk space to 8 GB. The lowest enabled limit wins; if
* none are enabled, the default applies.
@@ -361,16 +348,6 @@ export const featureConfig = {
minimumVersion: CODEQL_OVERLAY_MINIMUM_VERSION_RUBY,
},
// Other overlay-related feature flags
[Feature.OverlayAnalysisMatchCodeqlVersion]: {
defaultValue: false,
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_MATCH_CODEQL_VERSION",
minimumVersion: undefined,
},
[Feature.OverlayAnalysisMatchCodeqlVersionDryRun]: {
defaultValue: false,
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_MATCH_CODEQL_VERSION_DRY_RUN",
minimumVersion: undefined,
},
[Feature.OverlayAnalysisMinDisk8Gb]: {
defaultValue: false,
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_MIN_DISK_8_GB",

View File

@@ -1070,7 +1070,7 @@ async function fakeOverlayBaseCacheKey(
}
test.serial(
"getCodeQLSource uses overlay-aware default version when requested for a PR",
"getCodeQLSource uses overlay-aware default version for a Code Scanning PR",
async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
@@ -1097,7 +1097,7 @@ test.serial(
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]),
createFeatures([]),
new RecordingLogger(),
);
@@ -1109,7 +1109,95 @@ test.serial(
);
test.serial(
"getCodeQLSource skips overlay-aware default version when not requested",
"getCodeQLSource skips the Actions cache when only one default version is enabled",
async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
process.env[EnvVar.CODE_SCANNING_REF] = "refs/heads/feature-branch";
process.env[EnvVar.CODE_SCANNING_BASE_BRANCH] = "main";
sinon.stub(api, "getAutomationID").resolves("test/");
const listStub = sinon.stub(api, "listActionsCaches").resolves([
{
key: await fakeOverlayBaseCacheKey(
"javascript",
SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion,
"abc-1-1",
),
},
]);
sinon
.stub(toolcache, "find")
.withArgs(
"CodeQL",
SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion,
)
.returns("/path/to/codeql");
const source = await setupCodeql.getCodeQLSource(
undefined,
SAMPLE_DEFAULT_CLI_VERSION,
["javascript"],
undefined, // otherLanguagePacksReason
true,
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
createFeatures([]),
new RecordingLogger(),
);
t.assert(listStub.notCalled);
t.is(source.sourceType, "toolcache");
t.is(
source.toolsVersion,
SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion,
);
});
},
);
test.serial(
"getCodeQLSource skips overlay-aware default version outside pull request analysis",
async (t) => {
sinon.stub(actionsUtil, "isAnalyzingPullRequest").returns(false);
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
sinon.stub(api, "getAutomationID").resolves("test/");
const listStub = sinon.stub(api, "listActionsCaches").resolves([
{
key: await fakeOverlayBaseCacheKey("javascript", "2.20.1", "abc-1-1"),
},
]);
sinon
.stub(toolcache, "find")
.withArgs("CodeQL", "2.20.2")
.returns("/path/to/codeql-2.20.2");
const source = await setupCodeql.getCodeQLSource(
undefined,
overlayMatchEnabledVersions,
["javascript"],
undefined, // otherLanguagePacksReason
true,
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
createFeatures([]),
new RecordingLogger(),
);
t.assert(listStub.notCalled);
t.is(source.sourceType, "toolcache");
t.is(source.toolsVersion, "2.20.2");
});
},
);
test.serial(
"getCodeQLSource skips overlay-aware default version when not requested for Code Scanning",
async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
@@ -1136,7 +1224,7 @@ test.serial(
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]),
createFeatures([]),
new RecordingLogger(),
);
@@ -1608,7 +1696,7 @@ test.serial(
);
test.serial(
"getEnabledVersionsWithOverlayBaseDatabases returns flag-enabled versions present in cache, sorted desc",
"getEnabledVersionsWithOverlayBaseDatabases returns enabled versions present in cache, sorted desc",
async (t) => {
sinon.stub(api, "getAutomationID").resolves("test/");
sinon.stub(api, "listActionsCaches").resolves([
@@ -1629,7 +1717,6 @@ test.serial(
const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases(
overlayMatchEnabledVersions,
["javascript"],
createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]),
new RecordingLogger(),
);
t.deepEqual(result, [
@@ -1640,7 +1727,7 @@ test.serial(
);
test.serial(
"getEnabledVersionsWithOverlayBaseDatabases returns empty when no cached version is flag-enabled",
"getEnabledVersionsWithOverlayBaseDatabases returns empty when no cached version is enabled",
async (t) => {
sinon.stub(api, "getAutomationID").resolves("test/");
sinon.stub(api, "listActionsCaches").resolves([
@@ -1652,7 +1739,6 @@ test.serial(
const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases(
overlayMatchEnabledVersions,
["javascript"],
createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]),
new RecordingLogger(),
);
t.deepEqual(result, []);
@@ -1669,7 +1755,6 @@ const noLanguagesMacro = makeMacro({
const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases(
overlayMatchEnabledVersions,
rawLanguages,
createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]),
new RecordingLogger(),
);
t.deepEqual(result, []);
@@ -1694,7 +1779,6 @@ test.serial(
const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases(
overlayMatchEnabledVersions,
["javascript"],
createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]),
new RecordingLogger(),
);
t.deepEqual(result, []);
@@ -1714,7 +1798,6 @@ test.serial(
const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases(
overlayMatchEnabledVersions,
["javascript"],
createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]),
new RecordingLogger(),
);
t.deepEqual(result, [
@@ -1723,78 +1806,6 @@ test.serial(
},
);
test.serial(
"getEnabledVersionsWithOverlayBaseDatabases does not list caches when both gates are off",
async (t) => {
const listStub = sinon.stub(api, "listActionsCaches").resolves([]);
const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases(
overlayMatchEnabledVersions,
["javascript"],
createFeatures([]),
new RecordingLogger(),
);
t.deepEqual(result, []);
t.assert(
listStub.notCalled,
"Should not list Actions caches when both gating feature flags are off.",
);
},
);
test.serial(
"getEnabledVersionsWithOverlayBaseDatabases dry-run returns empty but lists caches",
async (t) => {
sinon.stub(api, "getAutomationID").resolves("test/");
const listStub = sinon.stub(api, "listActionsCaches").resolves([
{
key: await fakeOverlayBaseCacheKey("javascript", "2.20.1", "abc-1-1"),
},
]);
const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases(
overlayMatchEnabledVersions,
["javascript"],
createFeatures([Feature.OverlayAnalysisMatchCodeqlVersionDryRun]),
new RecordingLogger(),
);
t.deepEqual(
result,
[],
"Dry-run should return an empty list so the caller falls back.",
);
t.assert(
listStub.calledOnce,
"Dry-run should still list Actions caches to populate the diagnostic.",
);
},
);
test.serial(
"getEnabledVersionsWithOverlayBaseDatabases match flag wins over dry-run",
async (t) => {
sinon.stub(api, "getAutomationID").resolves("test/");
sinon.stub(api, "listActionsCaches").resolves([
{
key: await fakeOverlayBaseCacheKey("javascript", "2.20.1", "abc-1-1"),
},
]);
const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases(
overlayMatchEnabledVersions,
["javascript"],
createFeatures([
Feature.OverlayAnalysisMatchCodeqlVersion,
Feature.OverlayAnalysisMatchCodeqlVersionDryRun,
]),
new RecordingLogger(),
);
t.deepEqual(result, [
{ cliVersion: "2.20.1", tagName: "codeql-bundle-v2.20.1" },
]);
},
);
/** The CLI version that the toolcache cleanup tests download. */
const CLEANUP_CLI_VERSION = "2.21.0";
/** The bundle version that the toolcache cleanup tests download. */

View File

@@ -183,29 +183,16 @@ async function findOverridingToolsInCache(
/**
* Returns the sorted set of enabled versions that have cached overlay-base databases for the
* given languages, or an empty list if neither the `OverlayAnalysisMatchCodeqlVersion` nor the
* `OverlayAnalysisMatchCodeqlVersionDryRun` feature flag is enabled. When only the dry-run flag
* is enabled, this performs the lookup and emits a telemetry diagnostic with the version that
* would have been chosen, but still returns an empty list so the caller falls back.
* given languages.
*/
export async function getEnabledVersionsWithOverlayBaseDatabases(
defaultCliVersion: CodeQLDefaultVersionInfo,
rawLanguages: string[] | undefined,
features: FeatureEnablement,
logger: Logger,
): Promise<CodeQLVersionInfo[]> {
if (rawLanguages === undefined || rawLanguages.length === 0) {
return [];
}
const isEnabled = await features.getValue(
Feature.OverlayAnalysisMatchCodeqlVersion,
);
const isDryRun =
!isEnabled &&
(await features.getValue(Feature.OverlayAnalysisMatchCodeqlVersionDryRun));
if (!isEnabled && !isDryRun) {
return [];
}
let cachedVersions: string[] | undefined;
try {
@@ -226,66 +213,35 @@ export async function getEnabledVersionsWithOverlayBaseDatabases(
}
const cachedVersionsSet = new Set(cachedVersions);
const overlayVersions = defaultCliVersion.enabledVersions.filter((v) =>
return defaultCliVersion.enabledVersions.filter((v) =>
cachedVersionsSet.has(v.cliVersion),
);
if (overlayVersions.length === 0) {
return [];
}
const isCachedVersionDifferent =
overlayVersions[0].cliVersion !==
defaultCliVersion.enabledVersions[0].cliVersion;
if (isCachedVersionDifferent) {
addNoLanguageDiagnostic(
undefined,
makeTelemetryDiagnostic(
"codeql-action/overlay-aware-default-codeql-version",
"Overlay-aware default CodeQL version selection",
{
cachedVersions,
enabledVersions: defaultCliVersion.enabledVersions.map(
(v) => v.cliVersion,
),
isDryRun,
overlayAwareVersion: overlayVersions[0].cliVersion,
},
),
);
}
if (isDryRun) {
logger.debug(
`Overlay-aware default CodeQL version selection is running in dry-run mode. Would have used version ${overlayVersions[0].cliVersion}.`,
);
return [];
}
return overlayVersions;
}
/**
* Resolves the newest enabled default CLI version that has a cached overlay-base database for the
* relevant languages, if running a Code Scanning analysis for a pull request and one exists.
* Otherwise, falls back to the newest enabled default CLI version.
* relevant languages, if this is a Code Scanning analysis of a pull request, multiple default
* versions are enabled, and such a version exists. Otherwise, falls back to the newest enabled
* default CLI version.
*/
async function resolveDefaultCliVersion(
defaultCliVersion: CodeQLDefaultVersionInfo,
rawLanguages: string[] | undefined,
useOverlayAwareDefaultCliVersion: boolean,
features: FeatureEnablement,
logger: Logger,
): Promise<CodeQLVersionInfo> {
if (!useOverlayAwareDefaultCliVersion || !isAnalyzingPullRequest()) {
if (
!useOverlayAwareDefaultCliVersion ||
// Listing the Actions cache can't change which version we use if only one version is enabled.
defaultCliVersion.enabledVersions.length === 1 ||
!isAnalyzingPullRequest()
) {
return defaultCliVersion.enabledVersions[0];
}
const overlayVersions = await getEnabledVersionsWithOverlayBaseDatabases(
defaultCliVersion,
rawLanguages,
features,
logger,
);
if (overlayVersions.length > 0) {
@@ -482,7 +438,6 @@ export async function getCodeQLSource(
defaultCliVersion,
rawLanguages,
useOverlayAwareDefaultCliVersion,
features,
logger,
);
cliVersion = version.cliVersion;
@@ -505,7 +460,6 @@ export async function getCodeQLSource(
defaultCliVersion,
rawLanguages,
useOverlayAwareDefaultCliVersion,
features,
logger,
);
cliVersion = version.cliVersion;