Select the default bundle from the first compatible release

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Henry Mercer
2026-09-24 15:38:23 +01:00
parent 027ab3179c
commit 7f964a9e6e
3 changed files with 158 additions and 193 deletions

138
lib/entry-points.js generated
View File

@@ -27216,8 +27216,8 @@ var require_gte = __commonJS({
"node_modules/semver/functions/gte.js"(exports2, module2) { "node_modules/semver/functions/gte.js"(exports2, module2) {
"use strict"; "use strict";
var compare3 = require_compare(); var compare3 = require_compare();
var gte9 = (a, b, loose) => compare3(a, b, loose) >= 0; var gte8 = (a, b, loose) => compare3(a, b, loose) >= 0;
module2.exports = gte9; module2.exports = gte8;
} }
}); });
@@ -27238,7 +27238,7 @@ var require_cmp = __commonJS({
var eq = require_eq(); var eq = require_eq();
var neq = require_neq(); var neq = require_neq();
var gt = require_gt(); var gt = require_gt();
var gte9 = require_gte(); var gte8 = require_gte();
var lt2 = require_lt(); var lt2 = require_lt();
var lte2 = require_lte(); var lte2 = require_lte();
var cmp = (a, op, b, loose) => { var cmp = (a, op, b, loose) => {
@@ -27268,7 +27268,7 @@ var require_cmp = __commonJS({
case ">": case ">":
return gt(a, b, loose); return gt(a, b, loose);
case ">=": case ">=":
return gte9(a, b, loose); return gte8(a, b, loose);
case "<": case "<":
return lt2(a, b, loose); return lt2(a, b, loose);
case "<=": case "<=":
@@ -28076,7 +28076,7 @@ var require_outside = __commonJS({
var gt = require_gt(); var gt = require_gt();
var lt2 = require_lt(); var lt2 = require_lt();
var lte2 = require_lte(); var lte2 = require_lte();
var gte9 = require_gte(); var gte8 = require_gte();
var outside = (version, range2, hilo, options) => { var outside = (version, range2, hilo, options) => {
version = new SemVer(version, options); version = new SemVer(version, options);
range2 = new Range2(range2, options); range2 = new Range2(range2, options);
@@ -28091,7 +28091,7 @@ var require_outside = __commonJS({
break; break;
case "<": case "<":
gtfn = lt2; gtfn = lt2;
ltefn = gte9; ltefn = gte8;
ltfn = gt; ltfn = gt;
comp = "<"; comp = "<";
ecomp = "<="; ecomp = "<=";
@@ -28406,7 +28406,7 @@ var require_semver2 = __commonJS({
var lt2 = require_lt(); var lt2 = require_lt();
var eq = require_eq(); var eq = require_eq();
var neq = require_neq(); var neq = require_neq();
var gte9 = require_gte(); var gte8 = require_gte();
var lte2 = require_lte(); var lte2 = require_lte();
var cmp = require_cmp(); var cmp = require_cmp();
var coerce3 = require_coerce(); var coerce3 = require_coerce();
@@ -28445,7 +28445,7 @@ var require_semver2 = __commonJS({
lt: lt2, lt: lt2,
eq, eq,
neq, neq,
gte: gte9, gte: gte8,
lte: lte2, lte: lte2,
cmp, cmp,
coerce: coerce3, coerce: coerce3,
@@ -31721,7 +31721,7 @@ var require_brace_expansion = __commonJS({
function lte2(i, y) { function lte2(i, y) {
return i <= y; return i <= y;
} }
function gte9(i, y) { function gte8(i, y) {
return i >= y; return i >= y;
} }
function combine2(acc, base, pre, values, max, maxLength, dropEmpties, outBase) { function combine2(acc, base, pre, values, max, maxLength, dropEmpties, outBase) {
@@ -31754,7 +31754,7 @@ var require_brace_expansion = __commonJS({
var reverse = y < x; var reverse = y < x;
if (reverse) { if (reverse) {
incr *= -1; incr *= -1;
test = gte9; test = gte8;
} }
var pad = n.some(isPadded2); var pad = n.some(isPadded2);
var length = 0; var length = 0;
@@ -33901,8 +33901,8 @@ var require_semver3 = __commonJS({
function neq(a, b, loose) { function neq(a, b, loose) {
return compare3(a, b, loose) !== 0; return compare3(a, b, loose) !== 0;
} }
exports2.gte = gte9; exports2.gte = gte8;
function gte9(a, b, loose) { function gte8(a, b, loose) {
return compare3(a, b, loose) >= 0; return compare3(a, b, loose) >= 0;
} }
exports2.lte = lte2; exports2.lte = lte2;
@@ -33933,7 +33933,7 @@ var require_semver3 = __commonJS({
case ">": case ">":
return gt(a, b, loose); return gt(a, b, loose);
case ">=": case ">=":
return gte9(a, b, loose); return gte8(a, b, loose);
case "<": case "<":
return lt2(a, b, loose); return lt2(a, b, loose);
case "<=": case "<=":
@@ -34478,7 +34478,7 @@ var require_semver3 = __commonJS({
break; break;
case "<": case "<":
gtfn = lt2; gtfn = lt2;
ltefn = gte9; ltefn = gte8;
ltfn = gt; ltfn = gt;
comp = "<"; comp = "<";
ecomp = "<="; ecomp = "<=";
@@ -89595,7 +89595,7 @@ var require_brace_expansion2 = __commonJS({
function lte2(i, y) { function lte2(i, y) {
return i <= y; return i <= y;
} }
function gte9(i, y) { function gte8(i, y) {
return i >= y; return i >= y;
} }
function combine2(acc, pre, values, max, maxLength, dropEmpties) { function combine2(acc, pre, values, max, maxLength, dropEmpties) {
@@ -89627,7 +89627,7 @@ var require_brace_expansion2 = __commonJS({
var reverse = y < x; var reverse = y < x;
if (reverse) { if (reverse) {
incr *= -1; incr *= -1;
test = gte9; test = gte8;
} }
var pad = n.some(isPadded2); var pad = n.some(isPadded2);
var length = 0; var length = 0;
@@ -151359,6 +151359,18 @@ function getReleasePageURL(reference) {
const { serverURL, owner, repo, tagName } = reference; const { serverURL, owner, repo, tagName } = reference;
return `${serverURL}/${owner}/${repo}/releases/tag/${encodeTag(tagName)}`; return `${serverURL}/${owner}/${repo}/releases/tag/${encodeTag(tagName)}`;
} }
async function getRelease({ apiClient }, reference) {
const { owner, repo, tagName } = reference;
const { data: release2 } = await apiClient.rest.repos.getReleaseByTag({
owner,
repo,
tag: tagName
});
return {
url: getReleasePageURL(reference),
getAssetURL: (name) => release2.assets.find((asset) => asset.name === name)?.url
};
}
function getPublicRelease(reference) { function getPublicRelease(reference) {
const { serverURL, owner, repo, tagName } = reference; const { serverURL, owner, repo, tagName } = reference;
return { return {
@@ -152133,7 +152145,8 @@ function getCodeQLActionRepository(logger) {
} }
return getRequiredEnvParam("GITHUB_ACTION_REPOSITORY"); return getRequiredEnvParam("GITHUB_ACTION_REPOSITORY");
} }
async function getCodeQLBundleDownloadURL(tagName, apiDetails, codeQLBundleName, logger) { async function selectDefaultBundle(action, tagName, apiDetails, options) {
const { logger } = action;
const codeQLActionRepository = getCodeQLActionRepository(logger); const codeQLActionRepository = getCodeQLActionRepository(logger);
const potentialDownloadSources = [ const potentialDownloadSources = [
// This GitHub instance, and this Action. // This GitHub instance, and this Action.
@@ -152148,33 +152161,34 @@ async function getCodeQLBundleDownloadURL(tagName, apiDetails, codeQLBundleName,
return !self2.slice(0, index2).some((other) => (0, import_fast_deep_equal.default)(source, other)); return !self2.slice(0, index2).some((other) => (0, import_fast_deep_equal.default)(source, other));
} }
); );
for (const downloadSource of uniqueDownloadSources) { for (const [serverURL, repository] of uniqueDownloadSources) {
const [apiURL, repository] = downloadSource; if (serverURL === GITHUB_DOTCOM_URL && repository === CODEQL_DEFAULT_ACTION_REPOSITORY) {
if (apiURL === GITHUB_DOTCOM_URL && repository === CODEQL_DEFAULT_ACTION_REPOSITORY) {
break; break;
} }
const [repositoryOwner, repositoryName] = repository.split("/"); const [owner2, repo2] = repository.split("/");
try { try {
const release2 = await getApiClient().rest.repos.getReleaseByTag({ const release2 = await getRelease(
owner: repositoryOwner, { apiClient: getApiClient() },
repo: repositoryName, { serverURL, owner: owner2, repo: repo2, tagName }
tag: tagName );
}); return await selectBundle(action, release2, options);
for (const asset of release2.data.assets) {
if (asset.name === codeQLBundleName) {
logger.info(
`Found CodeQL bundle ${codeQLBundleName} in ${repository} on ${apiURL} with URL ${asset.url}.`
);
return asset.url;
}
}
} catch (e) { } catch (e) {
logger.info( logger.info(
`Looked for CodeQL bundle ${codeQLBundleName} in ${repository} on ${apiURL} but got error ${e}.` `Looked for CodeQL bundles in release ${tagName} of ${repository} on ${serverURL} but got error ${e}.`
); );
} }
} }
return `https://github.com/${CODEQL_DEFAULT_ACTION_REPOSITORY}/releases/download/${tagName}/${codeQLBundleName}`; const [owner, repo] = CODEQL_DEFAULT_ACTION_REPOSITORY.split("/");
return selectBundle(
action,
getPublicRelease({
serverURL: GITHUB_DOTCOM_URL,
owner,
repo,
tagName
}),
options
);
} }
function tryGetBundleVersionFromTagName(tagName, logger) { function tryGetBundleVersionFromTagName(tagName, logger) {
const match2 = tagName.match(/^codeql-bundle-(.+)$/); const match2 = tagName.match(/^codeql-bundle-(.+)$/);
@@ -152525,47 +152539,26 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, useO
} }
} }
let compressionMethod; let compressionMethod;
let perLanguageBundleFallback;
if (!url2) { if (!url2) {
const bundleTagName = tagName; if (tagName === void 0) {
if (bundleTagName === void 0) {
throw new Error( throw new Error(
"Could not determine a release tag for the requested CodeQL bundle." "Could not determine a release tag for the requested CodeQL bundle."
); );
} }
compressionMethod = cliVersion2 !== void 0 && await useZstdBundle(cliVersion2, tarSupportsZstd) ? "zstd" : "gzip"; ({ bundle, compressionMethod, perLanguageBundleFallback } = await selectDefaultBundle(
const platform2 = getBundlePlatform();
const perLanguageBundleLanguage = await getPerLanguageBundleLanguage(
{ env: getEnv(), features, logger }, { env: getEnv(), features, logger },
tagName,
apiDetails,
{ {
rawLanguages, rawLanguages,
cliVersion: cliVersion2, cliVersion: cliVersion2,
compressionMethod, platform: getBundlePlatform(),
platform: platform2, variant,
variant tarSupportsZstd
} }
); ));
const resolveBundleURL = (language) => getCodeQLBundleDownloadURL( url2 = bundle.url;
bundleTagName,
apiDetails,
getCodeQLBundleName(compressionMethod, platform2, language),
logger
);
const combinedBundleURL = await resolveBundleURL();
if (perLanguageBundleLanguage !== void 0) {
logger.info(
`Selected the per-language CodeQL bundle for '${perLanguageBundleLanguage}'.`
);
url2 = await resolveBundleURL(perLanguageBundleLanguage);
bundle = {
kind: "per-language",
url: url2,
language: perLanguageBundleLanguage,
combinedBundleURL
};
} else {
url2 = combinedBundleURL;
bundle = { kind: "combined", url: url2 };
}
} else { } else {
const method = inferCompressionMethod(url2); const method = inferCompressionMethod(url2);
if (method === void 0) { if (method === void 0) {
@@ -152591,6 +152584,7 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, useO
bundleVersion: bundleVersion2, bundleVersion: bundleVersion2,
cliVersion: cliVersion2, cliVersion: cliVersion2,
compressionMethod, compressionMethod,
...perLanguageBundleFallback ? { perLanguageBundleFallback } : {},
sourceType: "download", sourceType: "download",
toolsVersion: resolvedVersion ?? "unknown" toolsVersion: resolvedVersion ?? "unknown"
}; };
@@ -152804,12 +152798,6 @@ async function downloadCodeQLBundle(action, source, apiDetails, tarVersion, temp
}; };
} }
} }
async function useZstdBundle(cliVersion2, tarSupportsZstd) {
return (
// In testing, gzip performs better than zstd on Windows.
process.platform !== "win32" && tarSupportsZstd && semver11.gte(cliVersion2, CODEQL_VERSION_ZSTD_BUNDLE)
);
}
function getTempExtractionDir(tempDir) { function getTempExtractionDir(tempDir) {
return path13.join(tempDir, v4_default()); return path13.join(tempDir, v4_default());
} }
@@ -157064,7 +157052,7 @@ function isPadded(el) {
function lte(i, y) { function lte(i, y) {
return i <= y; return i <= y;
} }
function gte8(i, y) { function gte7(i, y) {
return i >= y; return i >= y;
} }
function combine(acc, pre, values, max, maxLength, dropEmpties) { function combine(acc, pre, values, max, maxLength, dropEmpties) {
@@ -157099,7 +157087,7 @@ function expandSequence(body, isAlphaSequence, max, maxLength) {
const reverse = y < x; const reverse = y < x;
if (reverse) { if (reverse) {
incr *= -1; incr *= -1;
test = gte8; test = gte7;
} }
const pad = n.some(isPadded); const pad = n.some(isPadded);
let length = 0; let length = 0;

View File

@@ -1272,9 +1272,14 @@ for (const status of [200, 404]) {
); );
} }
for (const fallback of [false, true]) { for (const scenario of ["per-language", "fallback", "missing"] as const) {
const suffix = {
"per-language": "",
fallback: " with fallback",
missing: " when the release lacks the per-language bundle",
}[scenario];
test.serial( test.serial(
`setupCodeQLBundle retains the selected release identity for an opaque asset URL${fallback ? " with fallback" : ""}`, `setupCodeQLBundle retains the selected release identity for an opaque asset URL${suffix}`,
async (t) => { async (t) => {
sinon.stub(process, "platform").value("linux"); sinon.stub(process, "platform").value("linux");
sinon.stub(process, "arch").value("x64"); sinon.stub(process, "arch").value("x64");
@@ -1288,22 +1293,22 @@ for (const fallback of [false, true]) {
const assetURL = const assetURL =
"https://api.github.com/repos/codeql-testing/action-fork/releases/assets/123"; "https://api.github.com/repos/codeql-testing/action-fork/releases/assets/123";
const combinedURL = `${assetURL}4`; const combinedURL = `${assetURL}4`;
const assets = [
{ name: "codeql-bundle-linux64.tar.zst", url: combinedURL },
];
if (scenario !== "missing") {
assets.push({
name: "codeql-bundle-java-linux64.tar.zst",
url: assetURL,
});
}
const fetchRelease = sinon const fetchRelease = sinon
.stub<Parameters<typeof fetch>, ReturnType<typeof fetch>>() .stub<Parameters<typeof fetch>, ReturnType<typeof fetch>>()
.callsFake( .callsFake(
async () => async () =>
new Response( new Response(JSON.stringify({ assets }), {
JSON.stringify({ headers: { "content-type": "application/json" },
assets: [ }),
{ name: "codeql-bundle-java-linux64.tar.zst", url: assetURL },
{
name: "codeql-bundle-linux64.tar.zst",
url: combinedURL,
},
],
}),
{ headers: { "content-type": "application/json" } },
),
); );
const client = github.getOctokit("123", { const client = github.getOctokit("123", {
request: { fetch: fetchRelease }, request: { fetch: fetchRelease },
@@ -1311,7 +1316,7 @@ for (const fallback of [false, true]) {
sinon.stub(api, "getApiClient").value(() => client); sinon.stub(api, "getApiClient").value(() => client);
const authorizationSpy = sinon.spy(api, "getAuthorizationHeaderFor"); const authorizationSpy = sinon.spy(api, "getAuthorizationHeaderFor");
const extractStub = stubDownloadAndExtract(); const extractStub = stubDownloadAndExtract();
if (fallback) { if (scenario === "fallback") {
extractStub.onFirstCall().rejects(new HTTPError("Not Found", 404)); extractStub.onFirstCall().rejects(new HTTPError("Not Found", 404));
} }
@@ -1331,41 +1336,46 @@ for (const fallback of [false, true]) {
getRunnerLogger(true), getRunnerLogger(true),
); );
t.true(fetchRelease.calledTwice); const usesPerLanguageBundle = scenario === "per-language";
t.true(fetchRelease.calledOnce);
t.is( t.is(
fetchRelease.firstCall.args[0], fetchRelease.firstCall.args[0],
`https://api.github.com/repos/codeql-testing/action-fork/releases/tags/${tag}`, `https://api.github.com/repos/codeql-testing/action-fork/releases/tags/${tag}`,
); );
t.is(extractStub.callCount, fallback ? 2 : 1); t.is(extractStub.callCount, scenario === "fallback" ? 2 : 1);
t.is(extractStub.firstCall.args[0], assetURL);
t.is(extractStub.lastCall.args[0], fallback ? combinedURL : assetURL);
t.is(authorizationSpy.callCount, extractStub.callCount);
t.is(authorizationSpy.firstCall.args[2], assetURL);
t.is( t.is(
authorizationSpy.lastCall.args[2], extractStub.firstCall.args[0],
fallback ? combinedURL : assetURL, scenario === "missing" ? combinedURL : assetURL,
);
t.is(
extractStub.lastCall.args[0],
usesPerLanguageBundle ? assetURL : combinedURL,
);
t.deepEqual(
authorizationSpy.getCalls().map((call) => call.args[2]),
extractStub.getCalls().map((call) => call.args[0]),
); );
t.is(extractStub.lastCall.args[3], "token token"); t.is(extractStub.lastCall.args[3], "token token");
t.is(result.toolsVersion, MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION); t.is(result.toolsVersion, MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION);
t.is( t.is(
result.toolsDownloadStatusReport?.perLanguage?.tools_bundle_language, result.toolsDownloadStatusReport?.perLanguage?.tools_bundle_language,
fallback ? undefined : BuiltInLanguage.java, usesPerLanguageBundle ? BuiltInLanguage.java : undefined,
); );
t.is( t.is(
result.toolsDownloadStatusReport?.perLanguage result.toolsDownloadStatusReport?.perLanguage
?.tools_per_language_bundle_fallback, ?.tools_per_language_bundle_fallback,
fallback ? true : undefined, usesPerLanguageBundle ? undefined : true,
); );
if (fallback) { if (usesPerLanguageBundle) {
t.is(path.dirname(result.codeqlFolder), tmpDir);
t.deepEqual(toolcache.findAllVersions("CodeQL"), []);
t.false(fs.existsSync(`${result.codeqlFolder}.complete`));
} else {
t.is( t.is(
result.codeqlFolder, result.codeqlFolder,
toolcache.find("CodeQL", MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION), toolcache.find("CodeQL", MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION),
); );
t.true(fs.existsSync(`${result.codeqlFolder}.complete`)); t.true(fs.existsSync(`${result.codeqlFolder}.complete`));
} else {
t.is(path.dirname(result.codeqlFolder), tmpDir);
t.deepEqual(toolcache.findAllVersions("CodeQL"), []);
t.false(fs.existsSync(`${result.codeqlFolder}.complete`));
} }
}); });
}, },

View File

@@ -21,9 +21,14 @@ import {
CodeQLBundle, CodeQLBundle,
CodeQLDownloadSource, CodeQLDownloadSource,
getCodeQLBundleFromUrl, getCodeQLBundleFromUrl,
getCodeQLBundleName,
} from "./codeql-bundle"; } from "./codeql-bundle";
import { getPublicRelease, selectBundle } from "./codeql-release"; import {
BundleSelection,
BundleSelectionOptions,
getPublicRelease,
getRelease,
selectBundle,
} from "./codeql-release";
import * as defaults from "./defaults.json"; import * as defaults from "./defaults.json";
import { import {
addNoLanguageDiagnostic, addNoLanguageDiagnostic,
@@ -32,19 +37,14 @@ import {
} from "./diagnostics"; } from "./diagnostics";
import { EnvVar, getEnv } from "./environment"; import { EnvVar, getEnv } from "./environment";
import { import {
CODEQL_VERSION_ZSTD_BUNDLE,
CodeQLDefaultVersionInfo, CodeQLDefaultVersionInfo,
CodeQLVersionInfo, CodeQLVersionInfo,
Feature, Feature,
FeatureEnablement, FeatureEnablement,
} from "./feature-flags"; } from "./feature-flags";
import { BuiltInLanguage } from "./languages";
import { Logger } from "./logging"; import { Logger } from "./logging";
import { getCodeQlVersionsForOverlayBaseDatabases } from "./overlay/caching"; import { getCodeQlVersionsForOverlayBaseDatabases } from "./overlay/caching";
import { import { logMissingPerLanguageBundle } from "./per-language-bundles";
getPerLanguageBundleLanguage,
logMissingPerLanguageBundle,
} from "./per-language-bundles";
import { getBundlePlatform } from "./platform"; import { getBundlePlatform } from "./platform";
import * as tar from "./tar"; import * as tar from "./tar";
import { import {
@@ -89,12 +89,19 @@ export function getCodeQLActionRepository(logger: Logger): string {
return util.getRequiredEnvParam("GITHUB_ACTION_REPOSITORY"); return util.getRequiredEnvParam("GITHUB_ACTION_REPOSITORY");
} }
async function getCodeQLBundleDownloadURL( /**
* Selects a bundle from the first release tagged `tagName` that has a compatible bundle, trying the
* Action repositories on this GitHub instance before the canonical Action on GitHub.com. If we
* can't look up a release, or it has no compatible bundle, we move on to the next repository. We
* assume that the public release on GitHub.com has every bundle.
*/
async function selectDefaultBundle(
action: ActionState<["Logger", "ReadOnlyEnv", "FeatureFlags"]>,
tagName: string, tagName: string,
apiDetails: api.GitHubApiDetails, apiDetails: api.GitHubApiDetails,
codeQLBundleName: string, options: BundleSelectionOptions,
logger: Logger, ): Promise<BundleSelection> {
): Promise<string> { const { logger } = action;
const codeQLActionRepository = getCodeQLActionRepository(logger); const codeQLActionRepository = getCodeQLActionRepository(logger);
const potentialDownloadSources = [ const potentialDownloadSources = [
// This GitHub instance, and this Action. // This GitHub instance, and this Action.
@@ -111,37 +118,38 @@ async function getCodeQLBundleDownloadURL(
return !self.slice(0, index).some((other) => deepEqual(source, other)); return !self.slice(0, index).some((other) => deepEqual(source, other));
}, },
); );
for (const downloadSource of uniqueDownloadSources) { for (const [serverURL, repository] of uniqueDownloadSources) {
const [apiURL, repository] = downloadSource;
// If we've reached the final case, short-circuit the API check since we know the bundle exists and is public. // If we've reached the final case, short-circuit the API check since we know the bundle exists and is public.
if ( if (
apiURL === util.GITHUB_DOTCOM_URL && serverURL === util.GITHUB_DOTCOM_URL &&
repository === CODEQL_DEFAULT_ACTION_REPOSITORY repository === CODEQL_DEFAULT_ACTION_REPOSITORY
) { ) {
break; break;
} }
const [repositoryOwner, repositoryName] = repository.split("/"); const [owner, repo] = repository.split("/");
try { try {
const release = await api.getApiClient().rest.repos.getReleaseByTag({ const release = await getRelease(
owner: repositoryOwner, { apiClient: api.getApiClient() },
repo: repositoryName, { serverURL, owner, repo, tagName },
tag: tagName, );
}); return await selectBundle(action, release, options);
for (const asset of release.data.assets) {
if (asset.name === codeQLBundleName) {
logger.info(
`Found CodeQL bundle ${codeQLBundleName} in ${repository} on ${apiURL} with URL ${asset.url}.`,
);
return asset.url;
}
}
} catch (e) { } catch (e) {
logger.info( logger.info(
`Looked for CodeQL bundle ${codeQLBundleName} in ${repository} on ${apiURL} but got error ${e}.`, `Looked for CodeQL bundles in release ${tagName} of ${repository} on ${serverURL} but got error ${e}.`,
); );
} }
} }
return `https://github.com/${CODEQL_DEFAULT_ACTION_REPOSITORY}/releases/download/${tagName}/${codeQLBundleName}`; const [owner, repo] = CODEQL_DEFAULT_ACTION_REPOSITORY.split("/");
return selectBundle(
action,
getPublicRelease({
serverURL: util.GITHUB_DOTCOM_URL,
owner,
repo,
tagName,
}),
options,
);
} }
function tryGetBundleVersionFromTagName( function tryGetBundleVersionFromTagName(
@@ -719,58 +727,28 @@ export async function getCodeQLSource(
} }
let compressionMethod: tar.CompressionMethod; let compressionMethod: tar.CompressionMethod;
let perLanguageBundleFallback: true | undefined;
if (!url) { if (!url) {
const bundleTagName = tagName; if (tagName === undefined) {
if (bundleTagName === undefined) {
throw new Error( throw new Error(
"Could not determine a release tag for the requested CodeQL bundle.", "Could not determine a release tag for the requested CodeQL bundle.",
); );
} }
({ bundle, compressionMethod, perLanguageBundleFallback } =
compressionMethod = await selectDefaultBundle(
cliVersion !== undefined && { env: getEnv(), features, logger },
(await useZstdBundle(cliVersion, tarSupportsZstd)) tagName,
? "zstd"
: "gzip";
const platform = getBundlePlatform();
const perLanguageBundleLanguage = await getPerLanguageBundleLanguage(
{ env: getEnv(), features, logger },
{
rawLanguages,
cliVersion,
compressionMethod,
platform,
variant,
},
);
// Resolves the combined or per-language bundle URL for the requested release.
const resolveBundleURL = (language?: BuiltInLanguage) =>
getCodeQLBundleDownloadURL(
bundleTagName,
apiDetails, apiDetails,
getCodeQLBundleName(compressionMethod, platform, language), {
logger, rawLanguages,
); cliVersion,
platform: getBundlePlatform(),
const combinedBundleURL = await resolveBundleURL(); variant,
if (perLanguageBundleLanguage !== undefined) { tarSupportsZstd,
logger.info( },
`Selected the per-language CodeQL bundle for '${perLanguageBundleLanguage}'.`, ));
); url = bundle.url;
url = await resolveBundleURL(perLanguageBundleLanguage);
bundle = {
kind: "per-language",
url,
language: perLanguageBundleLanguage,
combinedBundleURL,
};
} else {
url = combinedBundleURL;
bundle = { kind: "combined", url };
}
} else { } else {
const method = tar.inferCompressionMethod(url); const method = tar.inferCompressionMethod(url);
if (method === undefined) { if (method === undefined) {
@@ -801,6 +779,7 @@ export async function getCodeQLSource(
bundleVersion, bundleVersion,
cliVersion, cliVersion,
compressionMethod, compressionMethod,
...(perLanguageBundleFallback ? { perLanguageBundleFallback } : {}),
sourceType: "download", sourceType: "download",
toolsVersion: resolvedVersion ?? "unknown", toolsVersion: resolvedVersion ?? "unknown",
}; };
@@ -1161,18 +1140,6 @@ export async function downloadCodeQLBundle(
} }
} }
async function useZstdBundle(
cliVersion: string,
tarSupportsZstd: boolean,
): Promise<boolean> {
return (
// In testing, gzip performs better than zstd on Windows.
process.platform !== "win32" &&
tarSupportsZstd &&
semver.gte(cliVersion, CODEQL_VERSION_ZSTD_BUNDLE)
);
}
function getTempExtractionDir(tempDir: string) { function getTempExtractionDir(tempDir: string) {
return path.join(tempDir, uuidV4()); return path.join(tempDir, uuidV4());
} }