PATH:
usr
/
local
/
lib
/
node_modules
/
bower
/
lib
/
core
/
resolvers
var util = require('util'); var path = require('path'); var Q = require('q'); var rimraf = require('../../util/rimraf'); var mkdirp = require('mkdirp'); var which = require('which'); var LRU = require('lru-cache'); var mout = require('mout'); var Resolver = require('./Resolver'); var semver = require('../../util/semver'); var createError = require('../../util/createError'); var hasGit; // Check if git is installed try { which.sync('git'); hasGit = true; } catch (ex) { hasGit = false; } function GitResolver(decEndpoint, config, logger) { // Set template dir to the empty directory so that user templates are not run // This environment variable is not multiple config aware but it's not documented // anyway mkdirp.sync(config.storage.empty); process.env.GIT_TEMPLATE_DIR = config.storage.empty; if (!config.strictSsl) { process.env.GIT_SSL_NO_VERIFY = 'true'; } if (!config.interactive) { process.env.GIT_TERMINAL_PROMPT = '0'; if (!process.env.SSH_ASKPASS) { process.env.SSH_ASKPASS = 'echo'; } } Resolver.call(this, decEndpoint, config, logger); if (!hasGit) { throw createError('git is not installed or not in the PATH', 'ENOGIT'); } } util.inherits(GitResolver, Resolver); mout.object.mixIn(GitResolver, Resolver); // ----------------- GitResolver.prototype._hasNew = function(pkgMeta) { var oldResolution = pkgMeta._resolution || {}; return this._findResolution().then(function(resolution) { // Check if resolution types are different if (oldResolution.type !== resolution.type) { return true; } // If resolved to a version, there is new content if the tags are not equal if ( resolution.type === 'version' && semver.neq(resolution.tag, oldResolution.tag) ) { return true; } // As last check, we compare both commit hashes return resolution.commit !== oldResolution.commit; }); }; GitResolver.prototype._resolve = function() { var that = this; return this._findResolution().then(function() { return ( that ._checkout() // Always run cleanup after checkout to ensure that .git is removed! // If it's not removed, problems might arise when the "tmp" module attempts // to delete the temporary folder .fin(function() { return that._cleanup(); }) ); }); }; // ----------------- // Abstract functions that should be implemented by concrete git resolvers GitResolver.prototype._checkout = function() { throw new Error('_checkout not implemented'); }; GitResolver.refs = function(source) { throw new Error('refs not implemented'); }; // ----------------- GitResolver.prototype._findResolution = function(target) { var err; var self = this.constructor; var that = this; target = target || this._target || '*'; // Target is a commit, so it's a stale target (not a moving target) // There's nothing to do in this case if (/^[a-f0-9]{40}$/.test(target)) { this._resolution = { type: 'commit', commit: target }; return Q.resolve(this._resolution); } // Target is a range/version if (semver.validRange(target)) { return self.versions(this._source, true).then(function(versions) { var versionsArr, version, index; // If there are no tags and target is *, // fallback to the latest commit on master if (!versions.length && target === '*') { return that._findResolution('master'); } versionsArr = versions.map(function(obj) { return obj.version; }); // Find a satisfying version, enabling strict match so that pre-releases // have lower priority over normal ones when target is * index = semver.maxSatisfyingIndex(versionsArr, target, true); if (index !== -1) { version = versions[index]; return (that._resolution = { type: 'version', tag: version.tag, commit: version.commit }); } // Check if there's an exact branch/tag with this name as last resort return Q.all([ self.branches(that._source), self.tags(that._source) ]).spread(function(branches, tags) { // Use hasOwn because a branch/tag could have a name like "hasOwnProperty" if (mout.object.hasOwn(tags, target)) { return (that._resolution = { type: 'tag', tag: target, commit: tags[target] }); } if (mout.object.hasOwn(branches, target)) { return (that._resolution = { type: 'branch', branch: target, commit: branches[target] }); } throw createError( 'No tag found that was able to satisfy ' + target, 'ENORESTARGET', { details: !versions.length ? 'No versions found in ' + that._source : 'Available versions in ' + that._source + ': ' + versions .map(function(version) { return version.version; }) .join(', ') } ); }); }); } // Otherwise, target is either a tag or a branch return Q.all([self.branches(that._source), self.tags(that._source)]).spread( function(branches, tags) { // Use hasOwn because a branch/tag could have a name like "hasOwnProperty" if (mout.object.hasOwn(tags, target)) { return (that._resolution = { type: 'tag', tag: target, commit: tags[target] }); } if (mout.object.hasOwn(branches, target)) { return (that._resolution = { type: 'branch', branch: target, commit: branches[target] }); } if (/^[a-f0-9]{4,40}$/.test(target)) { if (target.length < 12) { that._logger.warn( 'short-sha', 'Consider using longer commit SHA to avoid conflicts' ); } that._resolution = { type: 'commit', commit: target }; return that._resolution; } branches = Object.keys(branches); tags = Object.keys(tags); err = createError( 'Tag/branch ' + target + ' does not exist', 'ENORESTARGET' ); err.details = !tags.length ? 'No tags found in ' + that._source : 'Available tags: ' + tags.join(', '); err.details += '\n'; err.details += !branches.length ? 'No branches found in ' + that._source : 'Available branches: ' + branches.join(', '); throw err; } ); }; GitResolver.prototype._cleanup = function() { var gitFolder = path.join(this._tempDir, '.git'); return Q.nfcall(rimraf, gitFolder); }; GitResolver.prototype._savePkgMeta = function(meta) { var version; if (this._resolution.type === 'version') { version = semver.clean(this._resolution.tag); // Warn if the package meta version is different than the resolved one if ( typeof meta.version === 'string' && semver.valid(meta.version) && semver.neq(meta.version, version) ) { this._logger.warn( 'mismatch', 'Version declared in the json (' + meta.version + ') is different than the resolved one (' + version + ')', { resolution: this._resolution, pkgMeta: meta } ); } // Ensure package meta version is the same as the resolution meta.version = version; } else { // If resolved to a target that is not a version, // remove the version from the meta delete meta.version; } // Save version/tag/commit in the release // Note that we can't store branches because _release is supposed to be // an unique id of this ref. meta._release = version || this._resolution.tag || this._resolution.commit.substr(0, 10); // Save resolution to be used in hasNew later meta._resolution = this._resolution; return Resolver.prototype._savePkgMeta.call(this, meta); }; // ------------------------------ GitResolver.versions = function(source, extra) { var value = this._cache.versions.get(source); if (value) { return Q.resolve(value).then( function() { var versions = this._cache.versions.get(source); // If no extra information was requested, // resolve simply with the versions if (!extra) { versions = versions.map(function(version) { return version.version; }); } return versions; }.bind(this) ); } value = this.tags(source).then( function(tags) { var tag; var version; var versions = []; // For each tag for (tag in tags) { version = semver.clean(tag); if (version) { versions.push({ version: version, tag: tag, commit: tags[tag] }); } } // Sort them by DESC order versions.sort(function(a, b) { return semver.rcompare(a.version, b.version); }); this._cache.versions.set(source, versions); // Call the function again to keep it DRY return this.versions(source, extra); }.bind(this) ); // Store the promise to be reused until it resolves // to a specific value this._cache.versions.set(source, value); return value; }; GitResolver.tags = function(source) { var value = this._cache.tags.get(source); if (value) { return Q.resolve(value); } value = this.refs(source).then( function(refs) { var tags = {}; // For each line in the refs, match only the tags refs.forEach(function(line) { var match = line.match(/^([a-f0-9]{40})\s+refs\/tags\/(\S+)/); if (match && !mout.string.endsWith(match[2], '^{}')) { tags[match[2]] = match[1]; } }); this._cache.tags.set(source, tags); return tags; }.bind(this) ); // Store the promise to be reused until it resolves // to a specific value this._cache.tags.set(source, value); return value; }; GitResolver.branches = function(source) { var value = this._cache.branches.get(source); if (value) { return Q.resolve(value); } value = this.refs(source).then( function(refs) { var branches = {}; // For each line in the refs, extract only the heads // Organize them in an object where keys are branches and values // the commit hashes refs.forEach(function(line) { var match = line.match(/^([a-f0-9]{40})\s+refs\/heads\/(\S+)/); if (match) { branches[match[2]] = match[1]; } }); this._cache.branches.set(source, branches); return branches; }.bind(this) ); // Store the promise to be reused until it resolves // to a specific value this._cache.branches.set(source, value); return value; }; GitResolver.clearRuntimeCache = function() { // Reset cache for branches, tags, etc mout.object.forOwn(GitResolver._cache, function(lru) { lru.reset(); }); }; GitResolver._cache = { branches: new LRU({ max: 50, maxAge: 5 * 60 * 1000 }), tags: new LRU({ max: 50, maxAge: 5 * 60 * 1000 }), versions: new LRU({ max: 50, maxAge: 5 * 60 * 1000 }), refs: new LRU({ max: 50, maxAge: 5 * 60 * 1000 }) }; module.exports = GitResolver;
[-] GitResolver.js
[edit]
[+]
..
[-] FsResolver.js
[edit]
[-] Resolver.js
[edit]
[-] UrlResolver.js
[edit]
[-] GitFsResolver.js
[edit]
[-] pluginResolverFactory.js
[edit]
[-] GitHubResolver.js
[edit]
[-] SvnResolver.js
[edit]
[-] index.js
[edit]
[-] GitRemoteResolver.js
[edit]