Accept GitHub release URLs in the tools input

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Henry Mercer
2026-09-25 17:44:56 +01:00
parent 4cea925541
commit 0e574bc821
7 changed files with 509 additions and 46 deletions

View File

@@ -4,7 +4,7 @@ See the [releases page](https://github.com/github/codeql-action/releases) for th
## [UNRELEASED]
No user facing changes.
- The `tools` input to the `init` and `setup-codeql` actions now accepts GitHub release URLs. The Action selects a compatible bundle from the specified release, including a per-language bundle when eligible.
## 4.38.2 - 24 Sept 2026

View File

@@ -10,6 +10,8 @@ inputs:
- A local path to a CodeQL Bundle tarball, or
- The URL of a CodeQL Bundle tarball GitHub release asset, or
- A release URL from GitHub.com or the current GitHub instance, such as
https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.27.0.
- A special value `linked` which uses the version of the CodeQL tools
that the Action has been bundled with.
- A special value `nightly` which uses the latest nightly version of the

156
lib/entry-points.js generated
View File

@@ -151352,6 +151352,74 @@ function logMissingPerLanguageBundle({ logger }, language, location) {
}
// src/codeql-release.ts
function parseCodeQLReleaseUrl(input, apiDetails) {
let url2;
try {
url2 = new URL(input);
} catch {
return void 0;
}
if (url2.protocol !== "https:" || url2.username || url2.password) {
return void 0;
}
const isCurrentInstance = url2.origin === new URL(apiDetails.url).origin;
if (!isCurrentInstance && url2.origin !== new URL(GITHUB_DOTCOM_URL).origin) {
return void 0;
}
const match2 = url2.pathname.replace(/\/$/, "").match(
/^\/([\w.-]+)\/([\w.-]+)\/releases\/(?:tag\/(.+)|(codeql-bundle-[^/]+))$/
);
if (match2 === null) {
return void 0;
}
let tagName;
try {
tagName = decodeURIComponent(match2[3] ?? match2[4]);
} catch {
throw new ConfigurationError(
"Invalid URL encoding in the CodeQL release tag."
);
}
return {
serverURL: url2.origin,
isCurrentInstance,
owner: match2[1],
repo: match2[2],
tagName
};
}
function parseCliVersion(value) {
const parsed = semver7.parse(value);
if (parsed === null) {
return void 0;
}
return parsed.version + (parsed.build.length ? `+${parsed.build.join(".")}` : "");
}
function getReleaseCliVersion(tagName, assetNames, logger) {
const versions = /* @__PURE__ */ new Set();
for (const name of assetNames) {
const match2 = name.match(/^cli-version-(.+)\.txt$/);
if (match2 === null) {
continue;
}
const version = parseCliVersion(match2[1]);
if (version !== void 0) {
versions.add(version);
} else {
logger.debug(`Ignoring invalid CLI version marker ${name}.`);
}
}
if (versions.size > 1) {
logger.warning(
`Release ${tagName} has conflicting CLI version markers. Using a combined CodeQL bundle.`
);
return void 0;
}
if (versions.size === 1) {
return versions.values().next().value;
}
return parseCliVersion(tagName.replace(/^codeql-bundle-/, ""));
}
function encodeTag(tagName) {
return tagName.split("/").map(encodeURIComponent).join("/");
}
@@ -151379,6 +151447,21 @@ function getPublicRelease(reference) {
getAssetURL: (name) => `${serverURL}/${owner}/${repo}/releases/download/${encodeTag(tagName)}/${name}`
};
}
async function getRequestedRelease(action, requested) {
if (!requested.isCurrentInstance) {
return getPublicRelease(requested);
}
try {
return await getRelease(action, requested);
} catch (e) {
if (asHTTPError(e)?.status === 404) {
throw new ConfigurationError(
`Could not find the CodeQL release ${getReleasePageURL(requested)}. Check that it exists and that the token has access to it.`
);
}
throw e;
}
}
function getCompressionMethods({
cliVersion: cliVersion2,
isLatestNightly,
@@ -152348,11 +152431,14 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, useO
toolsVersion: "local"
};
}
const requestedRelease = toolsInput === void 0 ? void 0 : parseCodeQLReleaseUrl(toolsInput, apiDetails);
let cliVersion2;
let tagName;
let url2;
let bundle;
const canForceNightlyWithFF = isDynamicWorkflow() || isInTestMode();
let release2;
let customReleaseURL;
const canForceNightlyWithFF = requestedRelease === void 0 && (isDynamicWorkflow() || isInTestMode());
const forceNightlyValueFF = await features.getValue("force_nightly" /* ForceNightly */);
const forceNightly = forceNightlyValueFF && canForceNightlyWithFF;
const nightlyRequestedByToolsInput = toolsInput !== void 0 && CODEQL_NIGHTLY_TOOLS_INPUTS.includes(toolsInput);
@@ -152434,6 +152520,18 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, useO
cliVersion2 = version.cliVersion;
tagName = version.tagName;
}
} else if (requestedRelease !== void 0) {
release2 = await getRequestedRelease(
{ apiClient: getApiClient() },
requestedRelease
);
tagName = requestedRelease.tagName;
cliVersion2 = getReleaseCliVersion(
tagName,
release2.assetNames ?? [],
logger
);
customReleaseURL = release2.url;
} else if (toolsInput !== void 0) {
tagName = tryGetTagNameFromUrl(toolsInput, logger);
url2 = toolsInput;
@@ -152454,18 +152552,21 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, useO
cliVersion2 = version.cliVersion;
tagName = version.tagName;
}
const bundleVersion2 = tagName !== void 0 ? tryGetBundleVersionFromTagName(tagName, logger) : void 0;
const bundleVersion2 = (
// Custom releases aren't cached, and their tags needn't contain a bundle version.
tagName !== void 0 && customReleaseURL === void 0 ? tryGetBundleVersionFromTagName(tagName, logger) : void 0
);
const resolvedVersion = cliVersion2 ?? (bundleVersion2 !== void 0 ? convertToSemVer(bundleVersion2, logger) : void 0);
const humanReadableVersion = resolvedVersion ?? tagName ?? url2 ?? "unknown";
logger.debug(
`Attempting to obtain CodeQL tools. CLI version: ${cliVersion2 ?? "unknown"}, bundle tag name: ${tagName ?? "unknown"}, URL: ${url2 ?? "unspecified"}.`
);
const codeqlFolder = await findCodeQLInToolcache(
const codeqlFolder = customReleaseURL === void 0 ? await findCodeQLInToolcache(
cliVersion2,
tagName,
humanReadableVersion,
logger
);
) : void 0;
if (codeqlFolder) {
if (cliVersion2) {
logger.info(
@@ -152492,23 +152593,34 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, useO
let compressionMethod;
let perLanguageBundleFallback;
if (!url2) {
if (tagName === void 0) {
throw new Error(
"Could not determine a release tag for the requested CodeQL bundle."
const selectionOptions = {
rawLanguages,
cliVersion: cliVersion2,
platform: getBundlePlatform(),
variant,
tarSupportsZstd
};
let selection;
if (release2 !== void 0) {
selection = await selectBundle(
{ env: getEnv(), features, logger },
release2,
selectionOptions
);
} else {
if (tagName === void 0) {
throw new Error(
"Could not determine a release tag for the requested CodeQL bundle."
);
}
selection = await selectDefaultBundle(
{ env: getEnv(), features, logger },
tagName,
apiDetails,
selectionOptions
);
}
({ bundle, compressionMethod, perLanguageBundleFallback } = await selectDefaultBundle(
{ env: getEnv(), features, logger },
tagName,
apiDetails,
{
rawLanguages,
cliVersion: cliVersion2,
platform: getBundlePlatform(),
variant,
tarSupportsZstd
}
));
({ bundle, compressionMethod, perLanguageBundleFallback } = selection);
url2 = bundle.url;
} else {
const method = inferCompressionMethod(url2);
@@ -152535,6 +152647,7 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, useO
bundleVersion: bundleVersion2,
cliVersion: cliVersion2,
compressionMethod,
...customReleaseURL !== void 0 ? { customReleaseURL } : {},
...perLanguageBundleFallback ? { perLanguageBundleFallback } : {},
sourceType: "download",
toolsVersion: resolvedVersion ?? "unknown"
@@ -152658,6 +152771,11 @@ var downloadCodeQL = async function(source, apiDetails, tarVersion, tempDir, log
};
};
function getToolcacheDestination({ logger }, source) {
if (source.customReleaseURL !== void 0) {
return new Failure(
`Not caching the CodeQL tools from ${source.customReleaseURL}, since we don't cache releases requested by URL.`
);
}
if (source.bundle.kind !== "combined") {
return new Failure(
"Not caching the CodeQL tools because they came from a bundle that contains only a single language."

View File

@@ -10,6 +10,8 @@ inputs:
- A local path to a CodeQL Bundle tarball, or
- The URL of a CodeQL Bundle tarball GitHub release asset, or
- A release URL from GitHub.com or the current GitHub instance, such as
https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.27.0.
- A special value `linked` which uses the version of the CodeQL tools
that the Action has been bundled with.
- A special value `nightly` which uses the latest nightly version of the

View File

@@ -29,6 +29,11 @@ export interface CodeQLDownloadSource {
toolsVersion: string;
/** The release lacks the eligible per-language bundle, so we selected the combined bundle. */
perLanguageBundleFallback?: true;
/**
* The page of a requested release that may contain a different build than the bundle we cache for
* its version, so we don't cache it.
*/
customReleaseURL?: string;
}
/** Returns the exact bundle asset name for a platform and optional language. */

View File

@@ -38,6 +38,7 @@ import {
} from "./testing-utils";
import * as toolsDownload from "./tools-download";
import {
ConfigurationError,
getErrorMessage,
GitHubVariant,
HTTPError,
@@ -1382,6 +1383,285 @@ for (const scenario of ["per-language", "fallback", "missing"] as const) {
);
}
const GHES_API_DETAILS = {
auth: "enterprise-token",
url: "https://github.example.test",
apiURL: "https://github.example.test/api/v3",
};
/** Models a hosted Linux runner with zstd and a release in `repository` on the instance `apiDetails` describes. */
function stubRequestedRelease({
apiDetails = SAMPLE_DOTCOM_API_DETAILS,
repository = "octo/tools",
tagName = `codeql-bundle-v${MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION}`,
assetNames = [
"codeql-bundle-linux64.tar.zst",
"codeql-bundle-java-linux64.tar.zst",
],
markers = [] as string[],
status = 200,
} = {}) {
sinon.stub(process, "platform").value("linux");
sinon.stub(process, "arch").value("x64");
sinon.stub(actionsUtil, "isRunningLocalAction").returns(false);
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = "github-hosted";
sinon.stub(tar, "isZstdAvailable").resolves({
available: true,
foundZstdBinary: true,
});
const apiBase = `${apiDetails.apiURL}/repos/${repository}/releases`;
const assets = [
...assetNames,
...markers.map((version) => `cli-version-${version}.txt`),
].map((name, index) => ({ name, url: `${apiBase}/assets/${1000 + index}` }));
const requests: string[] = [];
const client = github.getOctokit("123", {
baseUrl: apiDetails.apiURL,
request: {
fetch: async (input) => {
const url = String(input);
requests.push(url);
if (url !== `${apiBase}/tags/${tagName}`) {
throw new Error(`Unexpected API request: ${url}`);
}
return new Response(JSON.stringify({ tag_name: tagName, assets }), {
status,
headers: { "content-type": "application/json" },
});
},
},
});
sinon.stub(api, "getApiClient").value(() => client);
return {
assets,
requests,
releaseURL: `${apiDetails.url}/${repository}/releases/tag/${tagName}`,
};
}
for (const scenario of ["combined", "per-language", "fallback"] as const) {
test.serial(
`setupCodeQLBundle downloads a ${scenario} bundle from a custom release without using the toolcache`,
async (t) => {
const fixture = stubRequestedRelease();
const extract = stubDownloadAndExtract();
if (scenario === "fallback") {
extract.onFirstCall().rejects(new HTTPError("Not Found", 404));
}
const find = sinon.spy(toolcache, "find");
sinon.stub(actionsUtil, "isDynamicWorkflow").returns(true);
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
createToolcacheEntry(
tmpDir,
"CodeQL",
MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION,
);
const result = await setupCodeql.setupCodeQLBundle(
fixture.releaseURL,
SAMPLE_DOTCOM_API_DETAILS,
tmpDir,
GitHubVariant.DOTCOM,
PER_LANGUAGE_CLI_VERSION,
scenario === "combined" ? undefined : ["java"],
false, // useOverlayAwareDefaultCliVersion
// The requested release takes precedence over forcing the nightly.
createFeatures([Feature.PerLanguageBundles, Feature.ForceNightly]),
getRunnerLogger(true),
);
t.is(fixture.requests.length, 1);
t.true(find.notCalled);
t.is(result.toolsSource, setupCodeql.ToolsSource.Download);
t.is(result.toolsVersion, MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION);
t.is(extract.callCount, scenario === "fallback" ? 2 : 1);
t.is(
extract.lastCall.args[0],
fixture.assets[scenario === "per-language" ? 1 : 0].url,
);
t.is(extract.lastCall.args[3], "token token");
t.is(path.dirname(result.codeqlFolder), tmpDir);
t.false(fs.existsSync(`${result.codeqlFolder}.complete`));
});
},
);
}
test.serial(
"setupCodeQLBundle downloads the gzip bundle from a requested release that only has gzip bundles",
async (t) => {
const fixture = stubRequestedRelease({
assetNames: ["codeql-bundle-linux64.tar.gz"],
});
const extract = stubDownloadAndExtract();
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
const result = await setupCodeql.setupCodeQLBundle(
fixture.releaseURL,
SAMPLE_DOTCOM_API_DETAILS,
tmpDir,
GitHubVariant.DOTCOM,
PER_LANGUAGE_CLI_VERSION,
["java"],
false, // useOverlayAwareDefaultCliVersion
createFeatures([Feature.PerLanguageBundles]),
getRunnerLogger(true),
);
t.true(extract.calledOnce);
t.is(extract.firstCall.args[0], fixture.assets[0].url);
t.is(extract.firstCall.args[1], "gzip");
t.is(result.toolsSource, setupCodeql.ToolsSource.Download);
t.is(result.toolsDownloadStatusReport?.perLanguage, undefined);
});
},
);
for (const { repository, tagName, markers } of [
{
repository: "octo/tools",
tagName: "codeql-bundle-feature_branch",
markers: [],
},
]) {
test.serial(
`setupCodeQLBundle doesn't share the toolcache with ${tagName} in ${repository}`,
async (t) => {
const fixture = stubRequestedRelease({ repository, tagName, markers });
const extract = stubDownloadAndExtract();
const find = sinon.spy(toolcache, "find");
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
createToolcacheEntry(
tmpDir,
"CodeQL",
MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION,
);
const result = await setupCodeql.setupCodeQLBundle(
fixture.releaseURL,
SAMPLE_DOTCOM_API_DETAILS,
tmpDir,
GitHubVariant.DOTCOM,
PER_LANGUAGE_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
createFeatures([]),
getRunnerLogger(true),
);
t.true(find.notCalled);
t.is(result.toolsSource, setupCodeql.ToolsSource.Download);
t.is(extract.firstCall.args[0], fixture.assets[0].url);
t.is(path.dirname(result.codeqlFolder), tmpDir);
t.false(fs.existsSync(`${result.codeqlFolder}.complete`));
});
},
);
}
for (const repository of ["github/codeql-action", "octo/tools"]) {
test.serial(
`setupCodeQLBundle downloads a GitHub.com release in ${repository} from GHES by URL without credentials`,
async (t) => {
const fixture = stubRequestedRelease({ repository });
const extract = stubDownloadAndExtract();
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
const result = await setupCodeql.setupCodeQLBundle(
fixture.releaseURL,
GHES_API_DETAILS,
tmpDir,
GitHubVariant.GHES,
PER_LANGUAGE_CLI_VERSION,
["java"],
false, // useOverlayAwareDefaultCliVersion
createFeatures([Feature.PerLanguageBundles]),
getRunnerLogger(true),
);
t.deepEqual(fixture.requests, []);
t.true(extract.calledOnce);
t.is(
extract.firstCall.args[0],
`https://github.com/${repository}/releases/download/codeql-bundle-v${MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION}/codeql-bundle-linux64.tar.zst`,
);
t.is(extract.firstCall.args[3], undefined);
t.false(fs.existsSync(`${result.codeqlFolder}.complete`));
});
},
);
}
test.serial(
"setupCodeQLBundle doesn't substitute another release for a requested release that can't be found",
async (t) => {
const fixture = stubRequestedRelease({ status: 404 });
const extract = stubDownloadAndExtract();
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
await t.throwsAsync(
setupCodeql.setupCodeQLBundle(
fixture.releaseURL,
SAMPLE_DOTCOM_API_DETAILS,
tmpDir,
GitHubVariant.DOTCOM,
PER_LANGUAGE_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
createFeatures([]),
getRunnerLogger(true),
),
{
instanceOf: ConfigurationError,
message: `Could not find the CodeQL release ${fixture.releaseURL}. Check that it exists and that the token has access to it.`,
},
);
t.is(fixture.requests.length, 1);
t.true(extract.notCalled);
});
},
);
test.serial(
"setupCodeQLBundle uses the CLI version marker of a requested release",
async (t) => {
const fixture = stubRequestedRelease({
tagName: "run-123",
markers: [MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION],
});
const extract = stubDownloadAndExtract();
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
const result = await setupCodeql.setupCodeQLBundle(
fixture.releaseURL,
SAMPLE_DOTCOM_API_DETAILS,
tmpDir,
GitHubVariant.DOTCOM,
PER_LANGUAGE_CLI_VERSION,
["java"],
false, // useOverlayAwareDefaultCliVersion
createFeatures([Feature.PerLanguageBundles]),
getRunnerLogger(true),
);
// The tag doesn't give a version, so the job can only use the per-language bundle because of
// the marker.
t.is(extract.firstCall.args[0], fixture.assets[1].url);
t.is(result.toolsVersion, MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION);
t.is(
result.toolsDownloadStatusReport?.perLanguage?.tools_bundle_language,
BuiltInLanguage.java,
);
});
},
);
for (const bundle of ["per-language", "combined", "fallback"] as const) {
test.serial(
`setupCodeQLBundle preserves the nightly version for a ${bundle} download`,

View File

@@ -25,8 +25,12 @@ import {
import {
BundleSelection,
BundleSelectionOptions,
CodeQLRelease,
getPublicRelease,
getRelease,
getReleaseCliVersion,
getRequestedRelease,
parseCodeQLReleaseUrl,
selectBundle,
} from "./codeql-release";
import * as defaults from "./defaults.json";
@@ -392,6 +396,10 @@ async function resolveDefaultCliVersion(
* We handle the `tools` input in this order:
*
* - A local path is extracted without using the toolcache.
* - A release URL selects a bundle from that release, and takes precedence over the `force_nightly`
* feature flag. We don't use the toolcache, since a release may contain a different build than
* the cached bundle for its version. Bundle URLs keep using the toolcache for the version in
* their tag, for compatibility.
* - `nightly` or `nightly-latest`, or the `force_nightly` feature flag in a dynamic workflow,
* selects a bundle from the latest nightly release. We then continue with that bundle's URL.
* - `linked`, or its old name `latest`, selects the version shipped with the Action.
@@ -400,8 +408,9 @@ async function resolveDefaultCliVersion(
* - Any other value is the URL of a bundle.
* - Without a `tools` input, we use the default version.
*
* Apart from a local path, we look for the resolved version in the toolcache before downloading. A
* cached version takes precedence even if the job could use a per-language bundle.
* For other inputs apart from a local path, we look for the resolved version in the toolcache
* before downloading. A cached version takes precedence even if the job could use a per-language
* bundle.
*
* @param toolsInput The argument provided for the `tools` input, if any.
* @param defaultCliVersion The default CLI version that's linked to the CodeQL Action.
@@ -450,6 +459,11 @@ export async function getCodeQLSource(
};
}
const requestedRelease =
toolsInput === undefined
? undefined
: parseCodeQLReleaseUrl(toolsInput, apiDetails);
/** Requested CLI version number, for example 2.12.6. */
let cliVersion: string | undefined;
/** Tag name of the CodeQL bundle, for example `codeql-bundle-20230120`. */
@@ -461,10 +475,17 @@ export async function getCodeQLSource(
*/
let url: string | undefined;
let bundle: CodeQLBundle | undefined;
/** The release requested by URL, which we select the bundle from. */
let release: CodeQLRelease | undefined;
/** The page of a requested release whose bundles we don't cache. */
let customReleaseURL: string | undefined;
// We allow forcing the nightly CLI via the FF for `dynamic` events (or in test mode) where the
// `tools` input cannot be adjusted to explicitly request it.
const canForceNightlyWithFF = isDynamicWorkflow() || util.isInTestMode();
// `tools` input cannot be adjusted to explicitly request it. An explicitly requested release
// takes precedence.
const canForceNightlyWithFF =
requestedRelease === undefined &&
(isDynamicWorkflow() || util.isInTestMode());
const forceNightlyValueFF = await features.getValue(Feature.ForceNightly);
const forceNightly = forceNightlyValueFF && canForceNightlyWithFF;
@@ -585,6 +606,18 @@ export async function getCodeQLSource(
cliVersion = version.cliVersion;
tagName = version.tagName;
}
} else if (requestedRelease !== undefined) {
release = await getRequestedRelease(
{ apiClient: api.getApiClient() },
requestedRelease,
);
tagName = requestedRelease.tagName;
cliVersion = getReleaseCliVersion(
tagName,
release.assetNames ?? [],
logger,
);
customReleaseURL = release.url;
} else if (toolsInput !== undefined) {
// Any other value is a bundle URL, including one we selected from the latest nightly above.
// We use the version in its tag, if any, for the toolcache, so we assume that bundles with the
@@ -612,7 +645,8 @@ export async function getCodeQLSource(
}
const bundleVersion =
tagName !== undefined
// Custom releases aren't cached, and their tags needn't contain a bundle version.
tagName !== undefined && customReleaseURL === undefined
? tryGetBundleVersionFromTagName(tagName, logger)
: undefined;
const resolvedVersion =
@@ -629,12 +663,16 @@ export async function getCodeQLSource(
`URL: ${url ?? "unspecified"}.`,
);
const codeqlFolder = await findCodeQLInToolcache(
cliVersion,
tagName,
humanReadableVersion,
logger,
);
// A custom release may contain a different build than the bundle with the same version.
const codeqlFolder =
customReleaseURL === undefined
? await findCodeQLInToolcache(
cliVersion,
tagName,
humanReadableVersion,
logger,
)
: undefined;
if (codeqlFolder) {
if (cliVersion) {
logger.info(
@@ -671,24 +709,34 @@ export async function getCodeQLSource(
let perLanguageBundleFallback: true | undefined;
if (!url) {
if (tagName === undefined) {
throw new Error(
"Could not determine a release tag for the requested CodeQL bundle.",
const selectionOptions: BundleSelectionOptions = {
rawLanguages,
cliVersion,
platform: getBundlePlatform(),
variant,
tarSupportsZstd,
};
let selection: BundleSelection;
if (release !== undefined) {
selection = await selectBundle(
{ env: getEnv(), features, logger },
release,
selectionOptions,
);
}
({ bundle, compressionMethod, perLanguageBundleFallback } =
await selectDefaultBundle(
} else {
if (tagName === undefined) {
throw new Error(
"Could not determine a release tag for the requested CodeQL bundle.",
);
}
selection = await selectDefaultBundle(
{ env: getEnv(), features, logger },
tagName,
apiDetails,
{
rawLanguages,
cliVersion,
platform: getBundlePlatform(),
variant,
tarSupportsZstd,
},
));
selectionOptions,
);
}
({ bundle, compressionMethod, perLanguageBundleFallback } = selection);
url = bundle.url;
} else {
const method = tar.inferCompressionMethod(url);
@@ -720,6 +768,7 @@ export async function getCodeQLSource(
bundleVersion,
cliVersion,
compressionMethod,
...(customReleaseURL !== undefined ? { customReleaseURL } : {}),
...(perLanguageBundleFallback ? { perLanguageBundleFallback } : {}),
sourceType: "download",
toolsVersion: resolvedVersion ?? "unknown",
@@ -905,12 +954,19 @@ export const downloadCodeQL = async function (
* Returns the canonical toolcache directory, or the reason the bundle cannot be cached.
*
* The toolcache is keyed by version, so we don't cache bundles that would give a later request for
* the same version the wrong tools, such as per-language bundles, which lack the other languages.
* the same version the wrong tools: per-language bundles, which lack the other languages, and
* custom releases, which may be a different build.
*/
function getToolcacheDestination(
{ logger }: ActionState<["Logger"]>,
source: CodeQLDownloadSource,
): util.Result<string, string> {
if (source.customReleaseURL !== undefined) {
return new util.Failure(
`Not caching the CodeQL tools from ${source.customReleaseURL}, since we don't cache ` +
"releases requested by URL.",
);
}
if (source.bundle.kind !== "combined") {
return new util.Failure(
"Not caching the CodeQL tools because they came from a bundle that contains only a " +