summaryrefslogtreecommitdiffstats
path: root/lib/utils/git.js
blob: f7f63e7d6a9a53bcaba4a8a1362a65b72216f44d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
var  Q = require("q");
var _ = require("lodash");
var url = require("url");
var tmp = require("tmp");
var path = require("path");
var crc = require("crc");
var ngu = require('normalize-git-url');
var exec = Q.denodeify(require("child_process").exec);

var fs = require("./fs");

var GIT_PREFIX = "git+";
var GIT_TMP = null;


// Check if an url is a git dependency url
function checkGitUrl(giturl) {
	return (giturl.indexOf(GIT_PREFIX) === 0);
}

// Validates a SHA in hexadecimal
function validateSha(str) {
    return (/[0-9a-f]{40}/).test(str);
}

// Parse and extract infos
function parseGitUrl(giturl) {
	var ref, parts;

	if (!checkGitUrl(giturl)) return null;
	giturl = giturl.slice(GIT_PREFIX.length);

	if (!url.parse(giturl).protocol) {
		giturl = "ssh://"+giturl;
	}

	var normalized = ngu(giturl);

	parts = url.parse(normalized.url);
	ref = normalized.branch;

	// Extract file inside the repo (after the .git)
	var fileParts = parts.pathname.split(".git");
	var filepath = fileParts.length > 1? fileParts.slice(1).join(".git") : "";
	if (filepath[0] == "/") filepath = filepath.slice(1);

	// Rebuild
	parts.pathname = _.first(fileParts)+".git";
	var githost = url.format(parts);

	return {
		host: githost,
		ref: ref || "master",
		filepath: filepath
	};
}

// Clone a git repo from a specific ref
function cloneGitRepo(host, ref) {
	var isBranch = false;

	ref = ref || "master";
	if (!validateSha(ref)) isBranch = true;

	return Q()

	// 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 exec("git clone "+host+" "+repoPath)
				.then(function() {
					return 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 Q(null);

	// Clone or get from cache
	return cloneGitRepo(giturl.host, giturl.ref)
	.then(function(repo) {

		// Resolve relative path
		return path.resolve(repo, giturl.filepath);
	});
};


module.exports = {
	checkUrl: checkGitUrl,
	parseUrl: parseGitUrl,
	resolveFile: resolveFileFromGit
};