@@ -15,12 +15,13 @@ const MIN_REQUIRED_BSD_TAR_VERSION = "3.4.3";
15
15
const MIN_REQUIRED_GNU_TAR_VERSION = "1.31" ;
16
16
17
17
export type TarVersion = {
18
+ name : string ;
18
19
type : "gnu" | "bsd" ;
19
20
version : string ;
20
21
} ;
21
22
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 ) ;
24
25
let stdout = "" ;
25
26
const exitCode = await new ToolRunner ( tar , [ "--version" ] , {
26
27
listeners : {
@@ -30,28 +31,43 @@ async function getTarVersion(): Promise<TarVersion> {
30
31
} ,
31
32
} ) . exec ( ) ;
32
33
if ( exitCode !== 0 ) {
33
- throw new Error ( " Failed to call tar --version" ) ;
34
+ throw new Error ( ` Failed to call ${ programName } --version` ) ;
34
35
}
35
36
// Return whether this is GNU tar or BSD tar, and the version number
36
37
if ( stdout . includes ( "GNU tar" ) ) {
37
38
const match = stdout . match ( / t a r \( G N U t a r \) ( [ 0 - 9 . ] + ) / ) ;
38
39
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.` ) ;
40
41
}
41
42
42
- return { type : "gnu" , version : match [ 1 ] } ;
43
+ return { name : programName , type : "gnu" , version : match [ 1 ] } ;
43
44
} else if ( stdout . includes ( "bsdtar" ) ) {
44
45
const match = stdout . match ( / b s d t a r ( [ 0 - 9 . ] + ) / ) ;
45
46
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.` ) ;
47
48
}
48
49
49
- return { type : "bsd" , version : match [ 1 ] } ;
50
+ return { name : programName , type : "bsd" , version : match [ 1 ] } ;
50
51
} else {
51
52
throw new Error ( "Unknown tar version" ) ;
52
53
}
53
54
}
54
55
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
+
55
71
export interface ZstdAvailability {
56
72
available : boolean ;
57
73
foundZstdBinary : boolean ;
@@ -63,7 +79,7 @@ export async function isZstdAvailable(
63
79
) : Promise < ZstdAvailability > {
64
80
const foundZstdBinary = await isBinaryAccessible ( "zstd" , logger ) ;
65
81
try {
66
- const tarVersion = await getTarVersion ( ) ;
82
+ const tarVersion = await pickTarCommand ( ) ;
67
83
const { type, version } = tarVersion ;
68
84
logger . info ( `Found ${ type } tar version ${ version } .` ) ;
69
85
switch ( type ) {
@@ -162,10 +178,10 @@ export async function extractTarZst(
162
178
163
179
args . push ( "-f" , tar instanceof stream . Readable ? "-" : tar , "-C" , dest ) ;
164
180
165
- process . stdout . write ( `[command]tar ${ args . join ( " " ) } \n` ) ;
181
+ process . stdout . write ( `[command]${ tarVersion . name } ${ args . join ( " " ) } \n` ) ;
166
182
167
183
await new Promise < void > ( ( resolve , reject ) => {
168
- const tarProcess = spawn ( "tar" , args , { stdio : "pipe" } ) ;
184
+ const tarProcess = spawn ( tarVersion . name , args , { stdio : "pipe" } ) ;
169
185
170
186
let stdout = "" ;
171
187
tarProcess . stdout ?. on ( "data" , ( data : Buffer ) => {
@@ -196,7 +212,7 @@ export async function extractTarZst(
196
212
if ( code !== 0 ) {
197
213
reject (
198
214
new CommandInvocationError (
199
- "tar" ,
215
+ tarVersion . name ,
200
216
args ,
201
217
code ?? undefined ,
202
218
stdout ,
0 commit comments