Test Setup Failed
Pull Request — develop (#161)
by
unknown
47s
created

clean_dependencies()   B

Complexity

Conditions 2

Size

Total Lines 27

Duplication

Lines 27
Ratio 100 %

Code Coverage

Tests 10
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
dl 27
loc 27
ccs 10
cts 10
cp 1
crap 2
rs 8.8571
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 config file for the project."""
26 1
    success = False
27
28 1
    config = load_config()
29
30 1
    if config:
31 1
        msg = "Configuration file already exists: {}".format(config.path)
32 1
        common.show(msg, color='error')
33
34
    else:
35 1
        config = Config()
36 1
        source = Source(name="sample_dependency",
37
                        repo="https://github.com/githubtraining/hellogitworld")
38 1
        config.sources.append(source)
39 1
        source = source.lock(rev="ebbbf773431ba07510251bb03f9525c7bab2b13a")
40 1
        config.sources_locked.append(source)
41 1
        config.save()
42
43 1
        msg = "Created sample config file: {}".format(config.path)
44 1
        common.show(msg, color='success')
45 1
        success = True
46
47 1
    msg = "To edit this config file, run: gitman edit"
48 1
    common.show(msg, color='message')
49
50 1
    return success
51
52
53 1
@restore_cwd
54 1
def install(*names, root=None, depth=None,
55
            force=False, fetch=False, clean=True):
56
    """Install dependencies for a project.
57
58
    Optional arguments:
59
60
    - `*names`: optional list of dependency directory names to filter on
61
    - `root`: specifies the path to the root working tree
62
    - `depth`: number of levels of dependencies to traverse
63
    - `force`: indicates uncommitted changes can be overwritten and
64
               script errors can be ignored
65
    - `fetch`: indicates the latest branches should always be fetched
66
    - `clean`: indicates untracked files should be deleted from dependencies
67
68
    """
69 1
    log.info("%sInstalling dependencies: %s",
70
             'force-' if force else '',
71
             ', '.join(names) if names else '<all>')
72 1
    count = None
73
74 1
    config = load_config(root)
75
76 1
    if config:
77 1
        common.newline()
78 1
        common.show("Installing dependencies...", color='message', log=False)
79 1
        common.newline()
80 1
        count = config.install_dependencies(
81
            *names, update=False, depth=depth,
82
            force=force, fetch=fetch, clean=clean,
83
        )
84
85 1
        if count:
86 1
            _run_scripts(*names, depth=depth, force=force, _config=config)
87
88 1
    return _display_result("install", "Installed", count)
89
90
91 1
@restore_cwd
92 1
def update(*names, root=None, depth=None,
93
           recurse=False, force=False, clean=True, lock=None):  # pylint: disable=redefined-outer-name
94
    """Update dependencies for a project.
95
96
    Optional arguments:
97
98
    - `*names`: optional list of dependency directory names to filter on
99
    - `root`: specifies the path to the root working tree
100
    - `depth`: number of levels of dependencies to traverse
101
    - `recurse`: indicates nested dependencies should also be updated
102
    - `force`: indicates uncommitted changes can be overwritten and
103
               script errors can be ignored
104
    - `clean`: indicates untracked files should be deleted from dependencies
105
    - `lock`: indicates actual dependency versions should be recorded
106
107
    """
108 1
    log.info("%s dependencies%s: %s",
109
             'Force updating' if force else 'Updating',
110
             ', recursively' if recurse else '',
111
             ', '.join(names) if names else '<all>')
112 1
    count = None
113
114 1
    config = load_config(root)
115
116 1
    if config:
117 1
        common.newline()
118 1
        common.show("Updating dependencies...", color='message', log=False)
119 1
        common.newline()
120 1
        count = config.install_dependencies(
121
            *names, update=True, depth=depth,
122
            recurse=recurse, force=force, fetch=True, clean=clean,
123
        )
124
125 1
        if count and lock is not False:
126 1
            common.show("Recording installed versions...",
127
                        color='message', log=False)
128 1
            common.newline()
129 1
            config.lock_dependencies(*names, obey_existing=lock is None)
130
131 1
        if count:
132 1
            _run_scripts(*names, depth=depth, force=force, _config=config)
133
134 1
    return _display_result("update", "Updated", count)
135
136
137 1
def _run_scripts(*names, depth=None, force=False, _config=None):
138
    """Run post-install scripts.
139
140
    Optional arguments:
141
142
    - `*names`: optional list of dependency directory names filter on
143
    - `depth`: number of levels of dependencies to traverse
144
    - `force`: indicates script errors can be ignored
145
146
    """
147 1
    assert _config, "'_config' is required"
148
149 1
    common.show("Running scripts...", color='message', log=False)
150 1
    common.newline()
151 1
    _config.run_scripts(*names, depth=depth, force=force)
152
153
154 1
@restore_cwd
155 1
def display(*, root=None, depth=None, allow_dirty=True):
156
    """Display installed dependencies for a project.
157
158
    Optional arguments:
159
160
    - `root`: specifies the path to the root working tree
161
    - `depth`: number of levels of dependencies to traverse
162
    - `allow_dirty`: causes uncommitted changes to be ignored
163
164
    """
165 1
    log.info("Displaying dependencies...")
166 1
    count = None
167
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
    config = load_config(root)
200
201 1
    if config:
202 1
        common.newline()
203 1
        common.show("Locking dependencies...", color='message', log=False)
204 1
        common.newline()
205 1
        count = config.lock_dependencies(*names, obey_existing=False)
206 1
        common.dedent(level=0)
207
208 1
    return _display_result("lock", "Locked", count)
209
210
211 1
@restore_cwd
212 1
def delete(*, root=None, force=False, keep_location=False):
213
    """Delete dependencies for a project.
214
215
    Optional arguments:
216
217
    - `root`: specifies the path to the root working tree
218
    - `force`: indicates uncommitted changes can be overwritten
219
    - `keep_location`: delete top level folder or keep the location
220
221 1
    """
222 1
    log.info("Deleting dependencies...")
223
    count = None
224 1
225
    config = load_config(root)
226 1
227 1
    if config:
228 1
        common.newline()
229
        common.show("Checking for uncommitted changes...",
230 1
                    color='message', log=False)
231 1
        common.newline()
232 1
        count = len(list(config.get_dependencies(allow_dirty=force)))
233 1
        common.dedent(level=0)
234 1
        common.show("Deleting all dependencies...", color='message', log=False)
235 1
        common.newline()
236
        if keep_location:
237 1
            config.clean_dependencies(allow_dirty=force)
238
        else:
239
            config.uninstall_dependencies()
240 1
241
    return _display_result("delete", "Deleted", count, allow_zero=True)
242
243
244
def show(*names, root=None):
245
    """Display the path of an installed dependency or internal file.
246
247 1
    - `name`: dependency name or internal file keyword
248
    - `root`: specifies the path to the root working tree
249 1
250
    """
251 1
    log.info("Finding paths...")
252 1
253 1
    config = load_config(root)
254
255 1
    if not config:
256 1
        log.error("No config found")
257
        return False
258 1
259
    for name in names or [None]:
260
        common.show(config.get_path(name), color='path')
261 1
262
    return True
263
264
265
def edit(*, root=None):
266
    """Open the confuration file for a project.
267
268
    Optional arguments:
269 1
270
    - `root`: specifies the path to the root working tree
271 1
272
    """
273 1
    log.info("Launching config...")
274 1
275 1
    config = load_config(root)
276
277 1
    if not config:
278
        log.error("No config found")
279
        return False
280 1
281
    return system.launch(config.path)
282
283
284
def _display_result(present, past, count, allow_zero=False):
285
    """Convert a command's dependency count to a return status.
286
287
    >>> _display_result("sample", "Sampled", 1)
288
    True
289
290
    >>> _display_result("sample", "Sampled", None)
291
    False
292
293
    >>> _display_result("sample", "Sampled", 0)
294
    False
295
296 1
    >>> _display_result("sample", "Sampled", 0, allow_zero=True)
297 1
    True
298 1
299 1
    """
300
    if count is None:
301 1
        log.warning("No dependencies to %s", present)
302
    elif count == 1:
303 1
        log.info("%s 1 dependency", past)
304 1
    else:
305 1
        log.info("%s %s dependencies", past, count)
306 1
307
    if count:
308 1
        return True
309 1
    elif count is None:
310
        return False
311
312
    assert count == 0
313
    return allow_zero
314