11cb0ef41Sopenharmony_ci// This is the base class that the other fetcher types in lib 21cb0ef41Sopenharmony_ci// all descend from. 31cb0ef41Sopenharmony_ci// It handles the unpacking and retry logic that is shared among 41cb0ef41Sopenharmony_ci// all of the other Fetcher types. 51cb0ef41Sopenharmony_ci 61cb0ef41Sopenharmony_ciconst npa = require('npm-package-arg') 71cb0ef41Sopenharmony_ciconst ssri = require('ssri') 81cb0ef41Sopenharmony_ciconst { promisify } = require('util') 91cb0ef41Sopenharmony_ciconst { basename, dirname } = require('path') 101cb0ef41Sopenharmony_ciconst tar = require('tar') 111cb0ef41Sopenharmony_ciconst log = require('proc-log') 121cb0ef41Sopenharmony_ciconst retry = require('promise-retry') 131cb0ef41Sopenharmony_ciconst fs = require('fs/promises') 141cb0ef41Sopenharmony_ciconst fsm = require('fs-minipass') 151cb0ef41Sopenharmony_ciconst cacache = require('cacache') 161cb0ef41Sopenharmony_ciconst isPackageBin = require('./util/is-package-bin.js') 171cb0ef41Sopenharmony_ciconst removeTrailingSlashes = require('./util/trailing-slashes.js') 181cb0ef41Sopenharmony_ciconst getContents = require('@npmcli/installed-package-contents') 191cb0ef41Sopenharmony_ciconst readPackageJsonFast = require('read-package-json-fast') 201cb0ef41Sopenharmony_ciconst readPackageJson = promisify(require('read-package-json')) 211cb0ef41Sopenharmony_ciconst { Minipass } = require('minipass') 221cb0ef41Sopenharmony_ci 231cb0ef41Sopenharmony_ciconst cacheDir = require('./util/cache-dir.js') 241cb0ef41Sopenharmony_ci 251cb0ef41Sopenharmony_ci// Private methods. 261cb0ef41Sopenharmony_ci// Child classes should not have to override these. 271cb0ef41Sopenharmony_ci// Users should never call them. 281cb0ef41Sopenharmony_ciconst _extract = Symbol('_extract') 291cb0ef41Sopenharmony_ciconst _mkdir = Symbol('_mkdir') 301cb0ef41Sopenharmony_ciconst _empty = Symbol('_empty') 311cb0ef41Sopenharmony_ciconst _toFile = Symbol('_toFile') 321cb0ef41Sopenharmony_ciconst _tarxOptions = Symbol('_tarxOptions') 331cb0ef41Sopenharmony_ciconst _entryMode = Symbol('_entryMode') 341cb0ef41Sopenharmony_ciconst _istream = Symbol('_istream') 351cb0ef41Sopenharmony_ciconst _assertType = Symbol('_assertType') 361cb0ef41Sopenharmony_ciconst _tarballFromCache = Symbol('_tarballFromCache') 371cb0ef41Sopenharmony_ciconst _tarballFromResolved = Symbol.for('pacote.Fetcher._tarballFromResolved') 381cb0ef41Sopenharmony_ciconst _cacheFetches = Symbol.for('pacote.Fetcher._cacheFetches') 391cb0ef41Sopenharmony_ciconst _readPackageJson = Symbol.for('package.Fetcher._readPackageJson') 401cb0ef41Sopenharmony_ci 411cb0ef41Sopenharmony_ciclass FetcherBase { 421cb0ef41Sopenharmony_ci constructor (spec, opts) { 431cb0ef41Sopenharmony_ci if (!opts || typeof opts !== 'object') { 441cb0ef41Sopenharmony_ci throw new TypeError('options object is required') 451cb0ef41Sopenharmony_ci } 461cb0ef41Sopenharmony_ci this.spec = npa(spec, opts.where) 471cb0ef41Sopenharmony_ci 481cb0ef41Sopenharmony_ci this.allowGitIgnore = !!opts.allowGitIgnore 491cb0ef41Sopenharmony_ci 501cb0ef41Sopenharmony_ci // a bit redundant because presumably the caller already knows this, 511cb0ef41Sopenharmony_ci // but it makes it easier to not have to keep track of the requested 521cb0ef41Sopenharmony_ci // spec when we're dispatching thousands of these at once, and normalizing 531cb0ef41Sopenharmony_ci // is nice. saveSpec is preferred if set, because it turns stuff like 541cb0ef41Sopenharmony_ci // x/y#committish into github:x/y#committish. use name@rawSpec for 551cb0ef41Sopenharmony_ci // registry deps so that we turn xyz and xyz@ -> xyz@ 561cb0ef41Sopenharmony_ci this.from = this.spec.registry 571cb0ef41Sopenharmony_ci ? `${this.spec.name}@${this.spec.rawSpec}` : this.spec.saveSpec 581cb0ef41Sopenharmony_ci 591cb0ef41Sopenharmony_ci this[_assertType]() 601cb0ef41Sopenharmony_ci // clone the opts object so that others aren't upset when we mutate it 611cb0ef41Sopenharmony_ci // by adding/modifying the integrity value. 621cb0ef41Sopenharmony_ci this.opts = { ...opts } 631cb0ef41Sopenharmony_ci 641cb0ef41Sopenharmony_ci this.cache = opts.cache || cacheDir().cacache 651cb0ef41Sopenharmony_ci this.tufCache = opts.tufCache || cacheDir().tufcache 661cb0ef41Sopenharmony_ci this.resolved = opts.resolved || null 671cb0ef41Sopenharmony_ci 681cb0ef41Sopenharmony_ci // default to caching/verifying with sha512, that's what we usually have 691cb0ef41Sopenharmony_ci // need to change this default, or start overriding it, when sha512 701cb0ef41Sopenharmony_ci // is no longer strong enough. 711cb0ef41Sopenharmony_ci this.defaultIntegrityAlgorithm = opts.defaultIntegrityAlgorithm || 'sha512' 721cb0ef41Sopenharmony_ci 731cb0ef41Sopenharmony_ci if (typeof opts.integrity === 'string') { 741cb0ef41Sopenharmony_ci this.opts.integrity = ssri.parse(opts.integrity) 751cb0ef41Sopenharmony_ci } 761cb0ef41Sopenharmony_ci 771cb0ef41Sopenharmony_ci this.package = null 781cb0ef41Sopenharmony_ci this.type = this.constructor.name 791cb0ef41Sopenharmony_ci this.fmode = opts.fmode || 0o666 801cb0ef41Sopenharmony_ci this.dmode = opts.dmode || 0o777 811cb0ef41Sopenharmony_ci // we don't need a default umask, because we don't chmod files coming 821cb0ef41Sopenharmony_ci // out of package tarballs. they're forced to have a mode that is 831cb0ef41Sopenharmony_ci // valid, regardless of what's in the tarball entry, and then we let 841cb0ef41Sopenharmony_ci // the process's umask setting do its job. but if configured, we do 851cb0ef41Sopenharmony_ci // respect it. 861cb0ef41Sopenharmony_ci this.umask = opts.umask || 0 871cb0ef41Sopenharmony_ci 881cb0ef41Sopenharmony_ci this.preferOnline = !!opts.preferOnline 891cb0ef41Sopenharmony_ci this.preferOffline = !!opts.preferOffline 901cb0ef41Sopenharmony_ci this.offline = !!opts.offline 911cb0ef41Sopenharmony_ci 921cb0ef41Sopenharmony_ci this.before = opts.before 931cb0ef41Sopenharmony_ci this.fullMetadata = this.before ? true : !!opts.fullMetadata 941cb0ef41Sopenharmony_ci this.fullReadJson = !!opts.fullReadJson 951cb0ef41Sopenharmony_ci if (this.fullReadJson) { 961cb0ef41Sopenharmony_ci this[_readPackageJson] = readPackageJson 971cb0ef41Sopenharmony_ci } else { 981cb0ef41Sopenharmony_ci this[_readPackageJson] = readPackageJsonFast 991cb0ef41Sopenharmony_ci } 1001cb0ef41Sopenharmony_ci 1011cb0ef41Sopenharmony_ci // rrh is a registry hostname or 'never' or 'always' 1021cb0ef41Sopenharmony_ci // defaults to registry.npmjs.org 1031cb0ef41Sopenharmony_ci this.replaceRegistryHost = (!opts.replaceRegistryHost || opts.replaceRegistryHost === 'npmjs') ? 1041cb0ef41Sopenharmony_ci 'registry.npmjs.org' : opts.replaceRegistryHost 1051cb0ef41Sopenharmony_ci 1061cb0ef41Sopenharmony_ci this.defaultTag = opts.defaultTag || 'latest' 1071cb0ef41Sopenharmony_ci this.registry = removeTrailingSlashes(opts.registry || 'https://registry.npmjs.org') 1081cb0ef41Sopenharmony_ci 1091cb0ef41Sopenharmony_ci // command to run 'prepare' scripts on directories and git dirs 1101cb0ef41Sopenharmony_ci // To use pacote with yarn, for example, set npmBin to 'yarn' 1111cb0ef41Sopenharmony_ci // and npmCliConfig with yarn's equivalents. 1121cb0ef41Sopenharmony_ci this.npmBin = opts.npmBin || 'npm' 1131cb0ef41Sopenharmony_ci 1141cb0ef41Sopenharmony_ci // command to install deps for preparing 1151cb0ef41Sopenharmony_ci this.npmInstallCmd = opts.npmInstallCmd || ['install', '--force'] 1161cb0ef41Sopenharmony_ci 1171cb0ef41Sopenharmony_ci // XXX fill more of this in based on what we know from this.opts 1181cb0ef41Sopenharmony_ci // we explicitly DO NOT fill in --tag, though, since we are often 1191cb0ef41Sopenharmony_ci // going to be packing in the context of a publish, which may set 1201cb0ef41Sopenharmony_ci // a dist-tag, but certainly wants to keep defaulting to latest. 1211cb0ef41Sopenharmony_ci this.npmCliConfig = opts.npmCliConfig || [ 1221cb0ef41Sopenharmony_ci `--cache=${dirname(this.cache)}`, 1231cb0ef41Sopenharmony_ci `--prefer-offline=${!!this.preferOffline}`, 1241cb0ef41Sopenharmony_ci `--prefer-online=${!!this.preferOnline}`, 1251cb0ef41Sopenharmony_ci `--offline=${!!this.offline}`, 1261cb0ef41Sopenharmony_ci ...(this.before ? [`--before=${this.before.toISOString()}`] : []), 1271cb0ef41Sopenharmony_ci '--no-progress', 1281cb0ef41Sopenharmony_ci '--no-save', 1291cb0ef41Sopenharmony_ci '--no-audit', 1301cb0ef41Sopenharmony_ci // override any omit settings from the environment 1311cb0ef41Sopenharmony_ci '--include=dev', 1321cb0ef41Sopenharmony_ci '--include=peer', 1331cb0ef41Sopenharmony_ci '--include=optional', 1341cb0ef41Sopenharmony_ci // we need the actual things, not just the lockfile 1351cb0ef41Sopenharmony_ci '--no-package-lock-only', 1361cb0ef41Sopenharmony_ci '--no-dry-run', 1371cb0ef41Sopenharmony_ci ] 1381cb0ef41Sopenharmony_ci } 1391cb0ef41Sopenharmony_ci 1401cb0ef41Sopenharmony_ci get integrity () { 1411cb0ef41Sopenharmony_ci return this.opts.integrity || null 1421cb0ef41Sopenharmony_ci } 1431cb0ef41Sopenharmony_ci 1441cb0ef41Sopenharmony_ci set integrity (i) { 1451cb0ef41Sopenharmony_ci if (!i) { 1461cb0ef41Sopenharmony_ci return 1471cb0ef41Sopenharmony_ci } 1481cb0ef41Sopenharmony_ci 1491cb0ef41Sopenharmony_ci i = ssri.parse(i) 1501cb0ef41Sopenharmony_ci const current = this.opts.integrity 1511cb0ef41Sopenharmony_ci 1521cb0ef41Sopenharmony_ci // do not ever update an existing hash value, but do 1531cb0ef41Sopenharmony_ci // merge in NEW algos and hashes that we don't already have. 1541cb0ef41Sopenharmony_ci if (current) { 1551cb0ef41Sopenharmony_ci current.merge(i) 1561cb0ef41Sopenharmony_ci } else { 1571cb0ef41Sopenharmony_ci this.opts.integrity = i 1581cb0ef41Sopenharmony_ci } 1591cb0ef41Sopenharmony_ci } 1601cb0ef41Sopenharmony_ci 1611cb0ef41Sopenharmony_ci get notImplementedError () { 1621cb0ef41Sopenharmony_ci return new Error('not implemented in this fetcher type: ' + this.type) 1631cb0ef41Sopenharmony_ci } 1641cb0ef41Sopenharmony_ci 1651cb0ef41Sopenharmony_ci // override in child classes 1661cb0ef41Sopenharmony_ci // Returns a Promise that resolves to this.resolved string value 1671cb0ef41Sopenharmony_ci resolve () { 1681cb0ef41Sopenharmony_ci return this.resolved ? Promise.resolve(this.resolved) 1691cb0ef41Sopenharmony_ci : Promise.reject(this.notImplementedError) 1701cb0ef41Sopenharmony_ci } 1711cb0ef41Sopenharmony_ci 1721cb0ef41Sopenharmony_ci packument () { 1731cb0ef41Sopenharmony_ci return Promise.reject(this.notImplementedError) 1741cb0ef41Sopenharmony_ci } 1751cb0ef41Sopenharmony_ci 1761cb0ef41Sopenharmony_ci // override in child class 1771cb0ef41Sopenharmony_ci // returns a manifest containing: 1781cb0ef41Sopenharmony_ci // - name 1791cb0ef41Sopenharmony_ci // - version 1801cb0ef41Sopenharmony_ci // - _resolved 1811cb0ef41Sopenharmony_ci // - _integrity 1821cb0ef41Sopenharmony_ci // - plus whatever else was in there (corgi, full metadata, or pj file) 1831cb0ef41Sopenharmony_ci manifest () { 1841cb0ef41Sopenharmony_ci return Promise.reject(this.notImplementedError) 1851cb0ef41Sopenharmony_ci } 1861cb0ef41Sopenharmony_ci 1871cb0ef41Sopenharmony_ci // private, should be overridden. 1881cb0ef41Sopenharmony_ci // Note that they should *not* calculate or check integrity or cache, 1891cb0ef41Sopenharmony_ci // but *just* return the raw tarball data stream. 1901cb0ef41Sopenharmony_ci [_tarballFromResolved] () { 1911cb0ef41Sopenharmony_ci throw this.notImplementedError 1921cb0ef41Sopenharmony_ci } 1931cb0ef41Sopenharmony_ci 1941cb0ef41Sopenharmony_ci // public, should not be overridden 1951cb0ef41Sopenharmony_ci tarball () { 1961cb0ef41Sopenharmony_ci return this.tarballStream(stream => stream.concat().then(data => { 1971cb0ef41Sopenharmony_ci data.integrity = this.integrity && String(this.integrity) 1981cb0ef41Sopenharmony_ci data.resolved = this.resolved 1991cb0ef41Sopenharmony_ci data.from = this.from 2001cb0ef41Sopenharmony_ci return data 2011cb0ef41Sopenharmony_ci })) 2021cb0ef41Sopenharmony_ci } 2031cb0ef41Sopenharmony_ci 2041cb0ef41Sopenharmony_ci // private 2051cb0ef41Sopenharmony_ci // Note: cacache will raise a EINTEGRITY error if the integrity doesn't match 2061cb0ef41Sopenharmony_ci [_tarballFromCache] () { 2071cb0ef41Sopenharmony_ci return cacache.get.stream.byDigest(this.cache, this.integrity, this.opts) 2081cb0ef41Sopenharmony_ci } 2091cb0ef41Sopenharmony_ci 2101cb0ef41Sopenharmony_ci get [_cacheFetches] () { 2111cb0ef41Sopenharmony_ci return true 2121cb0ef41Sopenharmony_ci } 2131cb0ef41Sopenharmony_ci 2141cb0ef41Sopenharmony_ci [_istream] (stream) { 2151cb0ef41Sopenharmony_ci // if not caching this, just return it 2161cb0ef41Sopenharmony_ci if (!this.opts.cache || !this[_cacheFetches]) { 2171cb0ef41Sopenharmony_ci // instead of creating a new integrity stream, we only piggyback on the 2181cb0ef41Sopenharmony_ci // provided stream's events 2191cb0ef41Sopenharmony_ci if (stream.hasIntegrityEmitter) { 2201cb0ef41Sopenharmony_ci stream.on('integrity', i => this.integrity = i) 2211cb0ef41Sopenharmony_ci return stream 2221cb0ef41Sopenharmony_ci } 2231cb0ef41Sopenharmony_ci 2241cb0ef41Sopenharmony_ci const istream = ssri.integrityStream(this.opts) 2251cb0ef41Sopenharmony_ci istream.on('integrity', i => this.integrity = i) 2261cb0ef41Sopenharmony_ci stream.on('error', err => istream.emit('error', err)) 2271cb0ef41Sopenharmony_ci return stream.pipe(istream) 2281cb0ef41Sopenharmony_ci } 2291cb0ef41Sopenharmony_ci 2301cb0ef41Sopenharmony_ci // we have to return a stream that gets ALL the data, and proxies errors, 2311cb0ef41Sopenharmony_ci // but then pipe from the original tarball stream into the cache as well. 2321cb0ef41Sopenharmony_ci // To do this without losing any data, and since the cacache put stream 2331cb0ef41Sopenharmony_ci // is not a passthrough, we have to pipe from the original stream into 2341cb0ef41Sopenharmony_ci // the cache AFTER we pipe into the middleStream. Since the cache stream 2351cb0ef41Sopenharmony_ci // has an asynchronous flush to write its contents to disk, we need to 2361cb0ef41Sopenharmony_ci // defer the middleStream end until the cache stream ends. 2371cb0ef41Sopenharmony_ci const middleStream = new Minipass() 2381cb0ef41Sopenharmony_ci stream.on('error', err => middleStream.emit('error', err)) 2391cb0ef41Sopenharmony_ci stream.pipe(middleStream, { end: false }) 2401cb0ef41Sopenharmony_ci const cstream = cacache.put.stream( 2411cb0ef41Sopenharmony_ci this.opts.cache, 2421cb0ef41Sopenharmony_ci `pacote:tarball:${this.from}`, 2431cb0ef41Sopenharmony_ci this.opts 2441cb0ef41Sopenharmony_ci ) 2451cb0ef41Sopenharmony_ci cstream.on('integrity', i => this.integrity = i) 2461cb0ef41Sopenharmony_ci cstream.on('error', err => stream.emit('error', err)) 2471cb0ef41Sopenharmony_ci stream.pipe(cstream) 2481cb0ef41Sopenharmony_ci 2491cb0ef41Sopenharmony_ci // eslint-disable-next-line promise/catch-or-return 2501cb0ef41Sopenharmony_ci cstream.promise().catch(() => {}).then(() => middleStream.end()) 2511cb0ef41Sopenharmony_ci return middleStream 2521cb0ef41Sopenharmony_ci } 2531cb0ef41Sopenharmony_ci 2541cb0ef41Sopenharmony_ci pickIntegrityAlgorithm () { 2551cb0ef41Sopenharmony_ci return this.integrity ? this.integrity.pickAlgorithm(this.opts) 2561cb0ef41Sopenharmony_ci : this.defaultIntegrityAlgorithm 2571cb0ef41Sopenharmony_ci } 2581cb0ef41Sopenharmony_ci 2591cb0ef41Sopenharmony_ci // TODO: check error class, once those are rolled out to our deps 2601cb0ef41Sopenharmony_ci isDataCorruptionError (er) { 2611cb0ef41Sopenharmony_ci return er.code === 'EINTEGRITY' || er.code === 'Z_DATA_ERROR' 2621cb0ef41Sopenharmony_ci } 2631cb0ef41Sopenharmony_ci 2641cb0ef41Sopenharmony_ci // override the types getter 2651cb0ef41Sopenharmony_ci get types () { 2661cb0ef41Sopenharmony_ci return false 2671cb0ef41Sopenharmony_ci } 2681cb0ef41Sopenharmony_ci 2691cb0ef41Sopenharmony_ci [_assertType] () { 2701cb0ef41Sopenharmony_ci if (this.types && !this.types.includes(this.spec.type)) { 2711cb0ef41Sopenharmony_ci throw new TypeError(`Wrong spec type (${ 2721cb0ef41Sopenharmony_ci this.spec.type 2731cb0ef41Sopenharmony_ci }) for ${ 2741cb0ef41Sopenharmony_ci this.constructor.name 2751cb0ef41Sopenharmony_ci }. Supported types: ${this.types.join(', ')}`) 2761cb0ef41Sopenharmony_ci } 2771cb0ef41Sopenharmony_ci } 2781cb0ef41Sopenharmony_ci 2791cb0ef41Sopenharmony_ci // We allow ENOENTs from cacache, but not anywhere else. 2801cb0ef41Sopenharmony_ci // An ENOENT trying to read a tgz file, for example, is Right Out. 2811cb0ef41Sopenharmony_ci isRetriableError (er) { 2821cb0ef41Sopenharmony_ci // TODO: check error class, once those are rolled out to our deps 2831cb0ef41Sopenharmony_ci return this.isDataCorruptionError(er) || 2841cb0ef41Sopenharmony_ci er.code === 'ENOENT' || 2851cb0ef41Sopenharmony_ci er.code === 'EISDIR' 2861cb0ef41Sopenharmony_ci } 2871cb0ef41Sopenharmony_ci 2881cb0ef41Sopenharmony_ci // Mostly internal, but has some uses 2891cb0ef41Sopenharmony_ci // Pass in a function which returns a promise 2901cb0ef41Sopenharmony_ci // Function will be called 1 or more times with streams that may fail. 2911cb0ef41Sopenharmony_ci // Retries: 2921cb0ef41Sopenharmony_ci // Function MUST handle errors on the stream by rejecting the promise, 2931cb0ef41Sopenharmony_ci // so that retry logic can pick it up and either retry or fail whatever 2941cb0ef41Sopenharmony_ci // promise it was making (ie, failing extraction, etc.) 2951cb0ef41Sopenharmony_ci // 2961cb0ef41Sopenharmony_ci // The return value of this method is a Promise that resolves the same 2971cb0ef41Sopenharmony_ci // as whatever the streamHandler resolves to. 2981cb0ef41Sopenharmony_ci // 2991cb0ef41Sopenharmony_ci // This should never be overridden by child classes, but it is public. 3001cb0ef41Sopenharmony_ci tarballStream (streamHandler) { 3011cb0ef41Sopenharmony_ci // Only short-circuit via cache if we have everything else we'll need, 3021cb0ef41Sopenharmony_ci // and the user has not expressed a preference for checking online. 3031cb0ef41Sopenharmony_ci 3041cb0ef41Sopenharmony_ci const fromCache = ( 3051cb0ef41Sopenharmony_ci !this.preferOnline && 3061cb0ef41Sopenharmony_ci this.integrity && 3071cb0ef41Sopenharmony_ci this.resolved 3081cb0ef41Sopenharmony_ci ) ? streamHandler(this[_tarballFromCache]()).catch(er => { 3091cb0ef41Sopenharmony_ci if (this.isDataCorruptionError(er)) { 3101cb0ef41Sopenharmony_ci log.warn('tarball', `cached data for ${ 3111cb0ef41Sopenharmony_ci this.spec 3121cb0ef41Sopenharmony_ci } (${this.integrity}) seems to be corrupted. Refreshing cache.`) 3131cb0ef41Sopenharmony_ci return this.cleanupCached().then(() => { 3141cb0ef41Sopenharmony_ci throw er 3151cb0ef41Sopenharmony_ci }) 3161cb0ef41Sopenharmony_ci } else { 3171cb0ef41Sopenharmony_ci throw er 3181cb0ef41Sopenharmony_ci } 3191cb0ef41Sopenharmony_ci }) : null 3201cb0ef41Sopenharmony_ci 3211cb0ef41Sopenharmony_ci const fromResolved = er => { 3221cb0ef41Sopenharmony_ci if (er) { 3231cb0ef41Sopenharmony_ci if (!this.isRetriableError(er)) { 3241cb0ef41Sopenharmony_ci throw er 3251cb0ef41Sopenharmony_ci } 3261cb0ef41Sopenharmony_ci log.silly('tarball', `no local data for ${ 3271cb0ef41Sopenharmony_ci this.spec 3281cb0ef41Sopenharmony_ci }. Extracting by manifest.`) 3291cb0ef41Sopenharmony_ci } 3301cb0ef41Sopenharmony_ci return this.resolve().then(() => retry(tryAgain => 3311cb0ef41Sopenharmony_ci streamHandler(this[_istream](this[_tarballFromResolved]())) 3321cb0ef41Sopenharmony_ci .catch(streamErr => { 3331cb0ef41Sopenharmony_ci // Most likely data integrity. A cache ENOENT error is unlikely 3341cb0ef41Sopenharmony_ci // here, since we're definitely not reading from the cache, but it 3351cb0ef41Sopenharmony_ci // IS possible that the fetch subsystem accessed the cache, and the 3361cb0ef41Sopenharmony_ci // entry got blown away or something. Try one more time to be sure. 3371cb0ef41Sopenharmony_ci if (this.isRetriableError(streamErr)) { 3381cb0ef41Sopenharmony_ci log.warn('tarball', `tarball data for ${ 3391cb0ef41Sopenharmony_ci this.spec 3401cb0ef41Sopenharmony_ci } (${this.integrity}) seems to be corrupted. Trying again.`) 3411cb0ef41Sopenharmony_ci return this.cleanupCached().then(() => tryAgain(streamErr)) 3421cb0ef41Sopenharmony_ci } 3431cb0ef41Sopenharmony_ci throw streamErr 3441cb0ef41Sopenharmony_ci }), { retries: 1, minTimeout: 0, maxTimeout: 0 })) 3451cb0ef41Sopenharmony_ci } 3461cb0ef41Sopenharmony_ci 3471cb0ef41Sopenharmony_ci return fromCache ? fromCache.catch(fromResolved) : fromResolved() 3481cb0ef41Sopenharmony_ci } 3491cb0ef41Sopenharmony_ci 3501cb0ef41Sopenharmony_ci cleanupCached () { 3511cb0ef41Sopenharmony_ci return cacache.rm.content(this.cache, this.integrity, this.opts) 3521cb0ef41Sopenharmony_ci } 3531cb0ef41Sopenharmony_ci 3541cb0ef41Sopenharmony_ci [_empty] (path) { 3551cb0ef41Sopenharmony_ci return getContents({ path, depth: 1 }).then(contents => Promise.all( 3561cb0ef41Sopenharmony_ci contents.map(entry => fs.rm(entry, { recursive: true, force: true })))) 3571cb0ef41Sopenharmony_ci } 3581cb0ef41Sopenharmony_ci 3591cb0ef41Sopenharmony_ci async [_mkdir] (dest) { 3601cb0ef41Sopenharmony_ci await this[_empty](dest) 3611cb0ef41Sopenharmony_ci return await fs.mkdir(dest, { recursive: true }) 3621cb0ef41Sopenharmony_ci } 3631cb0ef41Sopenharmony_ci 3641cb0ef41Sopenharmony_ci // extraction is always the same. the only difference is where 3651cb0ef41Sopenharmony_ci // the tarball comes from. 3661cb0ef41Sopenharmony_ci async extract (dest) { 3671cb0ef41Sopenharmony_ci await this[_mkdir](dest) 3681cb0ef41Sopenharmony_ci return this.tarballStream((tarball) => this[_extract](dest, tarball)) 3691cb0ef41Sopenharmony_ci } 3701cb0ef41Sopenharmony_ci 3711cb0ef41Sopenharmony_ci [_toFile] (dest) { 3721cb0ef41Sopenharmony_ci return this.tarballStream(str => new Promise((res, rej) => { 3731cb0ef41Sopenharmony_ci const writer = new fsm.WriteStream(dest) 3741cb0ef41Sopenharmony_ci str.on('error', er => writer.emit('error', er)) 3751cb0ef41Sopenharmony_ci writer.on('error', er => rej(er)) 3761cb0ef41Sopenharmony_ci writer.on('close', () => res({ 3771cb0ef41Sopenharmony_ci integrity: this.integrity && String(this.integrity), 3781cb0ef41Sopenharmony_ci resolved: this.resolved, 3791cb0ef41Sopenharmony_ci from: this.from, 3801cb0ef41Sopenharmony_ci })) 3811cb0ef41Sopenharmony_ci str.pipe(writer) 3821cb0ef41Sopenharmony_ci })) 3831cb0ef41Sopenharmony_ci } 3841cb0ef41Sopenharmony_ci 3851cb0ef41Sopenharmony_ci // don't use this[_mkdir] because we don't want to rimraf anything 3861cb0ef41Sopenharmony_ci async tarballFile (dest) { 3871cb0ef41Sopenharmony_ci const dir = dirname(dest) 3881cb0ef41Sopenharmony_ci await fs.mkdir(dir, { recursive: true }) 3891cb0ef41Sopenharmony_ci return this[_toFile](dest) 3901cb0ef41Sopenharmony_ci } 3911cb0ef41Sopenharmony_ci 3921cb0ef41Sopenharmony_ci [_extract] (dest, tarball) { 3931cb0ef41Sopenharmony_ci const extractor = tar.x(this[_tarxOptions]({ cwd: dest })) 3941cb0ef41Sopenharmony_ci const p = new Promise((resolve, reject) => { 3951cb0ef41Sopenharmony_ci extractor.on('end', () => { 3961cb0ef41Sopenharmony_ci resolve({ 3971cb0ef41Sopenharmony_ci resolved: this.resolved, 3981cb0ef41Sopenharmony_ci integrity: this.integrity && String(this.integrity), 3991cb0ef41Sopenharmony_ci from: this.from, 4001cb0ef41Sopenharmony_ci }) 4011cb0ef41Sopenharmony_ci }) 4021cb0ef41Sopenharmony_ci 4031cb0ef41Sopenharmony_ci extractor.on('error', er => { 4041cb0ef41Sopenharmony_ci log.warn('tar', er.message) 4051cb0ef41Sopenharmony_ci log.silly('tar', er) 4061cb0ef41Sopenharmony_ci reject(er) 4071cb0ef41Sopenharmony_ci }) 4081cb0ef41Sopenharmony_ci 4091cb0ef41Sopenharmony_ci tarball.on('error', er => reject(er)) 4101cb0ef41Sopenharmony_ci }) 4111cb0ef41Sopenharmony_ci 4121cb0ef41Sopenharmony_ci tarball.pipe(extractor) 4131cb0ef41Sopenharmony_ci return p 4141cb0ef41Sopenharmony_ci } 4151cb0ef41Sopenharmony_ci 4161cb0ef41Sopenharmony_ci // always ensure that entries are at least as permissive as our configured 4171cb0ef41Sopenharmony_ci // dmode/fmode, but never more permissive than the umask allows. 4181cb0ef41Sopenharmony_ci [_entryMode] (path, mode, type) { 4191cb0ef41Sopenharmony_ci const m = /Directory|GNUDumpDir/.test(type) ? this.dmode 4201cb0ef41Sopenharmony_ci : /File$/.test(type) ? this.fmode 4211cb0ef41Sopenharmony_ci : /* istanbul ignore next - should never happen in a pkg */ 0 4221cb0ef41Sopenharmony_ci 4231cb0ef41Sopenharmony_ci // make sure package bins are executable 4241cb0ef41Sopenharmony_ci const exe = isPackageBin(this.package, path) ? 0o111 : 0 4251cb0ef41Sopenharmony_ci // always ensure that files are read/writable by the owner 4261cb0ef41Sopenharmony_ci return ((mode | m) & ~this.umask) | exe | 0o600 4271cb0ef41Sopenharmony_ci } 4281cb0ef41Sopenharmony_ci 4291cb0ef41Sopenharmony_ci [_tarxOptions] ({ cwd }) { 4301cb0ef41Sopenharmony_ci const sawIgnores = new Set() 4311cb0ef41Sopenharmony_ci return { 4321cb0ef41Sopenharmony_ci cwd, 4331cb0ef41Sopenharmony_ci noChmod: true, 4341cb0ef41Sopenharmony_ci noMtime: true, 4351cb0ef41Sopenharmony_ci filter: (name, entry) => { 4361cb0ef41Sopenharmony_ci if (/Link$/.test(entry.type)) { 4371cb0ef41Sopenharmony_ci return false 4381cb0ef41Sopenharmony_ci } 4391cb0ef41Sopenharmony_ci entry.mode = this[_entryMode](entry.path, entry.mode, entry.type) 4401cb0ef41Sopenharmony_ci // this replicates the npm pack behavior where .gitignore files 4411cb0ef41Sopenharmony_ci // are treated like .npmignore files, but only if a .npmignore 4421cb0ef41Sopenharmony_ci // file is not present. 4431cb0ef41Sopenharmony_ci if (/File$/.test(entry.type)) { 4441cb0ef41Sopenharmony_ci const base = basename(entry.path) 4451cb0ef41Sopenharmony_ci if (base === '.npmignore') { 4461cb0ef41Sopenharmony_ci sawIgnores.add(entry.path) 4471cb0ef41Sopenharmony_ci } else if (base === '.gitignore' && !this.allowGitIgnore) { 4481cb0ef41Sopenharmony_ci // rename, but only if there's not already a .npmignore 4491cb0ef41Sopenharmony_ci const ni = entry.path.replace(/\.gitignore$/, '.npmignore') 4501cb0ef41Sopenharmony_ci if (sawIgnores.has(ni)) { 4511cb0ef41Sopenharmony_ci return false 4521cb0ef41Sopenharmony_ci } 4531cb0ef41Sopenharmony_ci entry.path = ni 4541cb0ef41Sopenharmony_ci } 4551cb0ef41Sopenharmony_ci return true 4561cb0ef41Sopenharmony_ci } 4571cb0ef41Sopenharmony_ci }, 4581cb0ef41Sopenharmony_ci strip: 1, 4591cb0ef41Sopenharmony_ci onwarn: /* istanbul ignore next - we can trust that tar logs */ 4601cb0ef41Sopenharmony_ci (code, msg, data) => { 4611cb0ef41Sopenharmony_ci log.warn('tar', code, msg) 4621cb0ef41Sopenharmony_ci log.silly('tar', code, msg, data) 4631cb0ef41Sopenharmony_ci }, 4641cb0ef41Sopenharmony_ci umask: this.umask, 4651cb0ef41Sopenharmony_ci // always ignore ownership info from tarball metadata 4661cb0ef41Sopenharmony_ci preserveOwner: false, 4671cb0ef41Sopenharmony_ci } 4681cb0ef41Sopenharmony_ci } 4691cb0ef41Sopenharmony_ci} 4701cb0ef41Sopenharmony_ci 4711cb0ef41Sopenharmony_cimodule.exports = FetcherBase 4721cb0ef41Sopenharmony_ci 4731cb0ef41Sopenharmony_ci// Child classes 4741cb0ef41Sopenharmony_ciconst GitFetcher = require('./git.js') 4751cb0ef41Sopenharmony_ciconst RegistryFetcher = require('./registry.js') 4761cb0ef41Sopenharmony_ciconst FileFetcher = require('./file.js') 4771cb0ef41Sopenharmony_ciconst DirFetcher = require('./dir.js') 4781cb0ef41Sopenharmony_ciconst RemoteFetcher = require('./remote.js') 4791cb0ef41Sopenharmony_ci 4801cb0ef41Sopenharmony_ci// Get an appropriate fetcher object from a spec and options 4811cb0ef41Sopenharmony_ciFetcherBase.get = (rawSpec, opts = {}) => { 4821cb0ef41Sopenharmony_ci const spec = npa(rawSpec, opts.where) 4831cb0ef41Sopenharmony_ci switch (spec.type) { 4841cb0ef41Sopenharmony_ci case 'git': 4851cb0ef41Sopenharmony_ci return new GitFetcher(spec, opts) 4861cb0ef41Sopenharmony_ci 4871cb0ef41Sopenharmony_ci case 'remote': 4881cb0ef41Sopenharmony_ci return new RemoteFetcher(spec, opts) 4891cb0ef41Sopenharmony_ci 4901cb0ef41Sopenharmony_ci case 'version': 4911cb0ef41Sopenharmony_ci case 'range': 4921cb0ef41Sopenharmony_ci case 'tag': 4931cb0ef41Sopenharmony_ci case 'alias': 4941cb0ef41Sopenharmony_ci return new RegistryFetcher(spec.subSpec || spec, opts) 4951cb0ef41Sopenharmony_ci 4961cb0ef41Sopenharmony_ci case 'file': 4971cb0ef41Sopenharmony_ci return new FileFetcher(spec, opts) 4981cb0ef41Sopenharmony_ci 4991cb0ef41Sopenharmony_ci case 'directory': 5001cb0ef41Sopenharmony_ci return new DirFetcher(spec, opts) 5011cb0ef41Sopenharmony_ci 5021cb0ef41Sopenharmony_ci default: 5031cb0ef41Sopenharmony_ci throw new TypeError('Unknown spec type: ' + spec.type) 5041cb0ef41Sopenharmony_ci } 5051cb0ef41Sopenharmony_ci} 506