Fix assumption that download URLs contain file extension

This is not the case when downloading the bundle from a GitHub Release synced to GHES with the CodeQL Action sync tool.
This commit is contained in:
Henry Mercer 2025-01-22 12:25:58 +00:00
parent f89b8a7d52
commit d23f49f56f
9 changed files with 139 additions and 47 deletions

56
lib/setup-codeql.js generated
View file

@ -70,11 +70,18 @@ var ToolsSource;
})(ToolsSource || (exports.ToolsSource = ToolsSource = {}));
exports.CODEQL_DEFAULT_ACTION_REPOSITORY = "github/codeql-action";
const CODEQL_BUNDLE_VERSION_ALIAS = ["linked", "latest"];
function getCodeQLBundleExtension(useZstd) {
return useZstd ? ".tar.zst" : ".tar.gz";
function getCodeQLBundleExtension(compressionMethod) {
switch (compressionMethod) {
case "gzip":
return ".tar.gz";
case "zstd":
return ".tar.zst";
default:
util.assertNever(compressionMethod);
}
}
function getCodeQLBundleName(useZstd) {
const extension = getCodeQLBundleExtension(useZstd);
function getCodeQLBundleName(compressionMethod) {
const extension = getCodeQLBundleExtension(compressionMethod);
let platform;
if (process.platform === "win32") {
platform = "win64";
@ -100,7 +107,7 @@ function getCodeQLActionRepository(logger) {
}
return util.getRequiredEnvParam("GITHUB_ACTION_REPOSITORY");
}
async function getCodeQLBundleDownloadURL(tagName, apiDetails, useZstd, logger) {
async function getCodeQLBundleDownloadURL(tagName, apiDetails, compressionMethod, logger) {
const codeQLActionRepository = getCodeQLActionRepository(logger);
const potentialDownloadSources = [
// This GitHub instance, and this Action.
@ -115,7 +122,7 @@ async function getCodeQLBundleDownloadURL(tagName, apiDetails, useZstd, logger)
const uniqueDownloadSources = potentialDownloadSources.filter((source, index, self) => {
return !self.slice(0, index).some((other) => (0, fast_deep_equal_1.default)(source, other));
});
const codeQLBundleName = getCodeQLBundleName(useZstd);
const codeQLBundleName = getCodeQLBundleName(compressionMethod);
for (const downloadSource 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.
@ -132,13 +139,13 @@ async function getCodeQLBundleDownloadURL(tagName, apiDetails, useZstd, logger)
});
for (const asset of release.data.assets) {
if (asset.name === codeQLBundleName) {
logger.info(`Found CodeQL bundle in ${downloadSource[1]} on ${downloadSource[0]} with URL ${asset.url}.`);
logger.info(`Found CodeQL bundle ${codeQLBundleName} in ${repository} on ${apiURL} with URL ${asset.url}.`);
return asset.url;
}
}
}
catch (e) {
logger.info(`Looked for CodeQL bundle in ${downloadSource[1]} on ${downloadSource[0]} but got error ${e}.`);
logger.info(`Looked for CodeQL bundle ${codeQLBundleName} in ${repository} on ${apiURL} but got error ${e}.`);
}
}
return `https://github.com/${exports.CODEQL_DEFAULT_ACTION_REPOSITORY}/releases/download/${tagName}/${codeQLBundleName}`;
@ -221,8 +228,14 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian
!CODEQL_BUNDLE_VERSION_ALIAS.includes(toolsInput) &&
!toolsInput.startsWith("http")) {
logger.info(`Using CodeQL CLI from local path ${toolsInput}`);
const compressionMethod = tar.inferCompressionMethod(toolsInput);
if (compressionMethod === undefined) {
throw new util.ConfigurationError(`Could not infer compression method from path ${toolsInput}. Please specify a path ` +
"ending in '.tar.gz' or '.tar.zst'.");
}
return {
codeqlTarPath: toolsInput,
compressionMethod,
sourceType: "local",
toolsVersion: "local",
};
@ -356,9 +369,22 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian
return result;
}
}
let compressionMethod;
if (!url) {
url = await getCodeQLBundleDownloadURL(tagName, apiDetails, cliVersion !== undefined &&
(await useZstdBundle(cliVersion, tarSupportsZstd)), logger);
compressionMethod =
cliVersion !== undefined &&
(await useZstdBundle(cliVersion, tarSupportsZstd))
? "zstd"
: "gzip";
url = await getCodeQLBundleDownloadURL(tagName, apiDetails, compressionMethod, logger);
}
else {
const method = tar.inferCompressionMethod(url);
if (method === undefined) {
throw new util.ConfigurationError(`Could not infer compression method from URL ${url}. Please specify a URL ` +
"ending in '.tar.gz' or '.tar.zst'.");
}
compressionMethod = method;
}
if (cliVersion) {
logger.info(`Using CodeQL CLI version ${cliVersion} sourced from ${url} .`);
@ -370,6 +396,7 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian
bundleVersion: tagName && tryGetBundleVersionFromTagName(tagName, logger),
cliVersion,
codeqlURL: url,
compressionMethod,
sourceType: "download",
toolsVersion: cliVersion ?? humanReadableVersion,
};
@ -390,7 +417,7 @@ async function tryGetFallbackToolcacheVersion(cliVersion, tagName, logger) {
}
// Exported using `export const` for testing purposes. Specifically, we want to
// be able to stub this function and have other functions in this file use that stub.
const downloadCodeQL = async function (codeqlURL, maybeBundleVersion, maybeCliVersion, apiDetails, tarVersion, tempDir, features, logger) {
const downloadCodeQL = async function (codeqlURL, compressionMethod, maybeBundleVersion, maybeCliVersion, apiDetails, tarVersion, tempDir, features, logger) {
const parsedCodeQLURL = new URL(codeqlURL);
const searchParams = new URLSearchParams(parsedCodeQLURL.search);
const headers = {
@ -417,7 +444,7 @@ const downloadCodeQL = async function (codeqlURL, maybeBundleVersion, maybeCliVe
const extractedBundlePath = extractToToolcache
? toolcacheInfo.path
: getTempExtractionDir(tempDir);
let statusReport = await (0, tools_download_1.downloadAndExtract)(codeqlURL, extractedBundlePath, authorization, { "User-Agent": "CodeQL Action", ...headers }, tarVersion, logger);
let statusReport = await (0, tools_download_1.downloadAndExtract)(codeqlURL, compressionMethod, extractedBundlePath, authorization, { "User-Agent": "CodeQL Action", ...headers }, tarVersion, logger);
if (!toolcacheInfo) {
logger.debug("Could not cache CodeQL tools because we could not determine the bundle version from the " +
`URL ${codeqlURL}.`);
@ -509,8 +536,7 @@ async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, featu
let toolsSource;
switch (source.sourceType) {
case "local": {
const compressionMethod = tar.inferCompressionMethod(source.codeqlTarPath);
codeqlFolder = await tar.extract(source.codeqlTarPath, getTempExtractionDir(tempDir), compressionMethod, zstdAvailability.version, logger);
codeqlFolder = await tar.extract(source.codeqlTarPath, getTempExtractionDir(tempDir), source.compressionMethod, zstdAvailability.version, logger);
toolsSource = ToolsSource.Local;
break;
}
@ -520,7 +546,7 @@ async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, featu
toolsSource = ToolsSource.Toolcache;
break;
case "download": {
const result = await (0, exports.downloadCodeQL)(source.codeqlURL, source.bundleVersion, source.cliVersion, apiDetails, zstdAvailability.version, tempDir, features, logger);
const result = await (0, exports.downloadCodeQL)(source.codeqlURL, source.compressionMethod, source.bundleVersion, source.cliVersion, apiDetails, zstdAvailability.version, tempDir, features, logger);
toolsVersion = result.toolsVersion;
codeqlFolder = result.codeqlFolder;
toolsDownloadStatusReport = result.statusReport;