Passed
Pull Request — develop (#185)
by Jace
01:38
created

gitman.commands.delete()   A

Complexity

Conditions 3

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 3

Importance

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