1+ import { spawn } from "child_process" ;
12import * as fs from "fs" ;
23import path from "path" ;
4+ import * as stream from "stream" ;
35
46import { ToolRunner } from "@actions/exec/lib/toolrunner" ;
57import * as toolcache from "@actions/tool-cache" ;
68import { safeWhich } from "@chrisgavin/safe-which" ;
79import { v4 as uuidV4 } from "uuid" ;
810
9- import { getTemporaryDirectory , runTool } from "./actions-util" ;
11+ import { CommandInvocationError , getTemporaryDirectory } from "./actions-util" ;
1012import { Logger } from "./logging" ;
1113import { assertNever , cleanUpGlob } from "./util" ;
1214
@@ -123,7 +125,11 @@ export async function extract(
123125 "Could not determine tar version, which is required to extract a Zstandard archive." ,
124126 ) ;
125127 }
126- return await extractTarZst ( tarPath , tarVersion , logger ) ;
128+ return await extractTarZst (
129+ fs . createReadStream ( tarPath ) ,
130+ tarVersion ,
131+ logger ,
132+ ) ;
127133 }
128134}
129135
@@ -135,46 +141,64 @@ export async function extract(
135141 * @returns path to the destination directory
136142 */
137143export async function extractTarZst (
138- file : string ,
144+ tarStream : stream . Readable ,
139145 tarVersion : TarVersion ,
140146 logger : Logger ,
141147) : Promise < string > {
142- if ( ! file ) {
143- throw new Error ( "parameter 'file' is required" ) ;
144- }
145-
146- // Create dest
147148 const dest = await createExtractFolder ( ) ;
148149
149150 try {
150151 // Initialize args
151- const args = [ "-x" , "-v" ] ;
152-
153- let destArg = dest ;
154- let fileArg = file ;
155- if ( process . platform === "win32" && tarVersion . type === "gnu" ) {
156- args . push ( "--force-local" ) ;
157- destArg = dest . replace ( / \\ / g, "/" ) ;
158-
159- // Technically only the dest needs to have `/` but for aesthetic consistency
160- // convert slashes in the file arg too.
161- fileArg = file . replace ( / \\ / g, "/" ) ;
162- }
152+ const args = [ "-x" , "--zstd" ] ;
163153
164154 if ( tarVersion . type === "gnu" ) {
165155 // Suppress warnings when using GNU tar to extract archives created by BSD tar
166156 args . push ( "--warning=no-unknown-keyword" ) ;
167157 args . push ( "--overwrite" ) ;
168158 }
169159
170- args . push ( "-C" , destArg , "-f" , fileArg ) ;
171- await runTool ( `tar` , args ) ;
160+ args . push ( "-f" , "-" , "-C" , dest ) ;
161+
162+ process . stdout . write ( `[command]tar ${ args . join ( " " ) } \n` ) ;
163+
164+ const tarProcess = spawn ( "tar" , args , { stdio : "pipe" } ) ;
165+ let stdout = "" ;
166+ tarProcess . stdout ?. on ( "data" , ( data : Buffer ) => {
167+ stdout += data . toString ( ) ;
168+ process . stdout . write ( data ) ;
169+ } ) ;
170+
171+ let stderr = "" ;
172+ tarProcess . stderr ?. on ( "data" , ( data : Buffer ) => {
173+ stderr += data . toString ( ) ;
174+ // Mimic the standard behavior of the toolrunner by writing stderr to stdout
175+ process . stdout . write ( data ) ;
176+ } ) ;
177+
178+ tarStream . pipe ( tarProcess . stdin ) ;
179+
180+ await new Promise < void > ( ( resolve , reject ) => {
181+ tarProcess . on ( "exit" , ( code ) => {
182+ if ( code !== 0 ) {
183+ reject (
184+ new CommandInvocationError (
185+ "tar" ,
186+ args ,
187+ code ?? undefined ,
188+ stdout ,
189+ stderr ,
190+ ) ,
191+ ) ;
192+ }
193+ resolve ( ) ;
194+ } ) ;
195+ } ) ;
196+
197+ return dest ;
172198 } catch ( e ) {
173199 await cleanUpGlob ( dest , "extraction destination directory" , logger ) ;
174200 throw e ;
175201 }
176-
177- return dest ;
178202}
179203
180204async function createExtractFolder ( ) : Promise < string > {
0 commit comments