Skip to content

Commit bb3e381

Browse files
committed
Fix race between two 'finish' events
When unzipping the file that is being downloaded, there are two streams whose `finish` event are eagerly awaited: the stream that unpacks the last entry of the .zip file and the stream that processes the .zip file itself. When this code was originally written, the stream that extracted the .zip entry always sent its `finish` event before the stream that processed the enclosing .zip file. However, with our upgrade to node.js v20.x, this seems no longer to be true. As a consequence, the code that wants to verify that all expected bytes were extracted now _sometimes_ runs too early, and shouts "failure" just before the code that extracts the bytes runs and all files are written correctly. Let's work around it by ensuring that both `finish` events were received before determining success or failure. Signed-off-by: Johannes Schindelin <[email protected]>
1 parent 791f223 commit bb3e381

File tree

1 file changed

+16
-4
lines changed

1 file changed

+16
-4
lines changed

src/downloader.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,16 @@ export async function unzip(
7070
process.stderr.write(`Writing ${storeZipAs}\n`)
7171
res.pipe(fs.createWriteStream(storeZipAs)).on('error', reject)
7272
}
73+
74+
let writingEntry = false
75+
let wroteLastEntry = false
76+
const finish = (): void => {
77+
if (writingEntry || !wroteLastEntry) return
78+
if (bytesToExtract === 0) resolve(files)
79+
// eslint-disable-next-line prefer-promise-reject-errors
80+
else reject(`${bytesToExtract} bytes left to extract`)
81+
}
82+
7383
res
7484
.on('error', reject)
7585
.pipe(unzipper.Parse())
@@ -88,21 +98,23 @@ export async function unzip(
8898
mkdirp(entryPath.replace(/\/$/, ''))
8999
entry.autodrain()
90100
} else {
101+
writingEntry = true
91102
progress(entryPath)
92103
entry
93104
.pipe(fs.createWriteStream(`${entryPath}`))
105+
.on('error', reject)
94106
.on('finish', () => {
95107
bytesToExtract -= fs.statSync(entryPath).size
108+
writingEntry = false
109+
finish()
96110
})
97111
}
98112
})
99113
.on('error', reject)
100114
.on('finish', progress)
101115
.on('finish', () => {
102-
bytesToExtract === 0
103-
? resolve(files)
104-
: // eslint-disable-next-line prefer-promise-reject-errors
105-
reject(`${bytesToExtract} bytes left to extract`)
116+
wroteLastEntry = true
117+
finish()
106118
})
107119
}
108120
if (url.startsWith('file:')) {

0 commit comments

Comments
 (0)