@@ -5,6 +5,9 @@ import unzipper from 'unzipper'
5
5
import { spawn } from 'child_process'
6
6
import { delimiter } from 'path'
7
7
8
+ const gitForWindowsUsrBinPath = 'C:/Program Files/Git/usr/bin'
9
+ const gitForWindowsMINGW64BinPath = 'C:/Program Files/Git/mingw64/bin'
10
+
8
11
async function fetchJSONFromURL < T > ( url : string ) : Promise < T > {
9
12
return new Promise < T > ( ( resolve , reject ) => {
10
13
https
@@ -54,12 +57,11 @@ async function unzip(
54
57
stripPrefix : string ,
55
58
outputDirectory : string ,
56
59
verbose : boolean | number ,
57
- streamEntries ?: (
58
- path : string ,
59
- stream : Readable ,
60
+ downloader ?: (
61
+ _url : string ,
60
62
directory : string ,
61
63
_verbose : boolean | number
62
- ) => void
64
+ ) => Promise < void >
63
65
) : Promise < void > {
64
66
let progress =
65
67
verbose === false
@@ -76,15 +78,19 @@ async function unzip(
76
78
}
77
79
}
78
80
mkdirp ( outputDirectory )
81
+
82
+ if ( downloader ) {
83
+ // `https.get()` seems to have performance problems that cause frequent
84
+ // ECONNRESET problems with larger payloads. Let's (ab-)use Git for Windows'
85
+ // `curl.exe` to do the downloading for us in that case.
86
+ return await downloader ( url , outputDirectory , verbose )
87
+ }
88
+
79
89
return new Promise < void > ( ( resolve , reject ) => {
80
- const handleStream = ( res : Readable ) : void => {
90
+ https . get ( url , ( res : Readable ) : void => {
81
91
res
82
92
. pipe ( unzipper . Parse ( ) )
83
93
. on ( 'entry' , entry => {
84
- if ( streamEntries ) {
85
- streamEntries ( entry . path , entry , outputDirectory , verbose )
86
- return
87
- }
88
94
if ( ! entry . path . startsWith ( stripPrefix ) ) {
89
95
process . stderr . write (
90
96
`warning: skipping ${ entry . path } because it does not start with ${ stripPrefix } \n`
@@ -104,48 +110,72 @@ async function unzip(
104
110
. on ( 'error' , reject )
105
111
. on ( 'finish' , progress )
106
112
. on ( 'finish' , resolve )
107
- }
108
-
109
- if ( ! streamEntries ) {
110
- https . get ( url , handleStream )
111
- } else {
112
- // `https.get()` seems to have performance problems that cause frequent
113
- // ECONNRESET problems with larger payloads. Let's (ab-)use Git for Windows'
114
- // `curl.exe` to do the downloading for us in that case.
115
- const curl = spawn ( 'C:/Program Files/Git/mingw64/bin/curl.exe' , [ url ] )
116
- handleStream ( curl . stdout )
117
- // eslint-disable-next-line no-console
118
- curl . stderr . on ( 'data' , chunk => console . log ( `${ chunk } ` ) )
119
- }
113
+ } )
120
114
} )
121
115
}
122
116
123
117
/* We're (ab-)using Git for Windows' `tar.exe` and `xz.exe` to do the job */
124
- function unpackTarXZEntry (
125
- path : string ,
126
- stream : Readable ,
118
+ async function unpackTarXZInZipFromURL (
119
+ url : string ,
127
120
outputDirectory : string ,
128
121
verbose : boolean | number = false
129
- ) : void {
130
- if ( path . endsWith ( '/' ) ) return
131
- if ( ! path . endsWith ( '.tar.xz' ) ) {
132
- process . stderr . write ( `warning: unhandled entry: ${ path } ` )
133
- return
122
+ ) : Promise < void > {
123
+ const tmp = await fs . promises . mkdtemp ( `${ outputDirectory } /tmp` )
124
+ const zipPath = `${ tmp } /artifacts.zip`
125
+ const curl = spawn (
126
+ `${ gitForWindowsMINGW64BinPath } /curl.exe` ,
127
+ [ '-o' , zipPath , url ] ,
128
+ { stdio : [ undefined , 'inherit' , 'inherit' ] }
129
+ )
130
+ await new Promise < void > ( ( resolve , reject ) => {
131
+ curl
132
+ . on ( 'close' , code =>
133
+ code === 0 ? resolve ( ) : reject ( new Error ( `${ code } ` ) )
134
+ )
135
+ . on ( 'error' , e => reject ( new Error ( `${ e } ` ) ) )
136
+ } )
137
+
138
+ const zipContents = ( await unzipper . Open . file ( zipPath ) ) . files . filter (
139
+ e => ! e . path . endsWith ( '/' )
140
+ )
141
+ if ( zipContents . length !== 1 ) {
142
+ throw new Error (
143
+ `${ zipPath } does not contain exactly one file (${ zipContents . map (
144
+ e => e . path
145
+ ) } )`
146
+ )
134
147
}
135
148
136
- const usrBinPath = 'C:/Program Files/Git/usr/bin'
149
+ // eslint-disable-next-line no-console
150
+ console . log ( `unzipping ${ zipPath } \n` )
137
151
const tarXZ = spawn (
138
- `${ usrBinPath } /tar.exe` ,
139
- [ verbose === true ? 'xJvf' : 'xJf' , '-' ] ,
152
+ `${ gitForWindowsUsrBinPath } /bash.exe` ,
153
+ [
154
+ '-lc' ,
155
+ `unzip -p "${ zipPath } " ${ zipContents [ 0 ] . path } | tar ${
156
+ verbose === true ? 'xJvf' : 'xJf'
157
+ } -`
158
+ ] ,
140
159
{
141
160
cwd : outputDirectory ,
142
161
env : {
143
- PATH : `${ usrBinPath } ${ delimiter } ${ process . env . PATH } `
162
+ CHERE_INVOKING : '1' ,
163
+ MSYSTEM : 'MINGW64' ,
164
+ PATH : `${ gitForWindowsUsrBinPath } ${ delimiter } ${ process . env . PATH } `
144
165
} ,
145
- stdio : [ 'pipe' , 'inherit' , 'inherit' ]
166
+ stdio : [ undefined , 'inherit' , 'inherit' ]
146
167
}
147
168
)
148
- stream . pipe ( tarXZ . stdin )
169
+ await new Promise < void > ( ( resolve , reject ) => {
170
+ tarXZ . on ( 'close' , code => {
171
+ if ( code === 0 ) {
172
+ resolve ( )
173
+ } else {
174
+ reject ( new Error ( `tar: exited with code ${ code } ` ) )
175
+ }
176
+ } )
177
+ } )
178
+ await fs . promises . rmdir ( tmp , { recursive : true } )
149
179
}
150
180
151
181
export async function get (
@@ -221,7 +251,7 @@ export async function get(
221
251
`${ artifactName } /` ,
222
252
outputDirectory ,
223
253
verbose ,
224
- flavor === 'full' ? unpackTarXZEntry : undefined
254
+ flavor === 'full' ? unpackTarXZInZipFromURL : undefined
225
255
)
226
256
}
227
257
return { download, id}
0 commit comments