diff options
Diffstat (limited to 'deps/npm/node_modules/pacote/lib/util/git.js')
-rw-r--r-- | deps/npm/node_modules/pacote/lib/util/git.js | 155 |
1 files changed, 100 insertions, 55 deletions
diff --git a/deps/npm/node_modules/pacote/lib/util/git.js b/deps/npm/node_modules/pacote/lib/util/git.js index a6162ceeba..b854208ef9 100644 --- a/deps/npm/node_modules/pacote/lib/util/git.js +++ b/deps/npm/node_modules/pacote/lib/util/git.js @@ -12,8 +12,10 @@ const optCheck = require('./opt-check') const osenv = require('osenv') const path = require('path') const pinflight = require('promise-inflight') +const promiseRetry = require('promise-retry') const uniqueFilename = require('unique-filename') const which = BB.promisify(require('which')) +const semver = require('semver') const GOOD_ENV_VARS = new Set([ 'GIT_ASKPASS', @@ -25,6 +27,23 @@ const GOOD_ENV_VARS = new Set([ 'GIT_SSL_NO_VERIFY' ]) +const GIT_TRANSIENT_ERRORS = [ + 'remote error: Internal Server Error', + 'The remote end hung up unexpectedly', + 'Connection timed out', + 'Operation timed out', + 'Failed to connect to .* Timed out', + 'Connection reset by peer', + 'SSL_ERROR_SYSCALL', + 'The requested URL returned error: 503' +].join('|') + +const GIT_TRANSIENT_ERROR_RE = new RegExp(GIT_TRANSIENT_ERRORS) + +function shouldRetry (error) { + return GIT_TRANSIENT_ERROR_RE.test(error) +} + const GIT_ = 'GIT_' let GITENV function gitEnv () { @@ -113,57 +132,51 @@ function revs (repo, opts) { return pinflight(`ls-remote:${repo}`, () => { return spawnGit(['ls-remote', '-h', '-t', repo], { env: gitEnv() - }, opts).then(child => { - let stdout = '' - let stderr = '' - child.stdout.on('data', d => { stdout += d }) - child.stderr.on('data', d => { stderr += d }) - return finished(child).catch(err => { - err.message = `Error while executing:\n${GITPATH} ls-remote -h -t ${repo}\n\n${stderr}\n${err.message}` - throw err - }).then(() => { - return stdout.split('\n').reduce((revs, line) => { - const split = line.split(/\s+/, 2) - if (split.length < 2) { return revs } - const sha = split[0].trim() - const ref = split[1].trim().match(/(?:refs\/[^/]+\/)?(.*)/)[1] - if (!ref) { return revs } // ??? - if (ref.endsWith(CARET_BRACES)) { return revs } // refs/tags/x^{} crap - const type = refType(line) - const doc = {sha, ref, type} - - revs.refs[ref] = doc - // We can check out shallow clones on specific SHAs if we have a ref - if (revs.shas[sha]) { - revs.shas[sha].push(ref) - } else { - revs.shas[sha] = [ref] - } + }, opts).then((stdout) => { + return stdout.split('\n').reduce((revs, line) => { + const split = line.split(/\s+/, 2) + if (split.length < 2) { return revs } + const sha = split[0].trim() + const ref = split[1].trim().match(/(?:refs\/[^/]+\/)?(.*)/)[1] + if (!ref) { return revs } // ??? + if (ref.endsWith(CARET_BRACES)) { return revs } // refs/tags/x^{} crap + const type = refType(line) + const doc = {sha, ref, type} - if (type === 'tag') { - const match = ref.match(/v?(\d+\.\d+\.\d+)$/) - if (match) { - revs.versions[match[1]] = doc - } - } + revs.refs[ref] = doc + // We can check out shallow clones on specific SHAs if we have a ref + if (revs.shas[sha]) { + revs.shas[sha].push(ref) + } else { + revs.shas[sha] = [ref] + } - return revs - }, {versions: {}, 'dist-tags': {}, refs: {}, shas: {}}) - }).then(revs => { - if (revs.refs.HEAD) { - const HEAD = revs.refs.HEAD - Object.keys(revs.versions).forEach(v => { - if (v.sha === HEAD.sha) { - revs['dist-tags'].HEAD = v - if (!revs.refs.latest) { - revs['dist-tags'].latest = revs.refs.HEAD - } - } - }) + if (type === 'tag') { + const match = ref.match(/v?(\d+\.\d+\.\d+(?:[-+].+)?)$/) + if (match && semver.valid(match[1], true)) { + revs.versions[semver.clean(match[1], true)] = doc + } } - REVS.set(repo, revs) + return revs - }) + }, {versions: {}, 'dist-tags': {}, refs: {}, shas: {}}) + }, err => { + err.message = `Error while executing:\n${GITPATH} ls-remote -h -t ${repo}\n\n${err.stderr}\n${err.message}` + throw err + }).then(revs => { + if (revs.refs.HEAD) { + const HEAD = revs.refs.HEAD + Object.keys(revs.versions).forEach(v => { + if (v.sha === HEAD.sha) { + revs['dist-tags'].HEAD = v + if (!revs.refs.latest) { + revs['dist-tags'].latest = revs.refs.HEAD + } + } + }) + } + REVS.set(repo, revs) + return revs }) }) } @@ -172,7 +185,18 @@ module.exports._exec = execGit function execGit (gitArgs, gitOpts, opts) { opts = optCheck(opts) return checkGit().then(gitPath => { - return execFileAsync(gitPath, gitArgs, mkOpts(gitOpts, opts)) + return promiseRetry((retry, number) => { + if (number !== 1) { + opts.log.silly('pacote', 'Retrying git command: ' + gitArgs.join(' ') + ' attempt # ' + number) + } + return execFileAsync(gitPath, gitArgs, mkOpts(gitOpts, opts)).catch((err) => { + if (shouldRetry(err)) { + retry(err) + } else { + throw err + } + }) + }, opts.retry) }) } @@ -180,7 +204,28 @@ module.exports._spawn = spawnGit function spawnGit (gitArgs, gitOpts, opts) { opts = optCheck(opts) return checkGit().then(gitPath => { - return cp.spawn(gitPath, gitArgs, mkOpts(gitOpts, opts)) + return promiseRetry((retry, number) => { + if (number !== 1) { + opts.log.silly('pacote', 'Retrying git command: ' + gitArgs.join(' ') + ' attempt # ' + number) + } + const child = cp.spawn(gitPath, gitArgs, mkOpts(gitOpts, opts)) + + let stdout = '' + let stderr = '' + child.stdout.on('data', d => { stdout += d }) + child.stderr.on('data', d => { stderr += d }) + + return finished(child).catch(err => { + if (shouldRetry(stderr)) { + retry(err) + } else { + err.stderr = stderr + throw err + } + }).then(() => { + return stdout + }) + }, opts.retry) }) } @@ -213,10 +258,10 @@ const REFS_HEADS = 'refs/heads/' const HEAD = 'HEAD' function refType (ref) { return ref.indexOf(REFS_TAGS) !== -1 - ? 'tag' - : ref.indexOf(REFS_HEADS) !== -1 - ? 'branch' - : ref.endsWith(HEAD) - ? 'head' - : 'other' + ? 'tag' + : ref.indexOf(REFS_HEADS) !== -1 + ? 'branch' + : ref.endsWith(HEAD) + ? 'head' + : 'other' } |