summaryrefslogtreecommitdiff
path: root/deps/npm/node_modules/pacote/lib/util/git.js
diff options
context:
space:
mode:
Diffstat (limited to 'deps/npm/node_modules/pacote/lib/util/git.js')
-rw-r--r--deps/npm/node_modules/pacote/lib/util/git.js155
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'
}