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 | |
parent | 5cf0abaf2b50946420952e9ce36e357ac0d08d54 (diff) | |
download | gitbook-347a536a4e313dece2e12d0ba1ec6654ee729c85.zip gitbook-347a536a4e313dece2e12d0ba1ec6654ee729c85.tar.gz gitbook-347a536a4e313dece2e12d0ba1ec6654ee729c85.tar.bz2 |
Add git urls parsing and tests
-rw-r--r-- | lib/utils/command.js | 19 | ||||
-rw-r--r-- | lib/utils/git.js | 119 | ||||
-rw-r--r-- | test.html | 16 | ||||
-rw-r--r-- | test/all.js | 1 | ||||
-rw-r--r-- | test/git.js | 48 |
5 files changed, 203 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 +}; diff --git a/test.html b/test.html new file mode 100644 index 0000000..3dee7ef --- /dev/null +++ b/test.html @@ -0,0 +1,16 @@ +<!doctype html> + +<html lang="en"> +<head> + <meta charset="utf-8"> + + <title>The HTML5 Herald</title> + <meta name="description" content="The HTML5 Herald"> + <meta name="author" content="SitePoint"> + +</head> + +<body> + <script src="test-browser.js"></script> +</body> +</html>
\ No newline at end of file diff --git a/test/all.js b/test/all.js index 4a5ba83..0f55603 100644 --- a/test/all.js +++ b/test/all.js @@ -8,6 +8,7 @@ require('./langs'); require('./parse'); require('./template'); +require('./git'); // Output require('./output-json'); diff --git a/test/git.js b/test/git.js new file mode 100644 index 0000000..bad031b --- /dev/null +++ b/test/git.js @@ -0,0 +1,48 @@ +var should = require('should'); +var git = require('../lib/utils/git'); + +describe('Git', function() { + + describe('URL parsing', function() { + + it('should correctly validate git urls', function() { + // HTTPS + git.isUrl('git+https://github.com/Hello/world.git').should.be.ok; + + // SSH + git.isUrl('git+git@github.com:GitbookIO/gitbook.git/directory/README.md#e1594cde2c32e4ff48f6c4eff3d3d461743d74e1').should.be.ok; + + // Non valid + git.isUrl('https://github.com/Hello/world.git').should.not.be.ok; + git.isUrl('README.md').should.not.be.ok; + }); + + it('should parse HTTPS urls', function() { + var parts = git.parseUrl('git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md'); + + should.exist(parts); + parts.host.should.be.equal('https://gist.github.com/69ea4542e4c8967d2fa7.git'); + parts.ref.should.be.equal('master'); + parts.filepath.should.be.equal('test.md'); + }); + + it('should parse HTTPS urls with a reference', function() { + var parts = git.parseUrl('git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md#1.0.0'); + + should.exist(parts); + parts.host.should.be.equal('https://gist.github.com/69ea4542e4c8967d2fa7.git'); + parts.ref.should.be.equal('1.0.0'); + parts.filepath.should.be.equal('test.md'); + }); + + it('should parse SSH urls', function() { + var parts = git.parseUrl('git+git@github.com:GitbookIO/gitbook.git/directory/README.md#e1594cde2c32e4ff48f6c4eff3d3d461743d74e1'); + + should.exist(parts); + parts.host.should.be.equal('git@github.com:GitbookIO/gitbook.git'); + parts.ref.should.be.equal('e1594cde2c32e4ff48f6c4eff3d3d461743d74e1'); + parts.filepath.should.be.equal('directory/README.md'); + }); + }); + +}); |