Skip to content

Commit 3da852e

Browse files
committed
Capture stderr from extracting .tar.zst
1 parent 1aa7f6f commit 3da852e

File tree

6 files changed

+147
-28
lines changed

6 files changed

+147
-28
lines changed

lib/setup-codeql.js

+8-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/setup-codeql.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/tar.js

+56-10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/tar.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/setup-codeql.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { default as deepEqual } from "fast-deep-equal";
99
import * as semver from "semver";
1010
import { v4 as uuidV4 } from "uuid";
1111

12-
import { isRunningLocalAction } from "./actions-util";
12+
import { CommandInvocationError, isRunningLocalAction } from "./actions-util";
1313
import * as api from "./api-client";
1414
// Note: defaults.json is referenced from the CodeQL Action sync tool and the Actions runner image
1515
// creation scripts. Ensure that any changes to the format of this file are compatible with both of
@@ -497,6 +497,7 @@ export const downloadCodeQL = async function (
497497
maybeBundleVersion: string | undefined,
498498
maybeCliVersion: string | undefined,
499499
apiDetails: api.GitHubApiDetails,
500+
tarVersion: tar.TarVersion | undefined,
500501
tempDir: string,
501502
logger: Logger,
502503
): Promise<{
@@ -554,6 +555,7 @@ export const downloadCodeQL = async function (
554555
const extractedBundlePath = await tar.extract(
555556
archivedBundlePath,
556557
compressionMethod,
558+
tarVersion,
557559
);
558560
const extractionDurationMs = Math.round(performance.now() - extractionStart);
559561
logger.debug(
@@ -700,6 +702,10 @@ export async function setupCodeQLBundle(
700702
);
701703
} catch (e) {
702704
zstdFailureReason = util.getErrorMessage(e) || "unknown error";
705+
if (e instanceof CommandInvocationError) {
706+
zstdFailureReason += ` Full error: ${e.stderr}`;
707+
logger.debug(`Invocation output the following to stderr: ${e.stderr}`);
708+
}
703709
logger.warning(
704710
`Failed to set up CodeQL tools with zstd. Falling back to gzipped version. Error: ${util.getErrorMessage(
705711
e,
@@ -755,7 +761,11 @@ async function setupCodeQLBundleWithCompressionMethod(
755761
const compressionMethod = tar.inferCompressionMethod(
756762
source.codeqlTarPath,
757763
);
758-
codeqlFolder = await tar.extract(source.codeqlTarPath, compressionMethod);
764+
codeqlFolder = await tar.extract(
765+
source.codeqlTarPath,
766+
compressionMethod,
767+
zstdAvailability.version,
768+
);
759769
toolsSource = ToolsSource.Local;
760770
break;
761771
}
@@ -770,6 +780,7 @@ async function setupCodeQLBundleWithCompressionMethod(
770780
source.bundleVersion,
771781
source.cliVersion,
772782
apiDetails,
783+
zstdAvailability.version,
773784
tempDir,
774785
logger,
775786
);

src/tar.ts

+68-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
import * as fs from "fs";
2+
import path from "path";
3+
14
import { ToolRunner } from "@actions/exec/lib/toolrunner";
25
import * as toolcache from "@actions/tool-cache";
36
import { safeWhich } from "@chrisgavin/safe-which";
7+
import { v4 as uuidV4 } from "uuid";
48

9+
import { getTemporaryDirectory, runTool } from "./actions-util";
510
import { Logger } from "./logging";
611
import { assertNever } from "./util";
712

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

8691
export async function extract(
87-
path: string,
92+
tarPath: string,
8893
compressionMethod: CompressionMethod,
94+
tarVersion: TarVersion | undefined,
8995
): Promise<string> {
9096
switch (compressionMethod) {
9197
case "gzip":
92-
// While we could also ask tar to autodetect the compression method,
93-
// we defensively keep the gzip call identical as requesting a gzipped
94-
// bundle will soon be a fallback option.
95-
return await toolcache.extractTar(path);
98+
// Defensively continue to call the toolcache API as requesting a gzipped
99+
// bundle may be a fallback option.
100+
return await toolcache.extractTar(tarPath);
96101
case "zstd":
97-
// By specifying only the "x" flag, we ask tar to autodetect the
98-
// compression method.
99-
return await toolcache.extractTar(path, undefined, "x");
102+
if (!tarVersion) {
103+
throw new Error(
104+
"Could not determine tar version, which is required to extract a Zstandard archive.",
105+
);
106+
}
107+
return await extractTarZst(tarPath, tarVersion);
108+
}
109+
}
110+
111+
/**
112+
* Extract a compressed tar archive
113+
*
114+
* @param file path to the tar
115+
* @param dest destination directory. Optional.
116+
* @returns path to the destination directory
117+
*/
118+
export async function extractTarZst(
119+
file: string,
120+
tarVersion: TarVersion,
121+
): Promise<string> {
122+
if (!file) {
123+
throw new Error("parameter 'file' is required");
124+
}
125+
126+
// Create dest
127+
const dest = await createExtractFolder();
128+
129+
// Initialize args
130+
const args = ["-x", "-v"];
131+
132+
let destArg = dest;
133+
let fileArg = file;
134+
if (process.platform === "win32" && tarVersion.type === "gnu") {
135+
args.push("--force-local");
136+
destArg = dest.replace(/\\/g, "/");
137+
138+
// Technically only the dest needs to have `/` but for aesthetic consistency
139+
// convert slashes in the file arg too.
140+
fileArg = file.replace(/\\/g, "/");
141+
}
142+
143+
if (tarVersion.type === "gnu") {
144+
// Suppress warnings when using GNU tar to extract archives created by BSD tar
145+
args.push("--warning=no-unknown-keyword");
146+
args.push("--overwrite");
100147
}
148+
149+
args.push("-C", destArg, "-f", fileArg);
150+
await runTool(`tar`, args);
151+
152+
return dest;
153+
}
154+
155+
async function createExtractFolder(): Promise<string> {
156+
const dest = path.join(getTemporaryDirectory(), uuidV4());
157+
fs.mkdirSync(dest, { recursive: true });
158+
return dest;
101159
}
102160

103-
export function inferCompressionMethod(path: string): CompressionMethod {
104-
if (path.endsWith(".tar.gz")) {
161+
export function inferCompressionMethod(tarPath: string): CompressionMethod {
162+
if (tarPath.endsWith(".tar.gz")) {
105163
return "gzip";
106164
}
107165
return "zstd";

0 commit comments

Comments
 (0)