Completed
Push — master ( 14c8ac...ce45ac )
by Michael
7s
created

clone()   C

Complexity

Conditions 7

Size

Total Lines 42

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 7
dl 0
loc 42
rs 5.5
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
4
"""
5
cookiecutter.vcs
6
----------------
7
8
Helper functions for working with version control systems.
9
"""
10
11
from __future__ import unicode_literals
12
import logging
13
import os
14
import subprocess
15
import sys
16
17
from whichcraft import which
18
19
from .exceptions import UnknownRepoType, VCSNotInstalled
20
from .prompt import read_user_yes_no
21
from .utils import make_sure_path_exists, rmtree
22
23
24
def prompt_and_delete_repo(repo_dir, no_input=False):
25
    """
26
    Asks the user whether it's okay to delete the previously-cloned repo.
27
    If yes, deletes it. Otherwise, Cookiecutter exits.
28
29
    :param repo_dir: Directory of previously-cloned repo.
30
    :param no_input: Suppress prompt to delete repo and just delete it.
31
    """
32
33
    # Suppress prompt if called via API
34
    if no_input:
35
        ok_to_delete = True
36
    else:
37
        question = (
38
            "You've cloned {0} before. "
39
            'Is it okay to delete and re-clone it?'
40
        ).format(repo_dir)
41
42
        ok_to_delete = read_user_yes_no(question, 'yes')
43
44
    if ok_to_delete:
45
        rmtree(repo_dir)
46
    else:
47
        sys.exit()
48
49
50
def identify_repo(repo_url):
51
    """
52
    Determines if `repo_url` should be treated as a URL to a git or hg repo.
53
    Repos can be identified prepeding "hg+" or "git+" to repo URL.
54
55
    :param repo_url: Repo URL of unknown type.
56
    :returns: ("git", repo_url), ("hg", repo_url), or None.
57
    """
58
    repo_url_values = repo_url.split('+')
59
    if len(repo_url_values) == 2:
60
        repo_type = repo_url_values[0]
61
        if repo_type in ["git", "hg"]:
62
            return repo_type, repo_url_values[1]
63
        else:
64
            raise UnknownRepoType
65
    else:
66
        if "git" in repo_url:
67
            return "git", repo_url
68
        elif "bitbucket" in repo_url:
69
            return "hg", repo_url
70
        else:
71
            raise UnknownRepoType
72
73
74
def is_vcs_installed(repo_type):
75
    """
76
    Check if the version control system for a repo type is installed.
77
78
    :param repo_type:
79
    """
80
    return bool(which(repo_type))
81
82
83
def clone(repo_url, checkout=None, clone_to_dir=".", no_input=False):
84
    """
85
    Clone a repo to the current directory.
86
87
    :param repo_url: Repo URL of unknown type.
88
    :param checkout: The branch, tag or commit ID to checkout after clone.
89
    :param clone_to_dir: The directory to clone to.
90
                         Defaults to the current directory.
91
    :param no_input: Suppress all user prompts when calling via API.
92
    """
93
94
    # Ensure that clone_to_dir exists
95
    clone_to_dir = os.path.expanduser(clone_to_dir)
96
    make_sure_path_exists(clone_to_dir)
97
98
    # identify the repo_type
99
    repo_type, repo_url = identify_repo(repo_url)
100
101
    # check that the appropriate VCS for the repo_type is installed
102
    if not is_vcs_installed(repo_type):
103
        msg = "'{0}' is not installed.".format(repo_type)
104
        raise VCSNotInstalled(msg)
105
106
    repo_url = repo_url.rstrip('/')
107
    tail = os.path.split(repo_url)[1]
108
    if repo_type == 'git':
109
        repo_dir = os.path.normpath(os.path.join(clone_to_dir,
110
                                                 tail.rsplit('.git')[0]))
111
    elif repo_type == 'hg':
112
        repo_dir = os.path.normpath(os.path.join(clone_to_dir, tail))
113
    logging.debug('repo_dir is {0}'.format(repo_dir))
114
115
    if os.path.isdir(repo_dir):
116
        prompt_and_delete_repo(repo_dir, no_input=no_input)
117
118
    if repo_type in ['git', 'hg']:
119
        subprocess.check_call([repo_type, 'clone', repo_url], cwd=clone_to_dir)
120
        if checkout is not None:
121
            subprocess.check_call([repo_type, 'checkout', checkout],
122
                                  cwd=repo_dir)
123
124
    return repo_dir
125