1
+ import { spawn } from "child_process" ;
1
2
import * as fs from "fs" ;
2
3
import path from "path" ;
4
+ import * as stream from "stream" ;
3
5
4
6
import { ToolRunner } from "@actions/exec/lib/toolrunner" ;
5
7
import * as toolcache from "@actions/tool-cache" ;
6
8
import { safeWhich } from "@chrisgavin/safe-which" ;
7
9
import { v4 as uuidV4 } from "uuid" ;
8
10
9
- import { getTemporaryDirectory , runTool } from "./actions-util" ;
11
+ import { CommandInvocationError , getTemporaryDirectory } from "./actions-util" ;
10
12
import { Logger } from "./logging" ;
11
13
import { assertNever , cleanUpGlob } from "./util" ;
12
14
@@ -123,7 +125,11 @@ export async function extract(
123
125
"Could not determine tar version, which is required to extract a Zstandard archive." ,
124
126
) ;
125
127
}
126
- return await extractTarZst ( tarPath , tarVersion , logger ) ;
128
+ return await extractTarZst (
129
+ fs . createReadStream ( tarPath ) ,
130
+ tarVersion ,
131
+ logger ,
132
+ ) ;
127
133
}
128
134
}
129
135
@@ -135,46 +141,64 @@ export async function extract(
135
141
* @returns path to the destination directory
136
142
*/
137
143
export async function extractTarZst (
138
- file : string ,
144
+ tarStream : stream . Readable ,
139
145
tarVersion : TarVersion ,
140
146
logger : Logger ,
141
147
) : Promise < string > {
142
- if ( ! file ) {
143
- throw new Error ( "parameter 'file' is required" ) ;
144
- }
145
-
146
- // Create dest
147
148
const dest = await createExtractFolder ( ) ;
148
149
149
150
try {
150
151
// 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" ] ;
163
153
164
154
if ( tarVersion . type === "gnu" ) {
165
155
// Suppress warnings when using GNU tar to extract archives created by BSD tar
166
156
args . push ( "--warning=no-unknown-keyword" ) ;
167
157
args . push ( "--overwrite" ) ;
168
158
}
169
159
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 ;
172
198
} catch ( e ) {
173
199
await cleanUpGlob ( dest , "extraction destination directory" , logger ) ;
174
200
throw e ;
175
201
}
176
-
177
- return dest ;
178
202
}
179
203
180
204
async function createExtractFolder ( ) : Promise < string > {
0 commit comments