Merge pull request #4244 from github/henrymercer/graduate-per-language-bundles

Use per-language CodeQL bundles by default
This commit is contained in:
Henry Mercer
2026-10-22 14:01:48 +00:00
committed by GitHub
10 changed files with 143 additions and 200 deletions

View File

@@ -143,11 +143,6 @@ export enum Feature {
OverlayAnalysisStatusCheck = "overlay_analysis_status_check",
/** Controls whether overlay build failures on the default branch are stored in the Actions cache. */
OverlayAnalysisStatusSave = "overlay_analysis_status_save",
/**
* Controls whether we may download a bundle containing only the single language being analysed,
* rather than the combined bundle that contains every language.
*/
PerLanguageBundles = "per_language_bundles_v2",
QaTelemetryEnabled = "qa_telemetry_enabled",
/** Routes (some) API requests through the registry proxy. */
ProxyApiRequests = "proxy_api_requests",
@@ -393,11 +388,6 @@ export const featureConfig = {
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_SKIP_RESOURCE_CHECKS",
minimumVersion: undefined,
},
[Feature.PerLanguageBundles]: {
defaultValue: false,
envVar: "CODEQL_ACTION_PER_LANGUAGE_BUNDLES",
minimumVersion: undefined,
},
[Feature.QaTelemetryEnabled]: {
defaultValue: false,
envVar: "CODEQL_ACTION_QA_TELEMETRY",

View File

@@ -1,7 +1,6 @@
import test from "ava";
import { ActionsEnvVars } from "./environment";
import { Feature } from "./feature-flags";
import { BuiltInLanguage } from "./languages";
import {
getOtherLanguagePacksReason,
@@ -12,7 +11,6 @@ import {
} from "./per-language-bundles";
import { BundlePlatform } from "./platform";
import {
createFeatures,
getRecordingLogger,
getTestEnv,
initAllState,
@@ -40,7 +38,6 @@ async function checkEligibility(
env: getTestEnv({
[ActionsEnvVars.RUNNER_ENVIRONMENT]: "github-hosted",
}),
features: createFeatures([Feature.PerLanguageBundles]),
...stateOverrides,
}),
{ ...ELIGIBLE_OPTIONS, ...overrides },
@@ -145,27 +142,6 @@ test("getPerLanguageBundleLanguage requires a supported release version", async
t.is(await checkEligibility({ cliVersion: "2.27.1" }), BuiltInLanguage.java);
});
test("getPerLanguageBundleLanguage requires the feature flag", async (t) => {
t.is(await checkEligibility({}, { features: createFeatures([]) }), undefined);
});
test("getPerLanguageBundleLanguage explains a disabled feature before checking eligibility", async (t) => {
const messages: LoggedMessage[] = [];
const language = await getPerLanguageBundleLanguage(
initAllState({
features: createFeatures([]),
logger: getRecordingLogger(messages, { logToConsole: false }),
}),
{ ...ELIGIBLE_OPTIONS, rawLanguages: undefined, cliVersion: undefined },
);
t.is(language, undefined);
t.deepEqual(
messages.map((message) => message.message),
["Not using a per-language CodeQL bundle since the feature is disabled."],
);
});
test("getPerLanguageBundleLanguage skips only the release version check for the latest nightly", async (t) => {
const nightly = { isLatestNightly: true, cliVersion: undefined };
t.is(await checkEligibility(nightly), BuiltInLanguage.java);
@@ -181,10 +157,6 @@ test("getPerLanguageBundleLanguage skips only the release version check for the
]) {
t.is(await checkEligibility({ ...nightly, ...overrides }), undefined);
}
t.is(
await checkEligibility(nightly, { features: createFeatures([]) }),
undefined,
);
t.is(
await checkEligibility(nightly, {
env: getTestEnv({ [ActionsEnvVars.RUNNER_ENVIRONMENT]: "self-hosted" }),

View File

@@ -9,7 +9,6 @@ import {
QuerySpec,
UserConfig,
} from "./config/db-config";
import { Feature } from "./feature-flags";
import { RepositoryPropertyName } from "./feature-flags/properties";
import { BuiltInLanguage, parseBuiltInLanguage } from "./languages";
import { BundlePlatform } from "./platform";
@@ -139,14 +138,10 @@ export interface PerLanguageBundleOptions {
}
/** Returns the eligible bundle language, or undefined for the combined bundle. */
export async function getPerLanguageBundleLanguage(
{
env,
features,
logger,
}: ActionState<["Logger", "ReadOnlyEnv", "FeatureFlags"]>,
export function getPerLanguageBundleLanguage(
action: ActionState<["Logger", "ReadOnlyEnv"]>,
options: PerLanguageBundleOptions,
): Promise<BuiltInLanguage | undefined> {
): BuiltInLanguage | undefined {
const {
rawLanguages,
otherLanguagePacksReason,
@@ -158,14 +153,12 @@ export async function getPerLanguageBundleLanguage(
} = options;
const explain = (reason: string) => {
logger.debug(`Not using a per-language CodeQL bundle since ${reason}.`);
action.logger.debug(
`Not using a per-language CodeQL bundle since ${reason}.`,
);
return undefined;
};
if (!(await features.getValue(Feature.PerLanguageBundles))) {
return explain("the feature is disabled");
}
// A defined reason means the CodeQL CLI may need packs for other languages, for example because
// of the configured queries. That applies whichever languages were requested, so check it first
// to avoid suggesting that requesting a single language would be enough.
@@ -197,7 +190,7 @@ export async function getPerLanguageBundleLanguage(
return explain(`we are running against ${variant}`);
}
if (!isGitHubHostedRunner(env)) {
if (!isGitHubHostedRunner(action.env)) {
// Per-language installs stay out of the toolcache; self-hosted runners should retain
// the reusable combined bundle instead.
return explain("the job is not running on a GitHub-hosted runner");

View File

@@ -439,7 +439,7 @@ test.serial(
tmpDir,
GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
["javascript"],
undefined, // rawLanguages
undefined, // otherLanguagePacksReason
false, // useOverlayAwareDefaultCliVersion
features,
@@ -633,7 +633,7 @@ for (const toolsInput of ["nightly", "nightly-latest"]) {
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
true, // tarSupportsZstd
createFeatures([Feature.PerLanguageBundles]),
createFeatures([]),
new RecordingLogger(),
);
@@ -664,24 +664,16 @@ test.serial(
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
for (const { languages, otherLanguagePacksReason, features } of [
// The per-language feature is disabled.
{
languages: ["java"],
otherLanguagePacksReason: undefined,
features: createFeatures([]),
},
for (const { languages, otherLanguagePacksReason } of [
// More than one language requires a combined bundle.
{
languages: ["java", "python"],
otherLanguagePacksReason: undefined,
features: createFeatures([Feature.PerLanguageBundles]),
},
// The configured queries may need library packs for other languages.
{
languages: ["java"],
otherLanguagePacksReason: "an example reason",
features: createFeatures([Feature.PerLanguageBundles]),
},
]) {
const source = await setupCodeql.getCodeQLSource(
@@ -693,7 +685,7 @@ test.serial(
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
true, // tarSupportsZstd
features,
createFeatures([]),
new RecordingLogger(),
);
@@ -709,58 +701,48 @@ test.serial(
},
);
for (const perLanguageBundles of [false, true]) {
test.serial(
`getCodeQLSource uses the latest ${perLanguageBundles ? "per-language" : "combined"} bundle for a forced nightly`,
async (t) => {
const expectedTag = "codeql-bundle-30260213";
const baseURL = `https://github.com/dsp-testing/codeql-cli-nightlies/releases/download/${expectedTag}`;
const latestNightlyRequest = stubHostedNightly(expectedTag);
test.serial(
"getCodeQLSource uses the latest per-language bundle for a forced nightly",
async (t) => {
const expectedTag = "codeql-bundle-30260213";
const baseURL = `https://github.com/dsp-testing/codeql-cli-nightlies/releases/download/${expectedTag}`;
const latestNightlyRequest = stubHostedNightly(expectedTag);
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir, { GITHUB_EVENT_NAME: "dynamic" });
const source = await setupCodeql.getCodeQLSource(
undefined, // toolsInput: the nightly is selected by ForceNightly
SAMPLE_DEFAULT_CLI_VERSION,
["java"],
undefined, // otherLanguagePacksReason
false, // useOverlayAwareDefaultCliVersion
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
true, // tarSupportsZstd
createFeatures(
perLanguageBundles
? [Feature.ForceNightly, Feature.PerLanguageBundles]
: [Feature.ForceNightly],
),
new RecordingLogger(),
);
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir, { GITHUB_EVENT_NAME: "dynamic" });
const source = await setupCodeql.getCodeQLSource(
undefined, // toolsInput: the nightly is selected by ForceNightly
SAMPLE_DEFAULT_CLI_VERSION,
["java"],
undefined, // otherLanguagePacksReason
false, // useOverlayAwareDefaultCliVersion
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
true, // tarSupportsZstd
createFeatures([Feature.ForceNightly]),
new RecordingLogger(),
);
t.is(source.sourceType, "download");
t.true(latestNightlyRequest.calledOnce);
if (source.sourceType === "download") {
const combinedURL = `${baseURL}/codeql-bundle-linux64.tar.zst`;
t.deepEqual(
source.bundle,
perLanguageBundles
? {
kind: "per-language",
language: BuiltInLanguage.java,
url: `${baseURL}/codeql-bundle-java-linux64.tar.zst`,
combinedBundleURL: combinedURL,
}
: { kind: "combined", url: combinedURL },
);
}
});
},
);
}
t.is(source.sourceType, "download");
t.true(latestNightlyRequest.calledOnce);
if (source.sourceType === "download") {
t.deepEqual(source.bundle, {
kind: "per-language",
language: BuiltInLanguage.java,
url: `${baseURL}/codeql-bundle-java-linux64.tar.zst`,
combinedBundleURL: `${baseURL}/codeql-bundle-linux64.tar.zst`,
});
}
});
},
);
for (const date of ["20200101", "30260213"]) {
for (const bundle of ["combined", "per-language"] as const) {
test.serial(
`getCodeQLSource preserves an explicit ${bundle} nightly URL for ${date}`,
`getCodeQLSource preserves an explicit ${bundle} nightly URL for ${date}${
bundle === "per-language" ? " on GHES" : ""
}`,
async (t) => {
const latestNightlyRequest = stubHostedNightly(
"codeql-bundle-30260213",
@@ -770,7 +752,9 @@ for (const date of ["20200101", "30260213"]) {
? "codeql-bundle-linux64.tar.zst"
: "codeql-bundle-java-linux64.tar.zst";
const url = `https://github.com/dsp-testing/codeql-cli-nightlies/releases/download/codeql-bundle-${date}/${asset}`;
const features = createFeatures([Feature.PerLanguageBundles]);
const features = createFeatures([]);
const variant =
bundle === "per-language" ? GitHubVariant.GHES : GitHubVariant.DOTCOM;
const logger = getRecordingLogger([], { logToConsole: false });
const error = new HTTPError("Not Found", 404);
const extractStub = sinon
@@ -786,7 +770,7 @@ for (const date of ["20200101", "30260213"]) {
undefined, // otherLanguagePacksReason
false,
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
variant,
true,
features,
logger,
@@ -808,7 +792,7 @@ for (const date of ["20200101", "30260213"]) {
url,
SAMPLE_DOTCOM_API_DETAILS,
tmpDir,
GitHubVariant.DOTCOM,
variant,
SAMPLE_DEFAULT_CLI_VERSION,
["java"],
undefined, // otherLanguagePacksReason
@@ -1262,7 +1246,7 @@ test.serial(
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
true, // tarSupportsZstd
createFeatures([Feature.PerLanguageBundles]),
createFeatures([]),
new RecordingLogger(),
);
@@ -1295,13 +1279,16 @@ test.serial(
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
for (const { otherLanguagePacksReason, features } of [
// The per-language feature is disabled.
{ otherLanguagePacksReason: undefined, features: createFeatures([]) },
for (const { otherLanguagePacksReason, variant } of [
// Automatic per-language selection is limited to GitHub.com.
{
otherLanguagePacksReason: undefined,
variant: GitHubVariant.GHES,
},
// The configured queries may need library packs for other languages.
{
otherLanguagePacksReason: "an example reason",
features: createFeatures([Feature.PerLanguageBundles]),
variant: GitHubVariant.DOTCOM,
},
]) {
const source = await setupCodeql.getCodeQLSource(
@@ -1311,9 +1298,9 @@ test.serial(
otherLanguagePacksReason,
false, // useOverlayAwareDefaultCliVersion
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
variant,
true, // tarSupportsZstd
features,
createFeatures([]),
new RecordingLogger(),
);
@@ -1383,7 +1370,7 @@ for (const fallback of [false, true]) {
["java"],
undefined, // otherLanguagePacksReason
false, // useOverlayAwareDefaultCliVersion
createFeatures([Feature.PerLanguageBundles]),
createFeatures([]),
new RecordingLogger(),
);
@@ -1463,7 +1450,7 @@ for (const bundle of ["per-language", "combined", "fallback"] as const) {
};
});
const addDiagnostic = sinon.stub(diagnostics, "addNoLanguageDiagnostic");
const features = createFeatures([Feature.PerLanguageBundles]);
const features = createFeatures([]);
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
@@ -1648,7 +1635,7 @@ for (const error of [
["java"],
undefined, // otherLanguagePacksReason
false, // useOverlayAwareDefaultCliVersion
createFeatures([Feature.PerLanguageBundles]),
createFeatures([]),
new RecordingLogger(),
),
{ is: error },

View File

@@ -364,7 +364,7 @@ export async function getCodeQLSource(
);
}
bundle = await getLatestNightlyBundle(
{ env: getEnv(), features, logger },
{ env: getEnv(), logger },
rawLanguages,
otherLanguagePacksReason,
variant,
@@ -605,17 +605,14 @@ export async function getCodeQLSource(
const action = { env: getEnv(), logger };
const platform = getBundlePlatform();
const perLanguageBundleLanguage = await getPerLanguageBundleLanguage(
{ ...action, features },
{
rawLanguages,
otherLanguagePacksReason,
cliVersion,
compressionMethod,
platform,
variant,
},
);
const perLanguageBundleLanguage = getPerLanguageBundleLanguage(action, {
rawLanguages,
otherLanguagePacksReason,
cliVersion,
compressionMethod,
platform,
variant,
});
// Resolves the combined or per-language bundle URL for the requested release.
const resolveBundleURL = (language?: BuiltInLanguage) =>
@@ -1043,7 +1040,7 @@ function getTempExtractionDir(tempDir: string) {
* Records the combined bundle URL from that release for use if the selected asset is missing.
*/
async function getLatestNightlyBundle(
action: ActionState<["Logger", "ReadOnlyEnv", "FeatureFlags"]>,
action: ActionState<["Logger", "ReadOnlyEnv"]>,
rawLanguages: string[] | undefined,
otherLanguagePacksReason: string | undefined,
variant: util.GitHubVariant,
@@ -1059,7 +1056,7 @@ async function getLatestNightlyBundle(
: "gzip";
const platform = getBundlePlatform();
const language = await getPerLanguageBundleLanguage(action, {
const language = getPerLanguageBundleLanguage(action, {
rawLanguages,
otherLanguagePacksReason,
cliVersion: undefined,