summaryrefslogtreecommitdiffstats
path: root/lib/utils/git.js
blob: 6eb96811a5bcadb42f9c7980bc6dceb15286bd38 (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
119
120
121
122
123
124
125
126
127
128
129
var  Q = require("q");
var _ = require("lodash");
var url = require("url");
var tmp = require("tmp");
var path = require("path");
var crc = require("crc");
var exec = Q.denodeify(require("child_process").exec);
var URI = require("URIjs");
var pathUtil = require("./path");

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, uri, fileParts, filepath;

    if (!checkGitUrl(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) {
    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);
    });
};

// 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 = {
    checkUrl: checkGitUrl,
    parseUrl: parseGitUrl,
    resolveFile: resolveFileFromGit,
    resolveRoot: resolveGitRoot
};