summaryrefslogtreecommitdiffstats
path: root/lib/utils
diff options
context:
space:
mode:
authorSamy Pesse <samypesse@gmail.com>2016-02-11 16:35:41 +0100
committerSamy Pesse <samypesse@gmail.com>2016-02-11 16:35:41 +0100
commit347a536a4e313dece2e12d0ba1ec6654ee729c85 (patch)
tree8308fe7a3b031361bcafaaa94cc76232732e259d /lib/utils
parent5cf0abaf2b50946420952e9ce36e357ac0d08d54 (diff)
downloadgitbook-347a536a4e313dece2e12d0ba1ec6654ee729c85.zip
gitbook-347a536a4e313dece2e12d0ba1ec6654ee729c85.tar.gz
gitbook-347a536a4e313dece2e12d0ba1ec6654ee729c85.tar.bz2
Add git urls parsing and tests
Diffstat (limited to 'lib/utils')
-rw-r--r--lib/utils/command.js19
-rw-r--r--lib/utils/git.js119
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
+};