1# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright (c) 2016 Cisco Systems, Inc.
4#
5#  Permission is hereby granted, free of charge, to any person obtaining a copy
6#  of this software and associated documentation files (the "Software"), to
7#  deal in the Software without restriction, including without limitation the
8#  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9#  sell copies of the Software, and to permit persons to whom the Software is
10#  furnished to do so, subject to the following conditions:
11#
12#  The above copyright notice and this permission notice shall be included in
13#  all copies or substantial portions of the Software.
14#
15#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20#  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21#  DEALINGS IN THE SOFTWARE.
22
23import glob
24import os
25import shutil
26
27import sh
28
29from gilt import util
30
31
32def clone(name, repository, destination, debug=False):
33    """
34    Clone the specified repository into a temporary directory and return None.
35
36    :param name: A string containing the name of the repository being cloned.
37    :param repository: A string containing the repository to clone.
38    :param destination: A string containing the directory to clone the
39     repository into.
40    :param debug: An optional bool to toggle debug output.
41    :return: None
42    """
43    msg = '  - cloning {} to {}'.format(name, destination)
44    util.print_info(msg)
45    cmd = sh.git.bake('clone', repository, destination)
46    util.run_command(cmd, debug=debug)
47
48
49def extract(repository, destination, version, debug=False):
50    """
51    Extract the specified repository/version into the given directory and
52    return None.
53
54    :param repository: A string containing the path to the repository to be
55     extracted.
56    :param destination: A string containing the directory to clone the
57     repository into.  Relative to the directory ``gilt`` is running
58     in. Must end with a '/'.
59    :param version: A string containing the branch/tag/sha to be exported.
60    :param debug: An optional bool to toggle debug output.
61    :return: None
62    """
63    with util.saved_cwd():
64        if os.path.isdir(destination):
65            shutil.rmtree(destination)
66
67        os.chdir(repository)
68        _get_version(version, debug)
69        cmd = sh.git.bake(
70            'checkout-index', force=True, all=True, prefix=destination)
71        util.run_command(cmd, debug=debug)
72        msg = '  - extracting ({}) {} to {}'.format(version, repository,
73                                                    destination)
74        util.print_info(msg)
75
76
77def overlay(repository, files, version, debug=False):
78    """
79    Overlay files from the specified repository/version into the given
80    directory and return None.
81
82    :param repository: A string containing the path to the repository to be
83     extracted.
84    :param files: A list of `FileConfig` objects.
85    :param version: A string containing the branch/tag/sha to be exported.
86    :param debug: An optional bool to toggle debug output.
87    :return: None
88    """
89    with util.saved_cwd():
90        os.chdir(repository)
91        _get_version(version, debug)
92
93        for fc in files:
94            if '*' in fc.src:
95                for filename in glob.glob(fc.src):
96                    util.copy(filename, fc.dst)
97                    msg = '  - copied ({}) {} to {}'.format(
98                        version, filename, fc.dst)
99                    util.print_info(msg)
100            else:
101                if os.path.isdir(fc.dst) and os.path.isdir(fc.src):
102                    shutil.rmtree(fc.dst)
103                util.copy(fc.src, fc.dst)
104                msg = '  - copied ({}) {} to {}'.format(
105                    version, fc.src, fc.dst)
106                util.print_info(msg)
107
108
109def _get_version(version, debug=False):
110    """
111    Handle switching to the specified version and return None.
112
113    1. Fetch the origin.
114    2. Checkout the specified version.
115    3. Clean the repository before we begin.
116    4. Pull the origin when a branch; _not_ a commit id.
117
118    :param version: A string containing the branch/tag/sha to be exported.
119    :param debug: An optional bool to toggle debug output.
120    :return: None
121    """
122    if not any(
123        (_has_branch(version, debug), _has_tag(version, debug), _has_commit(
124            version, debug))):
125        cmd = sh.git.bake('fetch')
126        util.run_command(cmd, debug=debug)
127    cmd = sh.git.bake('checkout', version)
128    util.run_command(cmd, debug=debug)
129    cmd = sh.git.bake('clean', '-d', '-x', '-f')
130    util.run_command(cmd, debug=debug)
131    if _has_branch(version, debug):
132        cmd = sh.git.bake('pull', rebase=True, ff_only=True)
133        util.run_command(cmd, debug=debug)
134
135
136def _has_commit(version, debug=False):
137    """
138    Determine a version is a local git commit sha or not.
139
140    :param version: A string containing the branch/tag/sha to be determined.
141    :param debug: An optional bool to toggle debug output.
142    :return: bool
143    """
144    if _has_tag(version, debug) or _has_branch(version, debug):
145        return False
146    cmd = sh.git.bake('cat-file', '-e', version)
147    try:
148        util.run_command(cmd, debug=debug)
149        return True
150    except sh.ErrorReturnCode:
151        return False
152
153
154def _has_tag(version, debug=False):
155    """
156    Determine a version is a local git tag name or not.
157
158    :param version: A string containing the branch/tag/sha to be determined.
159    :param debug: An optional bool to toggle debug output.
160    :return: bool
161    """
162    cmd = sh.git.bake('show-ref', '--verify', '--quiet',
163                      "refs/tags/{}".format(version))
164    try:
165        util.run_command(cmd, debug=debug)
166        return True
167    except sh.ErrorReturnCode:
168        return False
169
170
171def _has_branch(version, debug=False):
172    """
173    Determine a version is a local git branch name or not.
174
175    :param version: A string containing the branch/tag/sha to be determined.
176    :param debug: An optional bool to toggle debug output.
177    :return: bool
178    """
179    cmd = sh.git.bake('show-ref', '--verify', '--quiet',
180                      "refs/heads/{}".format(version))
181    try:
182        util.run_command(cmd, debug=debug)
183        return True
184    except sh.ErrorReturnCode:
185        return False
186