Completed
Pull Request — develop (#148)
by Jace
05:26 queued 01:44
created

_run_scripts()   A

Complexity

Conditions 2

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
dl 0
loc 15
ccs 5
cts 5
cp 1
crap 2
rs 9.4285
c 0
b 0
f 0
1
"""Functions to manage the installation of dependencies."""
2
3 1
import os
4 1
import functools
5 1
import datetime
6 1
import logging
7
8 1
from . import common, system
9 1
from .models import load_config, Config, Source
10
11 1
log = logging.getLogger(__name__)
12
13
14 1
def restore_cwd(func):
15 1
    @functools.wraps(func)
16
    def wrapped(*args, **kwargs):
17 1
        cwd = os.getcwd()
18 1
        result = func(*args, **kwargs)
19 1
        os.chdir(cwd)
20 1
        return result
21 1
    return wrapped
22
23
24 1
def init():
25
    """Create a new configuration file for the project."""
26 1
    success = False
27
28 1
    config = load_config()
29 1
    if config:
30 1
        msg = "Configuration file already exists: {}".format(config.path)
31 1
        common.show(msg, 'error')
32
33
    else:
34 1
        config = Config()
35 1
        source = Source(name="sample_dependency",
36
                        repo="https://github.com/githubtraining/hellogitworld")
37 1
        config.sources.append(source)
38 1
        source = source.lock(rev="ebbbf773431ba07510251bb03f9525c7bab2b13a")
39 1
        config.sources_locked.append(source)
40 1
        config.save()
41
42 1
        msg = "Created sample configuration file: {}".format(config.path)
43 1
        common.show(msg, 'success')
44 1
        success = True
45
46 1
    common.show("To edit this configuration file, run: gitman edit", 'message')
47
48 1
    return success
49
50
51 1
@restore_cwd
52 1
def install(*names, root=None, depth=None,
53
            force=False, fetch=False, clean=True):
54
    """Install dependencies for a project.
55
56
    Optional arguments:
57
58
    - `*names`: optional list of dependency directory names to filter on
59
    - `root`: specifies the path to the root working tree
60
    - `depth`: number of levels of dependencies to traverse
61
    - `force`: indicates uncommitted changes can be overwritten and
62
               script errors can be ignored
63
    - `fetch`: indicates the latest branches should always be fetched
64
    - `clean`: indicates untracked files should be deleted from dependencies
65
66
    """
67 1
    log.info("%sInstalling dependencies: %s",
68
             'force-' if force else '',
69
             ', '.join(names) if names else '<all>')
70 1
    count = None
71
72 1
    root = _find_root(root)
73 1
    config = load_config(root)
74
75 1
    if config:
76 1
        common.newline()
77 1
        common.show("Installing dependencies...", color='message', log=False)
78 1
        common.newline()
79 1
        count = config.install_dependencies(
80
            *names, update=False, depth=depth,
81
            force=force, fetch=fetch, clean=clean,
82
        )
0 ignored issues
show
introduced by
invalid syntax
Loading history...
83
84 1
        if count:
85 1
            _run_scripts(*names, depth=depth, force=force, _config=config)
86
87 1
    return _display_result("install", "Installed", count)
88
89
90 1
@restore_cwd
91 1
def update(*names, root=None, depth=None,
92
           recurse=False, force=False, clean=True, lock=None):  # pylint: disable=redefined-outer-name
93
    """Update dependencies for a project.
94
95
    Optional arguments:
96
97
    - `*names`: optional list of dependency directory names to filter on
98
    - `root`: specifies the path to the root working tree
99
    - `depth`: number of levels of dependencies to traverse
100
    - `recurse`: indicates nested dependencies should also be updated
101
    - `force`: indicates uncommitted changes can be overwritten
102
    - `clean`: indicates untracked files should be deleted from dependencies
103
    - `lock`: indicates actual dependency versions should be recorded
104
105
    """
106 1
    log.info("%s dependencies%s: %s",
107
             'Force updating' if force else 'Updating',
108
             ', recursively' if recurse else '',
109
             ', '.join(names) if names else '<all>')
110 1
    count = None
111
112 1
    root = _find_root(root)
113 1
    config = load_config(root)
114
115 1
    if config:
116 1
        common.newline()
117 1
        common.show("Updating dependencies...", color='message', log=False)
118 1
        common.newline()
119 1
        count = config.install_dependencies(
120
            *names, update=True, depth=depth,
121
            recurse=recurse, force=force, fetch=True, clean=clean,
122
        )
123
124 1
        if count and lock is not False:
125 1
            common.show("Recording installed versions...",
126
                        color='message', log=False)
127 1
            common.newline()
128 1
            config.lock_dependencies(*names, obey_existing=lock is None)
129
130 1
        if count:
131 1
            _run_scripts(*names, depth=depth, force=force, _config=config)
132
133 1
    return _display_result("update", "Updated", count)
134
135
136 1
def _run_scripts(*names, depth=None, force=False, _config=None):
137
    """Run post-install scripts.
138
139
    Optional arguments:
140
141
    - `*names`: optional list of dependency directory names filter on
142
    - `depth`: number of levels of dependencies to traverse
143
    - `force`: indicates script errors can be ignored
144
145
    """
146 1
    assert _config, "'_config' is required"
147
148 1
    common.show("Running scripts...", color='message', log=False)
149 1
    common.newline()
150 1
    _config.run_scripts(*names, depth=depth, force=force)
151
152
153 1
@restore_cwd
154 1
def display(*, root=None, depth=None, allow_dirty=True):
155
    """Display installed dependencies for a project.
156
157
    Optional arguments:
158
159
    - `root`: specifies the path to the root working tree
160
    - `depth`: number of levels of dependencies to traverse
161
    - `allow_dirty`: causes uncommitted changes to be ignored
162
163
    """
164 1
    log.info("Displaying dependencies...")
165 1
    count = None
166
167 1
    root = _find_root(root)
168 1
    config = load_config(root)
169
170 1
    if config:
171 1
        common.newline()
172 1
        common.show("Displaying current dependency versions...",
173
                    color='message', log=False)
174 1
        common.newline()
175 1
        config.log(datetime.datetime.now().strftime("%F %T"))
176 1
        count = 0
177 1
        for identity in config.get_dependencies(depth=depth,
178
                                                allow_dirty=allow_dirty):
179 1
            count += 1
180 1
            config.log("{}: {} @ {}", *identity)
181 1
        config.log()
182
183 1
    return _display_result("display", "Displayed", count)
184
185
186 1
@restore_cwd
187 1
def lock(*names, root=None):
188
    """Lock current dependency versions for a project.
189
190
    Optional arguments:
191
192
    - `*names`: optional list of dependency directory names to filter on
193
    - `root`: specifies the path to the root working tree
194
195
    """
196 1
    log.info("Locking dependencies...")
197 1
    count = None
198
199 1
    root = _find_root(root)
200 1
    config = load_config(root)
201
202 1
    if config:
203 1
        common.newline()
204 1
        common.show("Locking dependencies...", color='message', log=False)
205 1
        common.newline()
206 1
        count = config.lock_dependencies(*names, obey_existing=False)
207 1
        common.dedent(level=0)
208
209 1
    return _display_result("lock", "Locked", count)
210
211
212 1
@restore_cwd
213 1
def delete(*, root=None, force=False):
214
    """Delete dependencies for a project.
215
216
    Optional arguments:
217
218
    - `root`: specifies the path to the root working tree
219
    - `force`: indicates uncommitted changes can be overwritten
220
221
    """
222 1
    log.info("Deleting dependencies...")
223 1
    count = None
224
225 1
    root = _find_root(root)
226 1
    config = load_config(root)
227
228 1
    if config:
229 1
        common.newline()
230 1
        common.show("Checking for uncommitted changes...",
231
                    color='message', log=False)
232 1
        common.newline()
233 1
        count = len(list(config.get_dependencies(allow_dirty=force)))
234 1
        common.dedent(level=0)
235 1
        common.show("Deleting all dependencies...", color='message', log=False)
236 1
        common.newline()
237 1
        config.uninstall_dependencies()
238
239 1
    return _display_result("delete", "Deleted", count, allow_zero=True)
240
241
242 1
def show(*names, root=None):
243
    """Display the path of an installed dependency or internal file.
244
245
    - `name`: dependency name or internal file keyword
246
    - `root`: specifies the path to the root working tree
247
248
    """
249 1
    log.info("Finding paths...")
250
251 1
    root = _find_root(root)
252 1
    config = load_config(root)
253 1
    if not config:
254 1
        log.error("No configuration found")
255 1
        return False
256
257 1
    for name in names or [None]:
258 1
        common.show(config.get_path(name), color='path')
259
260 1
    return True
261
262
263 1
def edit(*, root=None):
264
    """Open the confuration file for a project.
265
266
    Optional arguments:
267
268
    - `root`: specifies the path to the root working tree
269
270
    """
271 1
    log.info("Launching configuration...")
272
273 1
    root = _find_root(root)
274 1
    config = load_config(root)
275 1
    if not config:
276 1
        log.error("No configuration found")
277 1
        return False
278
279 1
    return system.launch(config.path)
280
281
282 1
def _find_root(base, cwd=None):
283 1
    if cwd is None:
284 1
        cwd = os.getcwd()
285 1
        log.info("Current directory: %s", cwd)
286
287 1
    if base:
288 1
        root = os.path.abspath(base)
289 1
        log.info("Specified root: %s", root)
290
291
    else:
292 1
        log.info("Searching for root...")
293 1
        path = cwd
294 1
        prev = None
295 1
        root = None
296 1
        while path != prev:
297 1
            log.debug("Checking path: %s", path)
298 1
            if '.git' in os.listdir(path):
299 1
                root = path
300 1
                break
301 1
            prev = path
302 1
            path = os.path.dirname(path)
303
304 1
        if root:
305 1
            log.info("Found root: %s", root)
306
        else:
307 1
            root = cwd
308 1
            log.warning("No root found, default: %s", root)
309
310 1
    return root
311
312
313 1
def _display_result(present, past, count, allow_zero=False):
314
    """Convert a command's dependency count to a return status.
315
316
    >>> _display_result("sample", "Sampled", 1)
317
    True
318
319
    >>> _display_result("sample", "Sampled", None)
320
    False
321
322
    >>> _display_result("sample", "Sampled", 0)
323
    False
324
325
    >>> _display_result("sample", "Sampled", 0, allow_zero=True)
326
    True
327
328
    """
329 1
    if count is None:
330 1
        log.warning("No dependencies to %s", present)
331 1
    elif count == 1:
332 1
        log.info("%s 1 dependency", past)
333
    else:
334 1
        log.info("%s %s dependencies", past, count)
335
336 1
    if count:
337 1
        return True
338 1
    elif count is None:
339 1
        return False
340
    else:
341 1
        assert count == 0
342
        return allow_zero
343