]> gerrit.simantics Code Review - simantics/district.git/blobdiff - org.simantics.maps.server/node/node-v4.8.0-win-x64/node_modules/npm/lib/cache/add-remote-git.js
Adding integrated tile server
[simantics/district.git] / org.simantics.maps.server / node / node-v4.8.0-win-x64 / node_modules / npm / lib / cache / add-remote-git.js
diff --git a/org.simantics.maps.server/node/node-v4.8.0-win-x64/node_modules/npm/lib/cache/add-remote-git.js b/org.simantics.maps.server/node/node-v4.8.0-win-x64/node_modules/npm/lib/cache/add-remote-git.js
new file mode 100644 (file)
index 0000000..112e841
--- /dev/null
@@ -0,0 +1,481 @@
+var assert = require('assert')
+var crypto = require('crypto')
+var fs = require('graceful-fs')
+var path = require('path')
+var url = require('url')
+
+var chownr = require('chownr')
+var dezalgo = require('dezalgo')
+var hostedFromURL = require('hosted-git-info').fromUrl
+var inflight = require('inflight')
+var log = require('npmlog')
+var mkdir = require('mkdirp')
+var normalizeGitUrl = require('normalize-git-url')
+var npa = require('npm-package-arg')
+var realizePackageSpecifier = require('realize-package-specifier')
+
+var addLocal = require('./add-local.js')
+var correctMkdir = require('../utils/correct-mkdir.js')
+var git = require('../utils/git.js')
+var npm = require('../npm.js')
+var rm = require('../utils/gently-rm.js')
+
+var remotes = path.resolve(npm.config.get('cache'), '_git-remotes')
+var templates = path.join(remotes, '_templates')
+
+var VALID_VARIABLES = [
+  'GIT_ASKPASS',
+  'GIT_EXEC_PATH',
+  'GIT_PROXY_COMMAND',
+  'GIT_SSH',
+  'GIT_SSH_COMMAND',
+  'GIT_SSL_CAINFO',
+  'GIT_SSL_NO_VERIFY'
+]
+
+module.exports = addRemoteGit
+function addRemoteGit (uri, _cb) {
+  assert(typeof uri === 'string', 'must have git URL')
+  assert(typeof _cb === 'function', 'must have callback')
+  var cb = dezalgo(_cb)
+
+  log.verbose('addRemoteGit', 'caching', uri)
+
+  // the URL comes in exactly as it was passed on the command line, or as
+  // normalized by normalize-package-data / read-package-json / read-installed,
+  // so figure out what to do with it using hosted-git-info
+  var parsed = hostedFromURL(uri)
+  if (parsed) {
+    // normalize GitHub syntax to org/repo (for now)
+    var from
+    if (parsed.type === 'github' && parsed.default === 'shortcut') {
+      from = parsed.path()
+    } else {
+      from = parsed.toString()
+    }
+
+    log.verbose('addRemoteGit', from, 'is a repository hosted by', parsed.type)
+
+    // prefer explicit URLs to pushing everything through shortcuts
+    if (parsed.default !== 'shortcut') {
+      return tryClone(from, parsed.toString(), false, cb)
+    }
+
+    // try git:, then git+ssh:, then git+https: before failing
+    tryGitProto(from, parsed, cb)
+  } else {
+    // verify that this is a Git URL before continuing
+    parsed = npa(uri)
+    if (parsed.type !== 'git') {
+      return cb(new Error(uri + 'is not a Git or GitHub URL'))
+    }
+
+    tryClone(parsed.rawSpec, uri, false, cb)
+  }
+}
+
+function tryGitProto (from, hostedInfo, cb) {
+  var gitURL = hostedInfo.git()
+  if (!gitURL) return trySSH(from, hostedInfo, cb)
+
+  log.silly('tryGitProto', 'attempting to clone', gitURL)
+  tryClone(from, gitURL, true, function (er) {
+    if (er) return tryHTTPS(from, hostedInfo, cb)
+
+    cb.apply(this, arguments)
+  })
+}
+
+function tryHTTPS (from, hostedInfo, cb) {
+  var httpsURL = hostedInfo.https()
+  if (!httpsURL) {
+    return cb(new Error(from + ' can not be cloned via Git, SSH, or HTTPS'))
+  }
+
+  log.silly('tryHTTPS', 'attempting to clone', httpsURL)
+  tryClone(from, httpsURL, true, function (er) {
+    if (er) return trySSH(from, hostedInfo, cb)
+
+    cb.apply(this, arguments)
+  })
+}
+
+function trySSH (from, hostedInfo, cb) {
+  var sshURL = hostedInfo.ssh()
+  if (!sshURL) return tryHTTPS(from, hostedInfo, cb)
+
+  log.silly('trySSH', 'attempting to clone', sshURL)
+  tryClone(from, sshURL, false, cb)
+}
+
+function tryClone (from, combinedURL, silent, cb) {
+  log.silly('tryClone', 'cloning', from, 'via', combinedURL)
+
+  var normalized = normalizeGitUrl(combinedURL)
+  var cloneURL = normalized.url
+  var treeish = normalized.branch
+
+  // ensure that similarly-named remotes don't collide
+  var repoID = cloneURL.replace(/[^a-zA-Z0-9]+/g, '-') + '-' +
+    crypto.createHash('sha1').update(combinedURL).digest('hex').slice(0, 8)
+  var cachedRemote = path.join(remotes, repoID)
+
+  cb = inflight(repoID, cb)
+  if (!cb) {
+    return log.verbose('tryClone', repoID, 'already in flight; waiting')
+  }
+  log.verbose('tryClone', repoID, 'not in flight; caching')
+
+  // initialize the remotes cache with the correct perms
+  getGitDir(function (er) {
+    if (er) return cb(er)
+    fs.stat(cachedRemote, function (er, s) {
+      if (er) return mirrorRemote(from, cloneURL, treeish, cachedRemote, silent, finish)
+      if (!s.isDirectory()) return resetRemote(from, cloneURL, treeish, cachedRemote, finish)
+
+      validateExistingRemote(from, cloneURL, treeish, cachedRemote, finish)
+    })
+
+    // always set permissions on the cached remote
+    function finish (er, data) {
+      if (er) return cb(er, data)
+      addModeRecursive(cachedRemote, npm.modes.file, function (er) {
+        return cb(er, data)
+      })
+    }
+  })
+}
+
+// don't try too hard to hold on to a remote
+function resetRemote (from, cloneURL, treeish, cachedRemote, cb) {
+  log.info('resetRemote', 'resetting', cachedRemote, 'for', from)
+  rm(cachedRemote, function (er) {
+    if (er) return cb(er)
+    mirrorRemote(from, cloneURL, treeish, cachedRemote, false, cb)
+  })
+}
+
+// reuse a cached remote when possible, but nuke it if it's in an
+// inconsistent state
+function validateExistingRemote (from, cloneURL, treeish, cachedRemote, cb) {
+  git.whichAndExec(
+    ['config', '--get', 'remote.origin.url'],
+    { cwd: cachedRemote, env: gitEnv() },
+    function (er, stdout, stderr) {
+      var originURL
+      if (stdout) {
+        originURL = stdout.trim()
+        log.silly('validateExistingRemote', from, 'remote.origin.url:', originURL)
+      }
+
+      if (stderr) stderr = stderr.trim()
+      if (stderr || er) {
+        log.warn('addRemoteGit', from, 'resetting remote', cachedRemote, 'because of error:', stderr || er)
+        return resetRemote(from, cloneURL, treeish, cachedRemote, cb)
+      } else if (cloneURL !== originURL) {
+        log.warn(
+          'addRemoteGit',
+          from,
+          'pre-existing cached repo', cachedRemote, 'points to', originURL, 'and not', cloneURL
+        )
+        return resetRemote(from, cloneURL, treeish, cachedRemote, cb)
+      }
+
+      log.verbose('validateExistingRemote', from, 'is updating existing cached remote', cachedRemote)
+      updateRemote(from, cloneURL, treeish, cachedRemote, cb)
+    }
+  )
+}
+
+// make a complete bare mirror of the remote repo
+// NOTE: npm uses a blank template directory to prevent weird inconsistencies
+// https://github.com/npm/npm/issues/5867
+function mirrorRemote (from, cloneURL, treeish, cachedRemote, silent, cb) {
+  mkdir(cachedRemote, function (er) {
+    if (er) return cb(er)
+
+    var args = [
+      'clone',
+      '--template=' + templates,
+      '--mirror',
+      cloneURL, cachedRemote
+    ]
+    git.whichAndExec(
+      ['clone', '--template=' + templates, '--mirror', cloneURL, cachedRemote],
+      { cwd: cachedRemote, env: gitEnv() },
+      function (er, stdout, stderr) {
+        if (er) {
+          var combined = (stdout + '\n' + stderr).trim()
+          var command = 'git ' + args.join(' ') + ':'
+          if (silent) {
+            log.verbose(command, combined)
+          } else {
+            log.error(command, combined)
+          }
+          return cb(er)
+        }
+        log.verbose('mirrorRemote', from, 'git clone ' + cloneURL, stdout.trim())
+        setPermissions(from, cloneURL, treeish, cachedRemote, cb)
+      }
+    )
+  })
+}
+
+function setPermissions (from, cloneURL, treeish, cachedRemote, cb) {
+  if (process.platform === 'win32') {
+    log.verbose('setPermissions', from, 'skipping chownr on Windows')
+    resolveHead(from, cloneURL, treeish, cachedRemote, cb)
+  } else {
+    getGitDir(function (er, cs) {
+      if (er) {
+        log.error('setPermissions', from, 'could not get cache stat')
+        return cb(er)
+      }
+
+      chownr(cachedRemote, cs.uid, cs.gid, function (er) {
+        if (er) {
+          log.error(
+            'setPermissions',
+            'Failed to change git repository ownership under npm cache for',
+            cachedRemote
+          )
+          return cb(er)
+        }
+
+        log.verbose('setPermissions', from, 'set permissions on', cachedRemote)
+        resolveHead(from, cloneURL, treeish, cachedRemote, cb)
+      })
+    })
+  }
+}
+
+// always fetch the origin, even right after mirroring, because this way
+// permissions will get set correctly
+function updateRemote (from, cloneURL, treeish, cachedRemote, cb) {
+  git.whichAndExec(
+    ['fetch', '-a', 'origin'],
+    { cwd: cachedRemote, env: gitEnv() },
+    function (er, stdout, stderr) {
+      if (er) {
+        var combined = (stdout + '\n' + stderr).trim()
+        log.error('git fetch -a origin (' + cloneURL + ')', combined)
+        return cb(er)
+      }
+      log.verbose('updateRemote', 'git fetch -a origin (' + cloneURL + ')', stdout.trim())
+
+      setPermissions(from, cloneURL, treeish, cachedRemote, cb)
+    }
+  )
+}
+
+// branches and tags are both symbolic labels that can be attached to different
+// commits, so resolve the commit-ish to the current actual treeish the label
+// corresponds to
+//
+// important for shrinkwrap
+function resolveHead (from, cloneURL, treeish, cachedRemote, cb) {
+  log.verbose('resolveHead', from, 'original treeish:', treeish)
+  var args = ['rev-list', '-n1', treeish]
+  git.whichAndExec(
+    args,
+    { cwd: cachedRemote, env: gitEnv() },
+    function (er, stdout, stderr) {
+      if (er) {
+        log.error('git ' + args.join(' ') + ':', stderr)
+        return cb(er)
+      }
+
+      var resolvedTreeish = stdout.trim()
+      log.silly('resolveHead', from, 'resolved treeish:', resolvedTreeish)
+
+      var resolvedURL = getResolved(cloneURL, resolvedTreeish)
+      if (!resolvedURL) {
+        return cb(new Error(
+          'unable to clone ' + from + ' because git clone string ' +
+            cloneURL + ' is in a form npm can\'t handle'
+        ))
+      }
+      log.verbose('resolveHead', from, 'resolved Git URL:', resolvedURL)
+
+      // generate a unique filename
+      var tmpdir = path.join(
+        npm.tmp,
+        'git-cache-' + crypto.pseudoRandomBytes(6).toString('hex'),
+        resolvedTreeish
+      )
+      log.silly('resolveHead', 'Git working directory:', tmpdir)
+
+      mkdir(tmpdir, function (er) {
+        if (er) return cb(er)
+
+        cloneResolved(from, resolvedURL, resolvedTreeish, cachedRemote, tmpdir, cb)
+      })
+    }
+  )
+}
+
+// make a clone from the mirrored cache so we have a temporary directory in
+// which we can check out the resolved treeish
+function cloneResolved (from, resolvedURL, resolvedTreeish, cachedRemote, tmpdir, cb) {
+  var args = ['clone', cachedRemote, tmpdir]
+  git.whichAndExec(
+    args,
+    { cwd: cachedRemote, env: gitEnv() },
+    function (er, stdout, stderr) {
+      stdout = (stdout + '\n' + stderr).trim()
+      if (er) {
+        log.error('git ' + args.join(' ') + ':', stderr)
+        return cb(er)
+      }
+      log.verbose('cloneResolved', from, 'clone', stdout)
+
+      checkoutTreeish(from, resolvedURL, resolvedTreeish, tmpdir, cb)
+    }
+  )
+}
+
+// there is no safe way to do a one-step clone to a treeish that isn't
+// guaranteed to be a branch, so explicitly check out the treeish once it's
+// cloned
+function checkoutTreeish (from, resolvedURL, resolvedTreeish, tmpdir, cb) {
+  var args = ['checkout', resolvedTreeish]
+  git.whichAndExec(
+    args,
+    { cwd: tmpdir, env: gitEnv() },
+    function (er, stdout, stderr) {
+      stdout = (stdout + '\n' + stderr).trim()
+      if (er) {
+        log.error('git ' + args.join(' ') + ':', stderr)
+        return cb(er)
+      }
+      log.verbose('checkoutTreeish', from, 'checkout', stdout)
+
+      // convince addLocal that the checkout is a local dependency
+      realizePackageSpecifier(tmpdir, function (er, spec) {
+        if (er) {
+          log.error('addRemoteGit', 'Failed to map', tmpdir, 'to a package specifier')
+          return cb(er)
+        }
+
+        // ensure pack logic is applied
+        // https://github.com/npm/npm/issues/6400
+        addLocal(spec, null, function (er, data) {
+          if (data) {
+            if (npm.config.get('save-exact')) {
+              log.verbose('addRemoteGit', 'data._from:', resolvedURL, '(save-exact)')
+              data._from = resolvedURL
+            } else {
+              log.verbose('addRemoteGit', 'data._from:', from)
+              data._from = from
+            }
+
+            log.verbose('addRemoteGit', 'data._resolved:', resolvedURL)
+            data._resolved = resolvedURL
+          }
+
+          cb(er, data)
+        })
+      })
+    }
+  )
+}
+
+function getGitDir (cb) {
+  correctMkdir(remotes, function (er, stats) {
+    if (er) return cb(er)
+
+    // We don't need global templates when cloning. Use an empty directory for
+    // the templates, creating it (and setting its permissions) if necessary.
+    mkdir(templates, function (er) {
+      if (er) return cb(er)
+
+      // Ensure that both the template and remotes directories have the correct
+      // permissions.
+      fs.chown(templates, stats.uid, stats.gid, function (er) {
+        cb(er, stats)
+      })
+    })
+  })
+}
+
+var gitEnv_
+function gitEnv () {
+  // git responds to env vars in some weird ways in post-receive hooks
+  // so don't carry those along.
+  if (gitEnv_) return gitEnv_
+
+  // allow users to override npm's insistence on not prompting for
+  // passphrases, but default to just failing when credentials
+  // aren't available
+  gitEnv_ = { GIT_ASKPASS: 'echo' }
+
+  for (var k in process.env) {
+    if (!~VALID_VARIABLES.indexOf(k) && k.match(/^GIT/)) continue
+    gitEnv_[k] = process.env[k]
+  }
+  return gitEnv_
+}
+
+addRemoteGit.getResolved = getResolved
+function getResolved (uri, treeish) {
+  // normalize hosted-git-info clone URLs back into regular URLs
+  // this will only work on URLs that hosted-git-info recognizes
+  // https://github.com/npm/npm/issues/7961
+  var rehydrated = hostedFromURL(uri)
+  if (rehydrated) uri = rehydrated.toString()
+
+  var parsed = url.parse(uri)
+
+  // Checks for known protocols:
+  // http:, https:, ssh:, and git:, with optional git+ prefix.
+  if (!parsed.protocol ||
+      !parsed.protocol.match(/^(((git\+)?(https?|ssh))|git|file):$/)) {
+    uri = 'git+ssh://' + uri
+  }
+
+  if (!/^git[+:]/.test(uri)) {
+    uri = 'git+' + uri
+  }
+
+  // Not all URIs are actually URIs, so use regex for the treeish.
+  return uri.replace(/(?:#.*)?$/, '#' + treeish)
+}
+
+// similar to chmodr except it add permissions rather than overwriting them
+// adapted from https://github.com/isaacs/chmodr/blob/master/chmodr.js
+function addModeRecursive (cachedRemote, mode, cb) {
+  fs.readdir(cachedRemote, function (er, children) {
+    // Any error other than ENOTDIR means it's not readable, or doesn't exist.
+    // Give up.
+    if (er && er.code !== 'ENOTDIR') return cb(er)
+    if (er || !children.length) return addMode(cachedRemote, mode, cb)
+
+    var len = children.length
+    var errState = null
+    children.forEach(function (child) {
+      addModeRecursive(path.resolve(cachedRemote, child), mode, then)
+    })
+
+    function then (er) {
+      if (errState) return undefined
+      if (er) return cb(errState = er)
+      if (--len === 0) return addMode(cachedRemote, dirMode(mode), cb)
+    }
+  })
+}
+
+function addMode (cachedRemote, mode, cb) {
+  fs.stat(cachedRemote, function (er, stats) {
+    if (er) return cb(er)
+    mode = stats.mode | mode
+    fs.chmod(cachedRemote, mode, cb)
+  })
+}
+
+// taken from https://github.com/isaacs/chmodr/blob/master/chmodr.js
+function dirMode (mode) {
+  if (mode & parseInt('0400', 8)) mode |= parseInt('0100', 8)
+  if (mode & parseInt('040', 8)) mode |= parseInt('010', 8)
+  if (mode & parseInt('04', 8)) mode |= parseInt('01', 8)
+  return mode
+}