Created
October 26, 2018 13:30
-
-
Save garronej/c94c9054b665340b9d900ec372449cad to your computer and use it in GitHub Desktop.
Fix for npm git on cygwin
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//File @ /cygdrive/c/Program Files/nodejs/node_modules/npm/node_modules/pacote/lib/util/git.js | |
'use strict' | |
const BB = require('bluebird') | |
const cp = require('child_process') | |
const execFileAsync = BB.promisify(cp.execFile, { | |
multiArgs: true | |
}) | |
const finished = require('./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 promiseRetry = require('promise-retry') | |
const uniqueFilename = require('unique-filename') | |
const which = BB.promisify(require('which')) | |
const semver = require('semver') | |
const win32 = process.platform === "win32" | |
const cygwin = win32 && (process.env.ORIGINAL_PATH || '').indexOf('/cygdrive/') != -1 | |
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' | |
]) | |
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 () { | |
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.startsWith(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', '--mirror', '-q', repo, path.join(target, '.git')] | |
if ( win32 && !cygwin) { | |
gitArgs.push('--config', 'core.longpaths=true') | |
} | |
return execGit(gitArgs, {cwd: target}).then(() => { | |
return execGit(['init'], {cwd: target}) | |
}).then(() => { | |
return execGit(['checkout', committish || 'HEAD'], {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'] | |
if (branch) { | |
gitArgs.push('-b', branch) | |
} | |
gitArgs.push(repo, target) | |
if ( win32 && !cygwin ) { | |
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 CARET_BRACES = '^{}' | |
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', '-h', '-t', repo], { | |
env: gitEnv() | |
}, 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} | |
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 && semver.valid(match[1], true)) { | |
revs.versions[semver.clean(match[1], true)] = doc | |
} | |
} | |
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 | |
}) | |
}) | |
} | |
module.exports._exec = execGit | |
function execGit (gitArgs, gitOpts, opts) { | |
if(cygwin && gitArgs) { | |
for(var i=0; i<gitArgs.length; i++) { | |
if(':\\'.indexOf(gitArgs[i]) != 1) { | |
gitArgs[i] = gitArgs[i].replace(/\\/g, '/').replace(/^([A-Za-z])\:\//, '/cygdrive/$1/'); | |
} | |
} | |
} | |
opts = optCheck(opts) | |
return checkGit().then(gitPath => { | |
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) | |
}) | |
} | |
module.exports._spawn = spawnGit | |
function spawnGit (gitArgs, gitOpts, opts) { | |
opts = optCheck(opts) | |
return checkGit().then(gitPath => { | |
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, true).catch(err => { | |
if (shouldRetry(stderr)) { | |
retry(err) | |
} else { | |
err.stderr = stderr | |
throw err | |
} | |
}).then(() => { | |
return stdout | |
}) | |
}, opts.retry) | |
}) | |
} | |
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) | |
} | |
} | |
const REFS_TAGS = 'refs/tags/' | |
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' | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is the spirit: