( entries: readonly PackageEntry[], archivePath: string, )
| 32 | } |
| 33 | |
| 34 | async function packageEntries( |
| 35 | entries: readonly PackageEntry[], |
| 36 | archivePath: string, |
| 37 | ): Promise<FeedbackArchive> { |
| 38 | if (entries.length === 0) { |
| 39 | throw new Error('Cannot package an empty feedback archive.'); |
| 40 | } |
| 41 | await mkdir(dirname(archivePath), { recursive: true }); |
| 42 | |
| 43 | const zip = new ZipFile(); |
| 44 | const hash = createHash('sha256'); |
| 45 | const output = createWriteStream(archivePath); |
| 46 | |
| 47 | try { |
| 48 | const done = new Promise<void>((resolvePromise, rejectPromise) => { |
| 49 | output.on('finish', resolvePromise); |
| 50 | output.on('error', rejectPromise); |
| 51 | zip.outputStream.on('error', rejectPromise); |
| 52 | }); |
| 53 | |
| 54 | zip.outputStream.on('data', (chunk: Buffer) => { |
| 55 | hash.update(chunk); |
| 56 | }); |
| 57 | zip.outputStream.pipe(output); |
| 58 | |
| 59 | for (const entry of entries) { |
| 60 | zip.addFile(entry.absolutePath, entry.archivePath, { |
| 61 | mtime: new Date(entry.mtimeMs), |
| 62 | mode: 0o100644, |
| 63 | }); |
| 64 | } |
| 65 | zip.end(); |
| 66 | await done; |
| 67 | |
| 68 | const archiveStat = await stat(archivePath); |
| 69 | return { |
| 70 | path: archivePath, |
| 71 | size: archiveStat.size, |
| 72 | sha256: hash.digest('hex'), |
| 73 | fingerprint: fingerprintEntries(entries), |
| 74 | fileCount: entries.length, |
| 75 | }; |
| 76 | } catch (error) { |
| 77 | // A failed zip (e.g. a source file vanished or became unreadable between |
| 78 | // scan and packaging) would otherwise leave a partial archive behind in |
| 79 | // the cache dir. Destroy the stream so the handle is released before we |
| 80 | // remove the file, then best-effort delete it. |
| 81 | output.destroy(); |
| 82 | await rm(archivePath, { force: true }).catch(() => {}); |
| 83 | throw error; |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | function fingerprintEntries(entries: readonly PackageEntry[]): string { |
| 88 | const hash = createHash('sha256'); |
no test coverage detected