UNPKG

@dependabot/yarn-lib

Version:

📦🐈 Fast, reliable, and secure dependency management.

638 lines (514 loc) 18.9 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _asyncToGenerator2; function _load_asyncToGenerator() { return _asyncToGenerator2 = _interopRequireDefault(require('babel-runtime/helpers/asyncToGenerator')); } var _extends2; function _load_extends() { return _extends2 = _interopRequireDefault(require('babel-runtime/helpers/extends')); } var _invariant; function _load_invariant() { return _invariant = _interopRequireDefault(require('invariant')); } var _string_decoder; function _load_string_decoder() { return _string_decoder = require('string_decoder'); } var _tarFs; function _load_tarFs() { return _tarFs = _interopRequireDefault(require('tar-fs')); } var _tarStream; function _load_tarStream() { return _tarStream = _interopRequireDefault(require('tar-stream')); } var _url; function _load_url() { return _url = _interopRequireDefault(require('url')); } var _fs; function _load_fs() { return _fs = require('fs'); } var _errors; function _load_errors() { return _errors = require('../errors.js'); } var _gitSpawn; function _load_gitSpawn() { return _gitSpawn = require('./git/git-spawn.js'); } var _gitRefResolver; function _load_gitRefResolver() { return _gitRefResolver = require('./git/git-ref-resolver.js'); } var _crypto; function _load_crypto() { return _crypto = _interopRequireWildcard(require('./crypto.js')); } var _fs2; function _load_fs2() { return _fs2 = _interopRequireWildcard(require('./fs.js')); } var _map; function _load_map() { return _map = _interopRequireDefault(require('./map.js')); } var _misc; function _load_misc() { return _misc = require('./misc.js'); } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const GIT_PROTOCOL_PREFIX = 'git+'; const SSH_PROTOCOL = 'ssh:'; const SCP_PATH_PREFIX = '/:'; const FILE_PROTOCOL = 'file:'; const GIT_VALID_REF_LINE_REGEXP = /^([a-fA-F0-9]+|ref)/; const validRef = line => { return GIT_VALID_REF_LINE_REGEXP.exec(line); }; const supportsArchiveCache = (0, (_map || _load_map()).default)({ 'github.com': false // not support, doubt they will ever support it }); const handleSpawnError = err => { if (err instanceof (_errors || _load_errors()).ProcessSpawnError) { throw err; } }; const SHORTHAND_SERVICES = (0, (_map || _load_map()).default)({ 'github:': parsedUrl => (0, (_extends2 || _load_extends()).default)({}, parsedUrl, { slashes: true, auth: 'git', protocol: SSH_PROTOCOL, host: 'github.com', hostname: 'github.com', pathname: `/${parsedUrl.hostname}${parsedUrl.pathname}` }), 'bitbucket:': parsedUrl => (0, (_extends2 || _load_extends()).default)({}, parsedUrl, { slashes: true, auth: 'git', protocol: SSH_PROTOCOL, host: 'bitbucket.com', hostname: 'bitbucket.com', pathname: `/${parsedUrl.hostname}${parsedUrl.pathname}` }) }); class Git { constructor(config, gitUrl, hash) { this.supportsArchive = false; this.fetched = false; this.config = config; this.reporter = config.reporter; this.hash = hash; this.ref = hash; this.gitUrl = gitUrl; this.cwd = this.config.getTemp((_crypto || _load_crypto()).hash(this.gitUrl.repository)); } /** * npm URLs contain a 'git+' scheme prefix, which is not understood by git. * git "URLs" also allow an alternative scp-like syntax, so they're not standard URLs. */ static npmUrlToGitUrl(npmUrl) { npmUrl = (0, (_misc || _load_misc()).removePrefix)(npmUrl, GIT_PROTOCOL_PREFIX); let parsed = (_url || _load_url()).default.parse(npmUrl); const expander = parsed.protocol && SHORTHAND_SERVICES[parsed.protocol]; if (expander) { parsed = expander(parsed); } // Special case in npm, where ssh:// prefix is stripped to pass scp-like syntax // which in git works as remote path only if there are no slashes before ':'. // See #3146. if (parsed.protocol === SSH_PROTOCOL && parsed.hostname && parsed.path && parsed.path.startsWith(SCP_PATH_PREFIX) && parsed.port === null) { const auth = parsed.auth ? parsed.auth + '@' : ''; const pathname = parsed.path.slice(SCP_PATH_PREFIX.length); return { hostname: parsed.hostname, protocol: parsed.protocol, repository: `${auth}${parsed.hostname}:${pathname}` }; } // git local repos are specified as `git+file:` and a filesystem path, not a url. let repository; if (parsed.protocol === FILE_PROTOCOL) { repository = parsed.path; } else { repository = (_url || _load_url()).default.format((0, (_extends2 || _load_extends()).default)({}, parsed, { hash: '' })); } return { hostname: parsed.hostname || null, protocol: parsed.protocol || FILE_PROTOCOL, repository: repository || '' }; } /** * Check if the host specified in the input `gitUrl` has archive capability. */ static hasArchiveCapability(ref) { return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { const hostname = ref.hostname; if (ref.protocol !== 'ssh:' || hostname == null) { return false; } if (hostname in supportsArchiveCache) { return supportsArchiveCache[hostname]; } try { yield (0, (_gitSpawn || _load_gitSpawn()).spawn)(['archive', `--remote=${ref.repository}`, 'HEAD', Date.now() + '']); throw new Error(); } catch (err) { handleSpawnError(err); const supports = err.message.indexOf('did not match any files') >= 0; return supportsArchiveCache[hostname] = supports; } })(); } /** * Check if the input `target` is a 5-40 character hex commit hash. */ static repoExists(ref) { return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { const isLocal = ref.protocol === FILE_PROTOCOL; try { if (isLocal) { yield (0, (_gitSpawn || _load_gitSpawn()).spawn)(['show-ref', '-t'], { cwd: ref.repository }); } else { yield (0, (_gitSpawn || _load_gitSpawn()).spawn)(['ls-remote', '-t', ref.repository]); } return true; } catch (err) { handleSpawnError(err); return false; } })(); } static replaceProtocol(ref, protocol) { return { hostname: ref.hostname, protocol, repository: ref.repository.replace(/^(?:git|http):/, protocol) }; } /** * Attempt to upgrade insecure protocols to secure protocol */ static secureGitUrl(ref, hash, reporter) { return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { if ((0, (_gitRefResolver || _load_gitRefResolver()).isCommitSha)(hash)) { // this is cryptographically secure return ref; } if (ref.protocol === 'git:') { const secureUrl = Git.replaceProtocol(ref, 'https:'); if (yield Git.repoExists(secureUrl)) { return secureUrl; } else { reporter.warn(reporter.lang('downloadGitWithoutCommit', ref.repository)); return ref; } } if (ref.protocol === 'http:') { const secureRef = Git.replaceProtocol(ref, 'https:'); if (yield Git.repoExists(secureRef)) { return secureRef; } else { reporter.warn(reporter.lang('downloadHTTPWithoutCommit', ref.repository)); return ref; } } return ref; })(); } /** * Archive a repo to destination */ archive(dest) { if (this.supportsArchive) { return this._archiveViaRemoteArchive(dest); } else { return this._archiveViaLocalFetched(dest); } } _archiveViaRemoteArchive(dest) { var _this = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { const hashStream = new (_crypto || _load_crypto()).HashStream(); yield (0, (_gitSpawn || _load_gitSpawn()).spawn)(['archive', `--remote=${_this.gitUrl.repository}`, _this.ref], { process(proc, resolve, reject, done) { const writeStream = (0, (_fs || _load_fs()).createWriteStream)(dest); proc.on('error', reject); writeStream.on('error', reject); writeStream.on('end', done); writeStream.on('open', function () { proc.stdout.pipe(hashStream).pipe(writeStream); }); writeStream.once('finish', done); } }); return hashStream.getHash(); })(); } _archiveViaLocalFetched(dest) { var _this2 = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { const hashStream = new (_crypto || _load_crypto()).HashStream(); yield (0, (_gitSpawn || _load_gitSpawn()).spawn)(['archive', _this2.hash], { cwd: _this2.cwd, process(proc, resolve, reject, done) { const writeStream = (0, (_fs || _load_fs()).createWriteStream)(dest); proc.on('error', reject); writeStream.on('error', reject); writeStream.on('open', function () { proc.stdout.pipe(hashStream).pipe(writeStream); }); writeStream.once('finish', done); } }); return hashStream.getHash(); })(); } /** * Clone a repo to the input `dest`. Use `git archive` if it's available, otherwise fall * back to `git clone`. */ clone(dest) { if (this.supportsArchive) { return this._cloneViaRemoteArchive(dest); } else { return this._cloneViaLocalFetched(dest); } } _cloneViaRemoteArchive(dest) { var _this3 = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { yield (0, (_gitSpawn || _load_gitSpawn()).spawn)(['archive', `--remote=${_this3.gitUrl.repository}`, _this3.ref], { process(proc, update, reject, done) { const extractor = (_tarFs || _load_tarFs()).default.extract(dest, { dmode: 0o555, // all dirs should be readable fmode: 0o444 // all files should be readable }); extractor.on('error', reject); extractor.on('finish', done); proc.stdout.pipe(extractor); proc.on('error', reject); } }); })(); } _cloneViaLocalFetched(dest) { var _this4 = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { yield (0, (_gitSpawn || _load_gitSpawn()).spawn)(['archive', _this4.hash], { cwd: _this4.cwd, process(proc, resolve, reject, done) { const extractor = (_tarFs || _load_tarFs()).default.extract(dest, { dmode: 0o555, // all dirs should be readable fmode: 0o444 // all files should be readable }); extractor.on('error', reject); extractor.on('finish', done); proc.stdout.pipe(extractor); } }); })(); } /** * Clone this repo. */ fetch() { var _this5 = this; const gitUrl = this.gitUrl, cwd = this.cwd; return (_fs2 || _load_fs2()).lockQueue.push(gitUrl.repository, (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { if (yield (_fs2 || _load_fs2()).exists(cwd)) { yield (0, (_gitSpawn || _load_gitSpawn()).spawn)(['fetch', '--tags'], { cwd }); yield (0, (_gitSpawn || _load_gitSpawn()).spawn)(['pull'], { cwd }); } else { yield (0, (_gitSpawn || _load_gitSpawn()).spawn)(['clone', gitUrl.repository, cwd]); } _this5.fetched = true; })); } /** * Fetch the file by cloning the repo and reading it. */ getFile(filename) { if (this.supportsArchive) { return this._getFileFromArchive(filename); } else { return this._getFileFromClone(filename); } } _getFileFromArchive(filename) { var _this6 = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { try { return yield (0, (_gitSpawn || _load_gitSpawn()).spawn)(['archive', `--remote=${_this6.gitUrl.repository}`, _this6.ref, filename], { process(proc, update, reject, done) { const parser = (_tarStream || _load_tarStream()).default.extract(); parser.on('error', reject); parser.on('finish', done); parser.on('entry', (header, stream, next) => { const decoder = new (_string_decoder || _load_string_decoder()).StringDecoder('utf8'); let fileContent = ''; stream.on('data', buffer => { fileContent += decoder.write(buffer); }); stream.on('end', () => { const remaining = decoder.end(); update(fileContent + remaining); next(); }); stream.resume(); }); proc.stdout.pipe(parser); } }); } catch (err) { if (err.message.indexOf('did not match any files') >= 0) { return false; } else { throw err; } } })(); } _getFileFromClone(filename) { var _this7 = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { (0, (_invariant || _load_invariant()).default)(_this7.fetched, 'Repo not fetched'); try { return yield (0, (_gitSpawn || _load_gitSpawn()).spawn)(['show', `${_this7.hash}:${filename}`], { cwd: _this7.cwd }); } catch (err) { handleSpawnError(err); // file doesn't exist return false; } })(); } /** * Initialize the repo, find a secure url to use and * set the ref to match an input `target`. */ init() { var _this8 = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { _this8.gitUrl = yield Git.secureGitUrl(_this8.gitUrl, _this8.hash, _this8.reporter); yield _this8.setRefRemote(); // check capabilities if (_this8.ref !== '' && (yield Git.hasArchiveCapability(_this8.gitUrl))) { _this8.supportsArchive = true; } else { yield _this8.fetch(); } return _this8.hash; })(); } setRefRemote() { var _this9 = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { const isLocal = _this9.gitUrl.protocol === FILE_PROTOCOL; let stdout; if (isLocal) { stdout = yield (0, (_gitSpawn || _load_gitSpawn()).spawn)(['show-ref', '--tags', '--heads'], { cwd: _this9.gitUrl.repository }); } else { stdout = yield (0, (_gitSpawn || _load_gitSpawn()).spawn)(['ls-remote', '--tags', '--heads', _this9.gitUrl.repository]); } const refs = (0, (_gitRefResolver || _load_gitRefResolver()).parseRefs)(stdout); return _this9.setRef(refs); })(); } setRefHosted(hostedRefsList) { const refs = (0, (_gitRefResolver || _load_gitRefResolver()).parseRefs)(hostedRefsList); return this.setRef(refs); } /** * Resolves the default branch of a remote repository (not always "master") */ resolveDefaultBranch() { var _this10 = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { const isLocal = _this10.gitUrl.protocol === FILE_PROTOCOL; try { let stdout; if (isLocal) { stdout = yield (0, (_gitSpawn || _load_gitSpawn()).spawn)(['show-ref', 'HEAD'], { cwd: _this10.gitUrl.repository }); const refs = (0, (_gitRefResolver || _load_gitRefResolver()).parseRefs)(stdout); const sha = refs.values().next().value; if (sha) { return { sha, ref: undefined }; } else { throw new Error('Unable to find SHA for git HEAD'); } } else { stdout = yield (0, (_gitSpawn || _load_gitSpawn()).spawn)(['ls-remote', '--symref', _this10.gitUrl.repository, 'HEAD']); const lines = stdout.split('\n').filter(validRef); var _lines$0$split = lines[0].split(/\s+/); const ref = _lines$0$split[1]; var _lines$1$split = lines[1].split(/\s+/); const sha = _lines$1$split[0]; return { sha, ref }; } } catch (err) { handleSpawnError(err); // older versions of git don't support "--symref" const stdout = yield (0, (_gitSpawn || _load_gitSpawn()).spawn)(['ls-remote', _this10.gitUrl.repository, 'HEAD']); const lines = stdout.split('\n').filter(validRef); var _lines$0$split2 = lines[0].split(/\s+/); const sha = _lines$0$split2[0]; return { sha, ref: undefined }; } })(); } /** * Resolve a git commit to it's 40-chars format and ensure it exists in the repository * We need to use the 40-chars format to avoid multiple folders in the cache */ resolveCommit(shaToResolve) { var _this11 = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { try { yield _this11.fetch(); const revListArgs = ['rev-list', '-n', '1', '--no-abbrev-commit', '--format=oneline', shaToResolve]; const stdout = yield (0, (_gitSpawn || _load_gitSpawn()).spawn)(revListArgs, { cwd: _this11.cwd }); var _stdout$split = stdout.split(/\s+/); const sha = _stdout$split[0]; return { sha, ref: undefined }; } catch (err) { handleSpawnError(err); // assuming commit not found, let's try something else return null; } })(); } /** * Resolves the input hash / ref / semver range to a valid commit sha * If possible also resolves the sha to a valid ref in order to use "git archive" */ setRef(refs) { var _this12 = this; return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { // get commit ref const version = _this12.hash; const resolvedResult = yield (0, (_gitRefResolver || _load_gitRefResolver()).resolveVersion)({ config: _this12.config, git: _this12, version, refs }); if (!resolvedResult) { throw new (_errors || _load_errors()).MessageError(_this12.reporter.lang('couldntFindMatch', version, Array.from(refs.keys()).join(','), _this12.gitUrl.repository)); } _this12.hash = resolvedResult.sha; _this12.ref = resolvedResult.ref || ''; return _this12.hash; })(); } } exports.default = Git;