Skip to content

Commit 8c1551c

Browse files
authored
Merge pull request #2767 from github/cklin/prefer-gtar
Prefer gtar if available
2 parents 1c15a48 + fc4873b commit 8c1551c

File tree

4 files changed

+55
-24
lines changed

4 files changed

+55
-24
lines changed

CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ See the [releases page](https://github.com/github/codeql-action/releases) for th
44

55
## [UNRELEASED]
66

7-
No user facing changes.
7+
- Update the action to prefer `gtar` over `tar` to make zstd archive extraction more robust. [2767](https://github.com/github/codeql-action/pull/2767)
88

99
## 3.28.9 - 07 Feb 2025
1010

lib/tar.js

+26-11
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/tar.ts

+27-11
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@ const MIN_REQUIRED_BSD_TAR_VERSION = "3.4.3";
1515
const MIN_REQUIRED_GNU_TAR_VERSION = "1.31";
1616

1717
export type TarVersion = {
18+
name: string;
1819
type: "gnu" | "bsd";
1920
version: string;
2021
};
2122

22-
async function getTarVersion(): Promise<TarVersion> {
23-
const tar = await io.which("tar", true);
23+
async function getTarVersion(programName: string): Promise<TarVersion> {
24+
const tar = await io.which(programName, true);
2425
let stdout = "";
2526
const exitCode = await new ToolRunner(tar, ["--version"], {
2627
listeners: {
@@ -30,28 +31,43 @@ async function getTarVersion(): Promise<TarVersion> {
3031
},
3132
}).exec();
3233
if (exitCode !== 0) {
33-
throw new Error("Failed to call tar --version");
34+
throw new Error(`Failed to call ${programName} --version`);
3435
}
3536
// Return whether this is GNU tar or BSD tar, and the version number
3637
if (stdout.includes("GNU tar")) {
3738
const match = stdout.match(/tar \(GNU tar\) ([0-9.]+)/);
3839
if (!match || !match[1]) {
39-
throw new Error("Failed to parse output of tar --version.");
40+
throw new Error(`Failed to parse output of ${programName} --version.`);
4041
}
4142

42-
return { type: "gnu", version: match[1] };
43+
return { name: programName, type: "gnu", version: match[1] };
4344
} else if (stdout.includes("bsdtar")) {
4445
const match = stdout.match(/bsdtar ([0-9.]+)/);
4546
if (!match || !match[1]) {
46-
throw new Error("Failed to parse output of tar --version.");
47+
throw new Error(`Failed to parse output of ${programName} --version.`);
4748
}
4849

49-
return { type: "bsd", version: match[1] };
50+
return { name: programName, type: "bsd", version: match[1] };
5051
} else {
5152
throw new Error("Unknown tar version");
5253
}
5354
}
5455

56+
async function pickTarCommand(): Promise<TarVersion> {
57+
// bsdtar 3.5.3 on the macos-14 (arm) action runner image is prone to crash with the following
58+
// error messages when extracting zstd archives:
59+
//
60+
// tar: Child process exited with status 1
61+
// tar: Error exit delayed from previous errors.
62+
//
63+
// To avoid this problem, prefer GNU tar under the name "gtar" if it is available.
64+
try {
65+
return await getTarVersion("gtar");
66+
} catch {
67+
return await getTarVersion("tar");
68+
}
69+
}
70+
5571
export interface ZstdAvailability {
5672
available: boolean;
5773
foundZstdBinary: boolean;
@@ -63,7 +79,7 @@ export async function isZstdAvailable(
6379
): Promise<ZstdAvailability> {
6480
const foundZstdBinary = await isBinaryAccessible("zstd", logger);
6581
try {
66-
const tarVersion = await getTarVersion();
82+
const tarVersion = await pickTarCommand();
6783
const { type, version } = tarVersion;
6884
logger.info(`Found ${type} tar version ${version}.`);
6985
switch (type) {
@@ -162,10 +178,10 @@ export async function extractTarZst(
162178

163179
args.push("-f", tar instanceof stream.Readable ? "-" : tar, "-C", dest);
164180

165-
process.stdout.write(`[command]tar ${args.join(" ")}\n`);
181+
process.stdout.write(`[command]${tarVersion.name} ${args.join(" ")}\n`);
166182

167183
await new Promise<void>((resolve, reject) => {
168-
const tarProcess = spawn("tar", args, { stdio: "pipe" });
184+
const tarProcess = spawn(tarVersion.name, args, { stdio: "pipe" });
169185

170186
let stdout = "";
171187
tarProcess.stdout?.on("data", (data: Buffer) => {
@@ -196,7 +212,7 @@ export async function extractTarZst(
196212
if (code !== 0) {
197213
reject(
198214
new CommandInvocationError(
199-
"tar",
215+
tarVersion.name,
200216
args,
201217
code ?? undefined,
202218
stdout,

0 commit comments

Comments
 (0)