UNPKG

@oclif/plugin-update

Version:

[![Version](https://img.shields.io/npm/v/@oclif/plugin-update.svg)](https://npmjs.org/package/@oclif/plugin-update) [![Downloads/week](https://img.shields.io/npm/dw/@oclif/plugin-update.svg)](https://npmjs.org/package/@oclif/plugin-update) [![License](htt

89 lines (88 loc) 3 kB
import makeDebug from 'debug'; import crypto from 'node:crypto'; import { existsSync } from 'node:fs'; import { rename, rm } from 'node:fs/promises'; import { join } from 'node:path'; import zlib from 'node:zlib'; import { extract as tarExtract } from 'tar-fs'; import { touch } from './util.js'; const debug = makeDebug('oclif-update'); const ignore = (_name, header) => { switch (header?.type) { case 'directory': case 'file': { if (process.env.OCLIF_DEBUG_UPDATE_FILES) debug(header.name); return false; } case 'symlink': { return true; } default: { throw new Error(header?.type); } } }; async function extract(stream, basename, output, sha) { const getTmp = () => `${output}.partial.${Math.random().toString().split('.')[1].slice(0, 5)}`; let tmp = getTmp(); if (existsSync(tmp)) tmp = getTmp(); debug(`extracting to ${tmp}`); try { await new Promise((resolve, reject) => { let shaValidated = false; let extracted = false; const check = () => shaValidated && extracted && resolve(null); if (sha) { const hasher = crypto.createHash('sha256'); stream.on('error', reject); stream.on('data', (d) => hasher.update(d)); stream.on('end', () => { const shasum = hasher.digest('hex'); if (sha === shasum) { shaValidated = true; check(); } else { reject(new Error(`SHA mismatch: expected ${shasum} to be ${sha}`)); } }); } else shaValidated = true; const extract = tarExtract(tmp, { ignore }); extract.on('error', reject); extract.on('finish', () => { extracted = true; check(); }); const gunzip = zlib.createGunzip(); gunzip.on('error', reject); stream.pipe(gunzip).pipe(extract); }); if (existsSync(output)) { try { await rm(output, { force: true, recursive: true }).catch(debug); } catch (error) { debug(error); await rm(tmp, { force: true, recursive: true }).catch(debug); } } const from = join(tmp, basename); debug('moving %s to %s', from, output); await rename(from, output); await rm(tmp, { force: true, recursive: true }).catch(debug); await touch(output); debug('done extracting'); } catch (error) { await rm(tmp, { force: true, recursive: true }).catch(process.emitWarning); throw error; } } // This is done so that we can stub it in tests export const Extractor = { extract, };