Skip to content
This repository has been archived by the owner on Jul 3, 2019. It is now read-only.

Commit

Permalink
feat(extract): add 'safe-mode' via extractOverwrite option
Browse files Browse the repository at this point in the history
closes #73
  • Loading branch information
aem committed Apr 29, 2017
1 parent d67ecf1 commit eecaa9d
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 41 deletions.
106 changes: 65 additions & 41 deletions extract.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,56 +9,61 @@ const pipe = BB.promisify(require('mississippi').pipe)
const optCheck = require('./lib/util/opt-check')
const retry = require('promise-retry')
const rimraf = BB.promisify(require('rimraf'))
const readdirAsync = BB.promisify(require('fs').readdir)

module.exports = extract
function extract (spec, dest, opts) {
function extract(spec, dest, opts) {
opts = optCheck(opts)
spec = typeof spec === 'string' ? npa(spec, opts.where) : spec
const startTime = Date.now()
if (opts.integrity && opts.cache && !opts.preferOnline) {
opts.log.silly('pacote', 'trying', spec.name, 'by hash:', opts.integrity.toString())
return extractByDigest(
startTime, spec, dest, opts
).catch(err => {
if (err.code === 'ENOENT') {
opts.log.silly('pacote', `data for ${opts.integrity} not present. Using manifest.`)
return extractByManifest(startTime, spec, dest, opts)
}
checkOverwrite(opts.extractOverwrite, spec, dest)
.then(() => {
if (opts.integrity && opts.cache && !opts.preferOnline) {
opts.log.silly('pacote', 'trying', spec.name, 'by hash:', opts.integrity.toString())
return extractByDigest(
startTime, spec, dest, opts
).catch(err => {
if (err.code === 'ENOENT') {
opts.log.silly('pacote', `data for ${opts.integrity} not present. Using manifest.`)
return extractByManifest(startTime, spec, dest, opts)
}

if (err.code === 'EINTEGRITY' || err.code === 'Z_DATA_ERROR') {
opts.log.warn('pacote', `cached data for ${spec} (${opts.integrity}) seems to be corrupted. Refreshing cache.`)
}
return cleanUpCached(
dest, opts.cache, opts.integrity, opts
).then(() => {
return extractByManifest(startTime, spec, dest, opts)
})
})
} else {
opts.log.silly('pacote', 'no tarball hash provided for', spec.name, '- extracting by manifest')
return retry((tryAgain, attemptNum) => {
return extractByManifest(
startTime, spec, dest, opts
).catch(err => {
// Retry once if we have a cache, to clear up any weird conditions.
// Don't retry network errors, though -- make-fetch-happen has already
// taken care of making sure we're all set on that front.
if (opts.cache && !err.code.match(/^E\d{3}$/)) {
if (err.code === 'EINTEGRITY' || err.code === 'Z_DATA_ERROR') {
opts.log.warn('pacote', `tarball data for ${spec} (${opts.integrity}) seems to be corrupted. Trying one more time.`)
opts.log.warn('pacote', `cached data for ${spec} (${opts.integrity}) seems to be corrupted. Refreshing cache.`)
}
return cleanUpCached(
dest, opts.cache, err.sri, opts
).then(() => tryAgain(err))
} else {
throw err
}
})
}, {retries: 1})
}
dest, opts.cache, opts.integrity, opts
).then(() => {
return extractByManifest(startTime, spec, dest, opts)
})
})
} else {
opts.log.silly('pacote', 'no tarball hash provided for', spec.name, '- extracting by manifest')
return retry((tryAgain, attemptNum) => {
return extractByManifest(
startTime, spec, dest, opts
).catch(err => {
// Retry once if we have a cache, to clear up any weird conditions.
// Don't retry network errors, though -- make-fetch-happen has already
// taken care of making sure we're all set on that front.
if (opts.cache && !err.code.match(/^E\d{3}$/)) {
if (err.code === 'EINTEGRITY' || err.code === 'Z_DATA_ERROR') {
opts.log.warn('pacote', `tarball data for ${spec} (${opts.integrity}) seems to be corrupted. Trying one more time.`)
}
return cleanUpCached(
dest, opts.cache, err.sri, opts
).then(() => tryAgain(err))
} else {
throw err
}
})
}, { retries: 1 })
}
})

}

function extractByDigest (start, spec, dest, opts) {
function extractByDigest(start, spec, dest, opts) {
const xtractor = extractStream(dest, opts)
const cached = cacache.get.stream.byDigest(opts.cache, opts.integrity, opts)
return pipe(cached, xtractor).then(() => {
Expand All @@ -67,7 +72,7 @@ function extractByDigest (start, spec, dest, opts) {
}

let fetch
function extractByManifest (start, spec, dest, opts) {
function extractByManifest(start, spec, dest, opts) {
const xtractor = extractStream(dest, opts)
return BB.resolve(null).then(() => {
if (!fetch) {
Expand All @@ -79,9 +84,28 @@ function extractByManifest (start, spec, dest, opts) {
})
}

function cleanUpCached (dest, cachePath, integrity, opts) {
function cleanUpCached(dest, cachePath, integrity, opts) {
return BB.join(
rimraf(dest),
cacache.rm.content(cachePath, integrity, opts)
)
}

function checkOverwrite(extractOverwrite, spec, dest) {
return new BB((resolve, reject) => {
if (!extractOverwrite) {
readdirAsync(dest)
.then((dir) => {
const err = new Error(`Attempted to extract ${spec} to non-empty directory ${dest}. Use the extractOverwrite option to override.`)
err.target = dest
err.code = 'EBADDIR'
reject(err)
})
.catch({ code: 'ENOENT' }, () => {
resolve()
})
} else {
resolve()
}
})
}
1 change: 1 addition & 0 deletions lib/util/opt-check.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ function PacoteOptions (opts) {
this.projectScope = opts.projectScope
this.fullMetadata = opts.fullMetadata
this.alwaysAuth = opts.alwaysAuth
this.extractOverwrite = opts.extractOverwrite

this.dirPacker = opts.dirPacker || null

Expand Down

0 comments on commit eecaa9d

Please sign in to comment.