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 | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/deps/npm/node_modules/pacote/lib/util/git.js b/deps/npm/node_modules/pacote/lib/util/git.js new file mode 100644 index 0000000000..dc189b5418 --- /dev/null +++ b/deps/npm/node_modules/pacote/lib/util/git.js @@ -0,0 +1,207 @@ +'use strict' + +const BB = require('bluebird') + +const cp = require('child_process') +const execFileAsync = BB.promisify(cp.execFile, { + multiArgs: true +}) +const finished = BB.promisify(require('mississippi').finished) +const LRU = require('lru-cache') +const optCheck = require('./opt-check') +const osenv = require('osenv') +const path = require('path') +const pinflight = require('promise-inflight') +const uniqueFilename = require('unique-filename') +const which = BB.promisify(require('which')) + +const GOOD_ENV_VARS = new Set([ + 'GIT_ASKPASS', + 'GIT_EXEC_PATH', + 'GIT_PROXY_COMMAND', + 'GIT_SSH', + 'GIT_SSH_COMMAND', + 'GIT_SSL_CAINFO', + 'GIT_SSL_NO_VERIFY' +]) + +let GITENV +function gitEnv () { + if (GITENV) { return GITENV } + const tmpDir = path.join(osenv.tmpdir(), 'pacote-git-template-tmp') + const tmpName = uniqueFilename(tmpDir, 'git-clone') + GITENV = { + GIT_ASKPASS: 'echo', + GIT_TEMPLATE_DIR: tmpName + } + Object.keys(process.env).forEach(k => { + if (GOOD_ENV_VARS.has(k) || !k.match(/^GIT_/)) { + GITENV[k] = process.env[k] + } + }) + return GITENV +} + +let GITPATH +try { + GITPATH = which.sync('git') +} catch (e) {} + +module.exports.clone = fullClone +function fullClone (repo, committish, target, opts) { + opts = optCheck(opts) + const gitArgs = ['clone', '-q', repo, target] + if (process.platform === 'win32') { + gitArgs.push('--config', 'core.longpaths=true') + } + return execGit(gitArgs, { + cwd: path.dirname(target) + }, opts).then(() => { + return execGit(['checkout', committish], { + cwd: target + }) + }).then(() => { + return updateSubmodules(target, opts) + }).then(() => headSha(target, opts)) +} + +module.exports.shallow = shallowClone +function shallowClone (repo, branch, target, opts) { + opts = optCheck(opts) + const gitArgs = ['clone', '--depth=1', '-q', '-b', branch, repo, target] + if (process.platform === 'win32') { + gitArgs.push('--config', 'core.longpaths=true') + } + return execGit(gitArgs, { + cwd: target + }, opts).then(() => { + return updateSubmodules(target, opts) + }).then(() => headSha(target, opts)) +} + +function updateSubmodules (localRepo, opts) { + const gitArgs = ['submodule', 'update', '-q', '--init', '--recursive'] + return execGit(gitArgs, { + cwd: localRepo + }, opts) +} + +function headSha (repo, opts) { + opts = optCheck(opts) + return execGit(['rev-parse', '--revs-only', 'HEAD'], {cwd: repo}, opts).spread(stdout => { + return stdout.trim() + }) +} + +const REVS = new LRU({ + max: 100, + maxAge: 5 * 60 * 1000 +}) +module.exports.revs = revs +function revs (repo, opts) { + opts = optCheck(opts) + const cached = REVS.get(repo) + if (cached) { + return BB.resolve(cached) + } + return pinflight(`ls-remote:${repo}`, () => { + return spawnGit(['ls-remote', repo, '-t', '-h', '*'], { + env: gitEnv() + }, opts).then(child => { + let stdout = '' + child.stdout.on('data', d => { stdout += d }) + return finished(child).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 } // ??? + 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] + } + + if (type === 'tag') { + const match = ref.match(/v?(\d+\.\d+\.\d+)$/) + if (match) { + revs.versions[match[1]] = doc + } + } + + 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 + } + } + }) + } + REVS.set(repo, revs) + return revs + }) + }) + }) +} + +module.exports._exec = execGit +function execGit (gitArgs, gitOpts, opts) { + opts = optCheck(opts) + return checkGit().then(gitPath => { + return execFileAsync(gitPath, gitArgs, mkOpts(gitOpts, opts)) + }) +} + +module.exports._spawn = spawnGit +function spawnGit (gitArgs, gitOpts, opts) { + opts = optCheck(opts) + return checkGit().then(gitPath => { + return cp.spawn(gitPath, gitArgs, mkOpts(gitOpts, opts)) + }) +} + +function mkOpts (_gitOpts, opts) { + const gitOpts = { + env: gitEnv() + } + if (+opts.uid && !isNaN(opts.uid)) { + gitOpts.uid = +opts.uid + } + if (+opts.gid && !isNaN(opts.gid)) { + gitOpts.gid = +opts.gid + } + Object.assign(gitOpts, _gitOpts) + return gitOpts +} + +function checkGit () { + if (!GITPATH) { + const err = new Error('No git binary found in $PATH') + err.code = 'ENOGIT' + return BB.reject(err) + } else { + return BB.resolve(GITPATH) + } +} + +function refType (ref) { + return ref.match(/refs\/tags\/.*$/) + ? 'tag' + : ref.match(/refs\/heads\/.*$/) + ? 'branch' + : ref.match(/HEAD$/) + ? 'head' + : 'other' +} |