Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions lib/setup-codeql.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/setup-codeql.js.map

Large diffs are not rendered by default.

66 changes: 56 additions & 10 deletions lib/tar.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/tar.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 13 additions & 2 deletions src/setup-codeql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { default as deepEqual } from "fast-deep-equal";
import * as semver from "semver";
import { v4 as uuidV4 } from "uuid";

import { isRunningLocalAction } from "./actions-util";
import { CommandInvocationError, isRunningLocalAction } from "./actions-util";
import * as api from "./api-client";
// Note: defaults.json is referenced from the CodeQL Action sync tool and the Actions runner image
// creation scripts. Ensure that any changes to the format of this file are compatible with both of
Expand Down Expand Up @@ -497,6 +497,7 @@ export const downloadCodeQL = async function (
maybeBundleVersion: string | undefined,
maybeCliVersion: string | undefined,
apiDetails: api.GitHubApiDetails,
tarVersion: tar.TarVersion | undefined,
tempDir: string,
logger: Logger,
): Promise<{
Expand Down Expand Up @@ -554,6 +555,7 @@ export const downloadCodeQL = async function (
const extractedBundlePath = await tar.extract(
archivedBundlePath,
compressionMethod,
tarVersion,
);
const extractionDurationMs = Math.round(performance.now() - extractionStart);
logger.debug(
Expand Down Expand Up @@ -700,6 +702,10 @@ export async function setupCodeQLBundle(
);
} catch (e) {
zstdFailureReason = util.getErrorMessage(e) || "unknown error";
if (e instanceof CommandInvocationError) {
zstdFailureReason += ` Full error: ${e.stderr}`;
logger.debug(`Invocation output the following to stderr: ${e.stderr}`);
}
logger.warning(
`Failed to set up CodeQL tools with zstd. Falling back to gzipped version. Error: ${util.getErrorMessage(
e,
Expand Down Expand Up @@ -755,7 +761,11 @@ async function setupCodeQLBundleWithCompressionMethod(
const compressionMethod = tar.inferCompressionMethod(
source.codeqlTarPath,
);
codeqlFolder = await tar.extract(source.codeqlTarPath, compressionMethod);
codeqlFolder = await tar.extract(
source.codeqlTarPath,
compressionMethod,
zstdAvailability.version,
);
toolsSource = ToolsSource.Local;
break;
}
Expand All @@ -770,6 +780,7 @@ async function setupCodeQLBundleWithCompressionMethod(
source.bundleVersion,
source.cliVersion,
apiDetails,
zstdAvailability.version,
tempDir,
logger,
);
Expand Down
78 changes: 68 additions & 10 deletions src/tar.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import * as fs from "fs";
import path from "path";

import { ToolRunner } from "@actions/exec/lib/toolrunner";
import * as toolcache from "@actions/tool-cache";
import { safeWhich } from "@chrisgavin/safe-which";
import { v4 as uuidV4 } from "uuid";

import { getTemporaryDirectory, runTool } from "./actions-util";
import { Logger } from "./logging";
import { assertNever } from "./util";

Expand Down Expand Up @@ -84,24 +89,77 @@ export async function isZstdAvailable(
export type CompressionMethod = "gzip" | "zstd";

export async function extract(
path: string,
tarPath: string,
compressionMethod: CompressionMethod,
tarVersion: TarVersion | undefined,
): Promise<string> {
switch (compressionMethod) {
case "gzip":
// While we could also ask tar to autodetect the compression method,
// we defensively keep the gzip call identical as requesting a gzipped
// bundle will soon be a fallback option.
return await toolcache.extractTar(path);
// Defensively continue to call the toolcache API as requesting a gzipped
// bundle may be a fallback option.
return await toolcache.extractTar(tarPath);
case "zstd":
// By specifying only the "x" flag, we ask tar to autodetect the
// compression method.
return await toolcache.extractTar(path, undefined, "x");
if (!tarVersion) {
throw new Error(
"Could not determine tar version, which is required to extract a Zstandard archive.",
);
}
Comment on lines +103 to +107
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does throwing here prevent us from falling back on the gzip version?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea to double check this — this should indeed be covered by the fallback in setupCodeQLBundle.

return await extractTarZst(tarPath, tarVersion);
}
}

/**
* Extract a compressed tar archive
*
* @param file path to the tar
* @param dest destination directory. Optional.
* @returns path to the destination directory
*/
export async function extractTarZst(
file: string,
tarVersion: TarVersion,
): Promise<string> {
if (!file) {
throw new Error("parameter 'file' is required");
}

// Create dest
const dest = await createExtractFolder();

// Initialize args
const args = ["-x", "-v"];

let destArg = dest;
let fileArg = file;
if (process.platform === "win32" && tarVersion.type === "gnu") {
args.push("--force-local");
destArg = dest.replace(/\\/g, "/");

// Technically only the dest needs to have `/` but for aesthetic consistency
// convert slashes in the file arg too.
fileArg = file.replace(/\\/g, "/");
}

if (tarVersion.type === "gnu") {
// Suppress warnings when using GNU tar to extract archives created by BSD tar
args.push("--warning=no-unknown-keyword");
args.push("--overwrite");
}

args.push("-C", destArg, "-f", fileArg);
await runTool(`tar`, args);

return dest;
}

async function createExtractFolder(): Promise<string> {
const dest = path.join(getTemporaryDirectory(), uuidV4());
fs.mkdirSync(dest, { recursive: true });
return dest;
}

export function inferCompressionMethod(path: string): CompressionMethod {
if (path.endsWith(".tar.gz")) {
export function inferCompressionMethod(tarPath: string): CompressionMethod {
if (tarPath.endsWith(".tar.gz")) {
return "gzip";
}
return "zstd";
Expand Down