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 = {})); })(ToolsSource || (exports.ToolsSource = ToolsSource = {}));
exports.CODEQL_DEFAULT_ACTION_REPOSITORY = "github/codeql-action"; exports.CODEQL_DEFAULT_ACTION_REPOSITORY = "github/codeql-action";
const CODEQL_BUNDLE_VERSION_ALIAS = ["linked", "latest"]; const CODEQL_BUNDLE_VERSION_ALIAS = ["linked", "latest"];
function getCodeQLBundleExtension(useZstd) { function getCodeQLBundleExtension(compressionMethod) {
return useZstd ? ".tar.zst" : ".tar.gz"; switch (compressionMethod) {
case "gzip":
return ".tar.gz";
case "zstd":
return ".tar.zst";
default:
util.assertNever(compressionMethod);
}
} }
function getCodeQLBundleName(useZstd) { function getCodeQLBundleName(compressionMethod) {
const extension = getCodeQLBundleExtension(useZstd); const extension = getCodeQLBundleExtension(compressionMethod);
let platform; let platform;
if (process.platform === "win32") { if (process.platform === "win32") {
platform = "win64"; platform = "win64";
@ -100,7 +107,7 @@ function getCodeQLActionRepository(logger) {
} }
return util.getRequiredEnvParam("GITHUB_ACTION_REPOSITORY"); 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 codeQLActionRepository = getCodeQLActionRepository(logger);
const potentialDownloadSources = [ const potentialDownloadSources = [
// This GitHub instance, and this Action. // This GitHub instance, and this Action.
@ -115,7 +122,7 @@ async function getCodeQLBundleDownloadURL(tagName, apiDetails, useZstd, logger)
const uniqueDownloadSources = potentialDownloadSources.filter((source, index, self) => { const uniqueDownloadSources = potentialDownloadSources.filter((source, index, self) => {
return !self.slice(0, index).some((other) => (0, fast_deep_equal_1.default)(source, other)); 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) { for (const downloadSource of uniqueDownloadSources) {
const [apiURL, repository] = downloadSource; 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.
@ -132,13 +139,13 @@ async function getCodeQLBundleDownloadURL(tagName, apiDetails, useZstd, logger)
}); });
for (const asset of release.data.assets) { for (const asset of release.data.assets) {
if (asset.name === codeQLBundleName) { 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; return asset.url;
} }
} }
} }
catch (e) { 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}`; 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) && !CODEQL_BUNDLE_VERSION_ALIAS.includes(toolsInput) &&
!toolsInput.startsWith("http")) { !toolsInput.startsWith("http")) {
logger.info(`Using CodeQL CLI from local path ${toolsInput}`); 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 { return {
codeqlTarPath: toolsInput, codeqlTarPath: toolsInput,
compressionMethod,
sourceType: "local", sourceType: "local",
toolsVersion: "local", toolsVersion: "local",
}; };
@ -356,9 +369,22 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, varian
return result; return result;
} }
} }
let compressionMethod;
if (!url) { if (!url) {
url = await getCodeQLBundleDownloadURL(tagName, apiDetails, cliVersion !== undefined && compressionMethod =
(await useZstdBundle(cliVersion, tarSupportsZstd)), logger); 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) { if (cliVersion) {
logger.info(`Using CodeQL CLI version ${cliVersion} sourced from ${url} .`); 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), bundleVersion: tagName && tryGetBundleVersionFromTagName(tagName, logger),
cliVersion, cliVersion,
codeqlURL: url, codeqlURL: url,
compressionMethod,
sourceType: "download", sourceType: "download",
toolsVersion: cliVersion ?? humanReadableVersion, toolsVersion: cliVersion ?? humanReadableVersion,
}; };
@ -390,7 +417,7 @@ async function tryGetFallbackToolcacheVersion(cliVersion, tagName, logger) {
} }
// Exported using `export const` for testing purposes. Specifically, we want to // 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. // 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 parsedCodeQLURL = new URL(codeqlURL);
const searchParams = new URLSearchParams(parsedCodeQLURL.search); const searchParams = new URLSearchParams(parsedCodeQLURL.search);
const headers = { const headers = {
@ -417,7 +444,7 @@ const downloadCodeQL = async function (codeqlURL, maybeBundleVersion, maybeCliVe
const extractedBundlePath = extractToToolcache const extractedBundlePath = extractToToolcache
? toolcacheInfo.path ? toolcacheInfo.path
: getTempExtractionDir(tempDir); : 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) { if (!toolcacheInfo) {
logger.debug("Could not cache CodeQL tools because we could not determine the bundle version from the " + logger.debug("Could not cache CodeQL tools because we could not determine the bundle version from the " +
`URL ${codeqlURL}.`); `URL ${codeqlURL}.`);
@ -509,8 +536,7 @@ async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, featu
let toolsSource; let toolsSource;
switch (source.sourceType) { switch (source.sourceType) {
case "local": { case "local": {
const compressionMethod = tar.inferCompressionMethod(source.codeqlTarPath); codeqlFolder = await tar.extract(source.codeqlTarPath, getTempExtractionDir(tempDir), source.compressionMethod, zstdAvailability.version, logger);
codeqlFolder = await tar.extract(source.codeqlTarPath, getTempExtractionDir(tempDir), compressionMethod, zstdAvailability.version, logger);
toolsSource = ToolsSource.Local; toolsSource = ToolsSource.Local;
break; break;
} }
@ -520,7 +546,7 @@ async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, featu
toolsSource = ToolsSource.Toolcache; toolsSource = ToolsSource.Toolcache;
break; break;
case "download": { 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; toolsVersion = result.toolsVersion;
codeqlFolder = result.codeqlFolder; codeqlFolder = result.codeqlFolder;
toolsDownloadStatusReport = result.statusReport; toolsDownloadStatusReport = result.statusReport;

File diff suppressed because one or more lines are too long

22
lib/tar.js generated
View file

@ -43,6 +43,7 @@ const stream = __importStar(require("stream"));
const toolrunner_1 = require("@actions/exec/lib/toolrunner"); const toolrunner_1 = require("@actions/exec/lib/toolrunner");
const io = __importStar(require("@actions/io")); const io = __importStar(require("@actions/io"));
const toolcache = __importStar(require("@actions/tool-cache")); const toolcache = __importStar(require("@actions/tool-cache"));
const semver = __importStar(require("semver"));
const actions_util_1 = require("./actions-util"); const actions_util_1 = require("./actions-util");
const util_1 = require("./util"); const util_1 = require("./util");
const MIN_REQUIRED_BSD_TAR_VERSION = "3.4.3"; const MIN_REQUIRED_BSD_TAR_VERSION = "3.4.3";
@ -88,13 +89,18 @@ async function isZstdAvailable(logger) {
switch (type) { switch (type) {
case "gnu": case "gnu":
return { return {
available: foundZstdBinary && version >= MIN_REQUIRED_GNU_TAR_VERSION, available: foundZstdBinary &&
// GNU tar only uses major and minor version numbers
semver.gte(semver.coerce(version), semver.coerce(MIN_REQUIRED_GNU_TAR_VERSION)),
foundZstdBinary, foundZstdBinary,
version: tarVersion, version: tarVersion,
}; };
case "bsd": case "bsd":
return { return {
available: foundZstdBinary && version >= MIN_REQUIRED_BSD_TAR_VERSION, available: foundZstdBinary &&
// Do a loose comparison since these version numbers don't contain
// a patch version number.
semver.gte(version, MIN_REQUIRED_BSD_TAR_VERSION),
foundZstdBinary, foundZstdBinary,
version: tarVersion, version: tarVersion,
}; };
@ -179,10 +185,16 @@ async function extractTarZst(tar, dest, tarVersion, logger) {
throw e; throw e;
} }
} }
const KNOWN_EXTENSIONS = {
"tar.gz": "gzip",
"tar.zst": "zstd",
};
function inferCompressionMethod(tarPath) { function inferCompressionMethod(tarPath) {
if (tarPath.endsWith(".tar.gz")) { for (const [ext, method] of Object.entries(KNOWN_EXTENSIONS)) {
return "gzip"; if (tarPath.endsWith(`.${ext}`)) {
return method;
}
} }
return "zstd"; return undefined;
} }
//# sourceMappingURL=tar.js.map //# sourceMappingURL=tar.js.map

File diff suppressed because one or more lines are too long

3
lib/tools-download.js generated
View file

@ -73,9 +73,8 @@ function makeStreamedToolsDownloadDurations(combinedDurationMs) {
streamExtraction: true, streamExtraction: true,
}; };
} }
async function downloadAndExtract(codeqlURL, dest, authorization, headers, tarVersion, logger) { async function downloadAndExtract(codeqlURL, compressionMethod, dest, authorization, headers, tarVersion, logger) {
logger.info(`Downloading CodeQL tools from ${codeqlURL} . This may take a while.`); logger.info(`Downloading CodeQL tools from ${codeqlURL} . This may take a while.`);
const compressionMethod = tar.inferCompressionMethod(codeqlURL);
try { try {
if (compressionMethod === "zstd" && process.platform === "linux") { if (compressionMethod === "zstd" && process.platform === "linux") {
logger.info(`Streaming the extraction of the CodeQL bundle.`); logger.info(`Streaming the extraction of the CodeQL bundle.`);

View file

@ -1 +1 @@
{"version":3,"file":"tools-download.js","sourceRoot":"","sources":["../src/tools-download.ts"],"names":[],"mappings":"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"} {"version":3,"file":"tools-download.js","sourceRoot":"","sources":["../src/tools-download.ts"],"names":[],"mappings":"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"}

View file

@ -39,12 +39,21 @@ export const CODEQL_DEFAULT_ACTION_REPOSITORY = "github/codeql-action";
const CODEQL_BUNDLE_VERSION_ALIAS: string[] = ["linked", "latest"]; const CODEQL_BUNDLE_VERSION_ALIAS: string[] = ["linked", "latest"];
function getCodeQLBundleExtension(useZstd: boolean): string { function getCodeQLBundleExtension(
return useZstd ? ".tar.zst" : ".tar.gz"; compressionMethod: tar.CompressionMethod,
): string {
switch (compressionMethod) {
case "gzip":
return ".tar.gz";
case "zstd":
return ".tar.zst";
default:
util.assertNever(compressionMethod);
}
} }
function getCodeQLBundleName(useZstd: boolean): string { function getCodeQLBundleName(compressionMethod: tar.CompressionMethod): string {
const extension = getCodeQLBundleExtension(useZstd); const extension = getCodeQLBundleExtension(compressionMethod);
let platform: string; let platform: string;
if (process.platform === "win32") { if (process.platform === "win32") {
@ -76,7 +85,7 @@ export function getCodeQLActionRepository(logger: Logger): string {
async function getCodeQLBundleDownloadURL( async function getCodeQLBundleDownloadURL(
tagName: string, tagName: string,
apiDetails: api.GitHubApiDetails, apiDetails: api.GitHubApiDetails,
useZstd: boolean, compressionMethod: tar.CompressionMethod,
logger: Logger, logger: Logger,
): Promise<string> { ): Promise<string> {
const codeQLActionRepository = getCodeQLActionRepository(logger); const codeQLActionRepository = getCodeQLActionRepository(logger);
@ -95,7 +104,7 @@ async function getCodeQLBundleDownloadURL(
return !self.slice(0, index).some((other) => deepEqual(source, other)); return !self.slice(0, index).some((other) => deepEqual(source, other));
}, },
); );
const codeQLBundleName = getCodeQLBundleName(useZstd); const codeQLBundleName = getCodeQLBundleName(compressionMethod);
for (const downloadSource of uniqueDownloadSources) { for (const downloadSource of uniqueDownloadSources) {
const [apiURL, repository] = downloadSource; 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.
@ -115,14 +124,14 @@ async function getCodeQLBundleDownloadURL(
for (const asset of release.data.assets) { for (const asset of release.data.assets) {
if (asset.name === codeQLBundleName) { if (asset.name === codeQLBundleName) {
logger.info( logger.info(
`Found CodeQL bundle in ${downloadSource[1]} on ${downloadSource[0]} with URL ${asset.url}.`, `Found CodeQL bundle ${codeQLBundleName} in ${repository} on ${apiURL} with URL ${asset.url}.`,
); );
return asset.url; return asset.url;
} }
} }
} catch (e) { } catch (e) {
logger.info( logger.info(
`Looked for CodeQL bundle in ${downloadSource[1]} on ${downloadSource[0]} but got error ${e}.`, `Looked for CodeQL bundle ${codeQLBundleName} in ${repository} on ${apiURL} but got error ${e}.`,
); );
} }
} }
@ -198,6 +207,7 @@ export function convertToSemVer(version: string, logger: Logger): string {
type CodeQLToolsSource = type CodeQLToolsSource =
| { | {
codeqlTarPath: string; codeqlTarPath: string;
compressionMethod: tar.CompressionMethod;
sourceType: "local"; sourceType: "local";
/** Human-readable description of the source of the tools for telemetry purposes. */ /** Human-readable description of the source of the tools for telemetry purposes. */
toolsVersion: "local"; toolsVersion: "local";
@ -213,6 +223,7 @@ type CodeQLToolsSource =
bundleVersion?: string; bundleVersion?: string;
/** CLI version of the tools, if known. */ /** CLI version of the tools, if known. */
cliVersion?: string; cliVersion?: string;
compressionMethod: tar.CompressionMethod;
codeqlURL: string; codeqlURL: string;
sourceType: "download"; sourceType: "download";
/** Human-readable description of the source of the tools for telemetry purposes. */ /** Human-readable description of the source of the tools for telemetry purposes. */
@ -272,8 +283,16 @@ export async function getCodeQLSource(
!toolsInput.startsWith("http") !toolsInput.startsWith("http")
) { ) {
logger.info(`Using CodeQL CLI from local path ${toolsInput}`); 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 { return {
codeqlTarPath: toolsInput, codeqlTarPath: toolsInput,
compressionMethod,
sourceType: "local", sourceType: "local",
toolsVersion: "local", toolsVersion: "local",
}; };
@ -455,14 +474,30 @@ export async function getCodeQLSource(
} }
} }
let compressionMethod: tar.CompressionMethod;
if (!url) { if (!url) {
compressionMethod =
cliVersion !== undefined &&
(await useZstdBundle(cliVersion, tarSupportsZstd))
? "zstd"
: "gzip";
url = await getCodeQLBundleDownloadURL( url = await getCodeQLBundleDownloadURL(
tagName!, tagName!,
apiDetails, apiDetails,
cliVersion !== undefined && compressionMethod,
(await useZstdBundle(cliVersion, tarSupportsZstd)),
logger, 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) { if (cliVersion) {
@ -474,6 +509,7 @@ export async function getCodeQLSource(
bundleVersion: tagName && tryGetBundleVersionFromTagName(tagName, logger), bundleVersion: tagName && tryGetBundleVersionFromTagName(tagName, logger),
cliVersion, cliVersion,
codeqlURL: url, codeqlURL: url,
compressionMethod,
sourceType: "download", sourceType: "download",
toolsVersion: cliVersion ?? humanReadableVersion, toolsVersion: cliVersion ?? humanReadableVersion,
}; };
@ -504,6 +540,7 @@ export async function tryGetFallbackToolcacheVersion(
// be able to stub this function and have other functions in this file use that stub. // be able to stub this function and have other functions in this file use that stub.
export const downloadCodeQL = async function ( export const downloadCodeQL = async function (
codeqlURL: string, codeqlURL: string,
compressionMethod: tar.CompressionMethod,
maybeBundleVersion: string | undefined, maybeBundleVersion: string | undefined,
maybeCliVersion: string | undefined, maybeCliVersion: string | undefined,
apiDetails: api.GitHubApiDetails, apiDetails: api.GitHubApiDetails,
@ -552,6 +589,7 @@ export const downloadCodeQL = async function (
let statusReport = await downloadAndExtract( let statusReport = await downloadAndExtract(
codeqlURL, codeqlURL,
compressionMethod,
extractedBundlePath, extractedBundlePath,
authorization, authorization,
{ "User-Agent": "CodeQL Action", ...headers }, { "User-Agent": "CodeQL Action", ...headers },
@ -714,13 +752,10 @@ export async function setupCodeQLBundle(
let toolsSource: ToolsSource; let toolsSource: ToolsSource;
switch (source.sourceType) { switch (source.sourceType) {
case "local": { case "local": {
const compressionMethod = tar.inferCompressionMethod(
source.codeqlTarPath,
);
codeqlFolder = await tar.extract( codeqlFolder = await tar.extract(
source.codeqlTarPath, source.codeqlTarPath,
getTempExtractionDir(tempDir), getTempExtractionDir(tempDir),
compressionMethod, source.compressionMethod,
zstdAvailability.version, zstdAvailability.version,
logger, logger,
); );
@ -735,6 +770,7 @@ export async function setupCodeQLBundle(
case "download": { case "download": {
const result = await downloadCodeQL( const result = await downloadCodeQL(
source.codeqlURL, source.codeqlURL,
source.compressionMethod,
source.bundleVersion, source.bundleVersion,
source.cliVersion, source.cliVersion,
apiDetails, apiDetails,

View file

@ -5,6 +5,7 @@ import * as stream from "stream";
import { ToolRunner } from "@actions/exec/lib/toolrunner"; import { ToolRunner } from "@actions/exec/lib/toolrunner";
import * as io from "@actions/io"; import * as io from "@actions/io";
import * as toolcache from "@actions/tool-cache"; import * as toolcache from "@actions/tool-cache";
import * as semver from "semver";
import { CommandInvocationError } from "./actions-util"; import { CommandInvocationError } from "./actions-util";
import { Logger } from "./logging"; import { Logger } from "./logging";
@ -68,13 +69,23 @@ export async function isZstdAvailable(
switch (type) { switch (type) {
case "gnu": case "gnu":
return { return {
available: foundZstdBinary && version >= MIN_REQUIRED_GNU_TAR_VERSION, available:
foundZstdBinary &&
// GNU tar only uses major and minor version numbers
semver.gte(
semver.coerce(version)!,
semver.coerce(MIN_REQUIRED_GNU_TAR_VERSION)!,
),
foundZstdBinary, foundZstdBinary,
version: tarVersion, version: tarVersion,
}; };
case "bsd": case "bsd":
return { return {
available: foundZstdBinary && version >= MIN_REQUIRED_BSD_TAR_VERSION, available:
foundZstdBinary &&
// Do a loose comparison since these version numbers don't contain
// a patch version number.
semver.gte(version, MIN_REQUIRED_BSD_TAR_VERSION),
foundZstdBinary, foundZstdBinary,
version: tarVersion, version: tarVersion,
}; };
@ -202,9 +213,18 @@ export async function extractTarZst(
} }
} }
export function inferCompressionMethod(tarPath: string): CompressionMethod { const KNOWN_EXTENSIONS: Record<string, CompressionMethod> = {
if (tarPath.endsWith(".tar.gz")) { "tar.gz": "gzip",
return "gzip"; "tar.zst": "zstd",
};
export function inferCompressionMethod(
tarPath: string,
): CompressionMethod | undefined {
for (const [ext, method] of Object.entries(KNOWN_EXTENSIONS)) {
if (tarPath.endsWith(`.${ext}`)) {
return method;
}
} }
return "zstd"; return undefined;
} }

View file

@ -82,6 +82,7 @@ export type ToolsDownloadStatusReport = {
export async function downloadAndExtract( export async function downloadAndExtract(
codeqlURL: string, codeqlURL: string,
compressionMethod: tar.CompressionMethod,
dest: string, dest: string,
authorization: string | undefined, authorization: string | undefined,
headers: OutgoingHttpHeaders, headers: OutgoingHttpHeaders,
@ -92,8 +93,6 @@ export async function downloadAndExtract(
`Downloading CodeQL tools from ${codeqlURL} . This may take a while.`, `Downloading CodeQL tools from ${codeqlURL} . This may take a while.`,
); );
const compressionMethod = tar.inferCompressionMethod(codeqlURL);
try { try {
if (compressionMethod === "zstd" && process.platform === "linux") { if (compressionMethod === "zstd" && process.platform === "linux") {
logger.info(`Streaming the extraction of the CodeQL bundle.`); logger.info(`Streaming the extraction of the CodeQL bundle.`);