diff options
author | Samy Pesse <samypesse@gmail.com> | 2016-02-11 16:35:41 +0100 |
---|---|---|
committer | Samy Pesse <samypesse@gmail.com> | 2016-02-11 16:35:41 +0100 |
commit | 347a536a4e313dece2e12d0ba1ec6654ee729c85 (patch) | |
tree | 8308fe7a3b031361bcafaaa94cc76232732e259d /lib | |
parent | 5cf0abaf2b50946420952e9ce36e357ac0d08d54 (diff) | |
download | gitbook-347a536a4e313dece2e12d0ba1ec6654ee729c85.zip gitbook-347a536a4e313dece2e12d0ba1ec6654ee729c85.tar.gz gitbook-347a536a4e313dece2e12d0ba1ec6654ee729c85.tar.bz2 |
Add git urls parsing and tests
Diffstat (limited to 'lib')
-rw-r--r-- | lib/utils/command.js | 19 | ||||
-rw-r--r-- | lib/utils/git.js | 119 |
2 files changed, 138 insertions, 0 deletions
diff --git a/lib/utils/command.js b/lib/utils/command.js new file mode 100644 index 0000000..f395fa1 --- /dev/null +++ b/lib/utils/command.js @@ -0,0 +1,19 @@ +var childProcess = require('child_process'); +var Promise = require('./promise'); + +// On borwser, command execution is not possible +var isAvailable = childProcess && childProcess.exec; + +// Execute a command +function exec(command, options) { + if (!isAvailable) { + return Promise.reject(new Error('Command execution is not possible on this platform')); + } + + return Promise.nfcall(childProcess.exec, command, options); +} + +module.exports = { + isAvailable: isAvailable, + exec: exec +}; diff --git a/lib/utils/git.js b/lib/utils/git.js new file mode 100644 index 0000000..6e37b14 --- /dev/null +++ b/lib/utils/git.js @@ -0,0 +1,119 @@ +var _ = require('lodash'); +var path = require('path'); +var crc = require('crc'); +var URI = require('urijs'); +var pathUtil = require('./path'); + +var Promise = require('./promise'); +var command = require('./command'); +var fs = require('./fs'); + +var GIT_PREFIX = 'git+'; +var GIT_TMP = null; + + +// Check if an url is a git dependency url +function isGitUrl(giturl) { + return (giturl.indexOf(GIT_PREFIX) === 0); +} + +// Parse and extract infos +function parseGitUrl(giturl) { + var ref, uri, fileParts, filepath; + + if (!isGitUrl(giturl)) return null; + giturl = giturl.slice(GIT_PREFIX.length); + + uri = new URI(giturl); + ref = uri.fragment() || 'master'; + uri.fragment(null); + + // Extract file inside the repo (after the .git) + fileParts =uri.path().split('.git'); + filepath = fileParts.length > 1? fileParts.slice(1).join('.git') : ''; + if (filepath[0] == '/') filepath = filepath.slice(1); + + // Recreate pathname without the real filename + uri.path(_.first(fileParts)+'.git'); + + return { + host: uri.toString(), + ref: ref || 'master', + filepath: filepath + }; +} + +// Clone a git repo from a specific ref +function cloneGitRepo(host, ref) { + ref = ref || 'master'; + + return Promise() + + // Create temporary folder to store git repos + .then(function() { + if (GIT_TMP) return; + return fs.tmp.dir() + .then(function(_tmp) { + GIT_TMP = _tmp; + }); + }) + + // Return or clone the git repo + .then(function() { + // Unique ID for repo/ref combinaison + var repoId = crc.crc32(host+'#'+ref).toString(16); + + // Absolute path to the folder + var repoPath = path.resolve(GIT_TMP, repoId); + + return fs.exists(repoPath) + .then(function(doExists) { + if (doExists) return; + + // Clone repo + return command.exec('git clone '+host+' '+repoPath) + .then(function() { + return command.exec('git checkout '+ref, { cwd: repoPath }); + }); + }) + .thenResolve(repoPath); + }); +} + +// Get file from a git repo +function resolveFileFromGit(giturl) { + if (_.isString(giturl)) giturl = parseGitUrl(giturl); + if (!giturl) return Promise(null); + + // Clone or get from cache + return cloneGitRepo(giturl.host, giturl.ref) + .then(function(repo) { + + // Resolve relative path + return path.resolve(repo, giturl.filepath); + }); +} + +// Return root of git repo from a filepath +function resolveGitRoot(filepath) { + var relativeToGit, repoId; + + // No git repo cloned, or file is not in a git repository + if (!GIT_TMP || !pathUtil.isInRoot(GIT_TMP, filepath)) return null; + + // Extract first directory (is the repo id) + relativeToGit = path.relative(GIT_TMP, filepath); + repoId = _.first(relativeToGit.split(path.sep)); + if (!repoId) return; + + // Return an absolute file + return path.resolve(GIT_TMP, repoId); +} + + +module.exports = { + isUrl: isGitUrl, + parseUrl: parseGitUrl, + resolveFile: resolveFileFromGit, + resolveRoot: resolveGitRoot +}; |