~launchpad-pqm/launchpad/devel

9370.1.2 by Jonathan Lange
Add parser for the config file.
1
# Copyright 2009 Canonical Ltd.  This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
4
"""Tools for maintaining the Launchpad source code."""
5
6
__metaclass__ = type
7
__all__ = [
9370.1.3 by Jonathan Lange
Interpret the configuration file.
8
    'interpret_config',
9370.1.2 by Jonathan Lange
Add parser for the config file.
9
    'parse_config_file',
9370.1.4 by Jonathan Lange
A method for planning the way we are going to update the scripts.
10
    'plan_update',
9370.1.2 by Jonathan Lange
Add parser for the config file.
11
    ]
12
13111.1.1 by Aaron Bentley
Make update-sourcecode fast through caching.
13
import errno
14
import json
9570.6.1 by Michael Hudson
add a --public-only option to update-sourcecode
15
import optparse
9370.1.8 by Jonathan Lange
Find the branches.
16
import os
9370.1.10 by Jonathan Lange
Add a script that updates sourcecode.
17
import shutil
9370.1.17 by Jonathan Lange
Act on comments from mwh
18
import sys
9370.1.8 by Jonathan Lange
Find the branches.
19
14612.2.5 by William Grant
Format the non-contrib bits of lib.
20
from bzrlib import ui
9370.1.13 by Jonathan Lange
Share transports, making the whole thing a little faster.
21
from bzrlib.branch import Branch
14612.2.5 by William Grant
Format the non-contrib bits of lib.
22
from bzrlib.errors import (
23
    BzrError,
24
    IncompatibleRepositories,
25
    NotBranchError,
26
    )
9370.1.12 by Jonathan Lange
Use cmd_pull rather than shelling out.
27
from bzrlib.plugin import load_plugins
9935.1.1 by Gary Poster
update our sourcecode config files to include revision numbers; update our sourcecode updater to use the revision numbers.
28
from bzrlib.revisionspec import RevisionSpec
14612.2.5 by William Grant
Format the non-contrib bits of lib.
29
from bzrlib.trace import (
30
    enable_default_logging,
31
    report_exception,
32
    )
10625.2.1 by Jelmer Vernooij
Automatically upgrade Bazaar branches when encountering IncompatibleRepositories
33
from bzrlib.upgrade import upgrade
9370.1.13 by Jonathan Lange
Share transports, making the whole thing a little faster.
34
from bzrlib.workingtree import WorkingTree
9370.1.8 by Jonathan Lange
Find the branches.
35
9404.1.31 by Jonathan Lange
Move get_launchpad_root to devscripts
36
from devscripts import get_launchpad_root
37
9370.1.2 by Jonathan Lange
Add parser for the config file.
38
39
def parse_config_file(file_handle):
9370.1.3 by Jonathan Lange
Interpret the configuration file.
40
    """Parse the source code config file 'file_handle'.
41
42
    :param file_handle: A file-like object containing sourcecode
43
        configuration.
44
    :return: A sequence of lines of either '[key, value]' or
45
        '[key, value, optional]'.
46
    """
9370.1.2 by Jonathan Lange
Add parser for the config file.
47
    for line in file_handle:
48
        if line.startswith('#'):
49
            continue
9935.1.1 by Gary Poster
update our sourcecode config files to include revision numbers; update our sourcecode updater to use the revision numbers.
50
        yield line.split()
9370.1.3 by Jonathan Lange
Interpret the configuration file.
51
52
53
def interpret_config_entry(entry):
54
    """Interpret a single parsed line from the config file."""
9935.1.1 by Gary Poster
update our sourcecode config files to include revision numbers; update our sourcecode updater to use the revision numbers.
55
    branch_name = entry[0]
56
    components = entry[1].split(';revno=')
57
    branch_url = components[0]
58
    if len(components) == 1:
59
        revision = None
60
    else:
61
        assert len(components) == 2, 'Bad branch URL: ' + entry[1]
62
        revision = components[1] or None
63
    if len(entry) > 2:
64
        assert len(entry) == 3 and entry[2].lower() == 'optional', (
65
            'Bad configuration line: should be space delimited values of '
66
            'sourcecode directory name, branch URL [, "optional"]\n' +
67
            ' '.join(entry))
68
        optional = True
69
    else:
70
        optional = False
71
    return branch_name, branch_url, revision, optional
9570.6.1 by Michael Hudson
add a --public-only option to update-sourcecode
72
13168.7.2 by Aaron Bentley
Fix lint
73
13111.1.1 by Aaron Bentley
Make update-sourcecode fast through caching.
74
def load_cache(cache_filename):
75
    try:
76
        cache_file = open(cache_filename, 'rb')
77
    except IOError as e:
78
        if e.errno == errno.ENOENT:
79
            return {}
80
        else:
81
            raise
82
    with cache_file:
83
        return json.load(cache_file)
9570.6.1 by Michael Hudson
add a --public-only option to update-sourcecode
84
13168.7.2 by Aaron Bentley
Fix lint
85
9570.6.1 by Michael Hudson
add a --public-only option to update-sourcecode
86
def interpret_config(config_entries, public_only):
9370.1.3 by Jonathan Lange
Interpret the configuration file.
87
    """Interpret a configuration stream, as parsed by 'parse_config_file'.
88
89
    :param configuration: A sequence of parsed configuration entries.
9570.6.1 by Michael Hudson
add a --public-only option to update-sourcecode
90
    :param public_only: If true, ignore private/optional branches.
9370.1.3 by Jonathan Lange
Interpret the configuration file.
91
    :return: A dict mapping the names of the sourcecode dependencies to a
92
        2-tuple of their branches and whether or not they are optional.
93
    """
9570.6.1 by Michael Hudson
add a --public-only option to update-sourcecode
94
    config = {}
95
    for entry in config_entries:
9935.1.1 by Gary Poster
update our sourcecode config files to include revision numbers; update our sourcecode updater to use the revision numbers.
96
        branch_name, branch_url, revision, optional = interpret_config_entry(
97
            entry)
9570.6.2 by Michael Hudson
gar, logic is hard
98
        if not optional or not public_only:
9935.1.1 by Gary Poster
update our sourcecode config files to include revision numbers; update our sourcecode updater to use the revision numbers.
99
            config[branch_name] = (branch_url, revision, optional)
9570.6.1 by Michael Hudson
add a --public-only option to update-sourcecode
100
    return config
9370.1.4 by Jonathan Lange
A method for planning the way we are going to update the scripts.
101
102
103
def _subset_dict(d, keys):
104
    """Return a dict that's a subset of 'd', based on the keys in 'keys'."""
105
    return dict((key, d[key]) for key in keys)
106
107
108
def plan_update(existing_branches, configuration):
9370.1.5 by Jonathan Lange
Docs.
109
    """Plan the update to existing branches based on 'configuration'.
110
111
    :param existing_branches: A sequence of branches that already exist.
112
    :param configuration: A dictionary of sourcecode configuration, such as is
113
        returned by `interpret_config`.
114
    :return: (new_branches, update_branches, removed_branches), where
115
        'new_branches' are the branches in the configuration that don't exist
116
        yet, 'update_branches' are the branches in the configuration that do
117
        exist, and 'removed_branches' are the branches that exist locally, but
118
        not in the configuration. 'new_branches' and 'update_branches' are
119
        dicts of the same form as 'configuration', 'removed_branches' is a
120
        set of the same form as 'existing_branches'.
121
    """
9370.1.4 by Jonathan Lange
A method for planning the way we are going to update the scripts.
122
    existing_branches = set(existing_branches)
123
    config_branches = set(configuration.keys())
124
    new_branches = config_branches - existing_branches
125
    removed_branches = existing_branches - config_branches
126
    update_branches = config_branches.intersection(existing_branches)
127
    return (
128
        _subset_dict(configuration, new_branches),
129
        _subset_dict(configuration, update_branches),
130
        removed_branches)
9370.1.8 by Jonathan Lange
Find the branches.
131
132
133
def find_branches(directory):
9370.1.9 by Jonathan Lange
Docstring.
134
    """List the directory names in 'directory' that are branches."""
10271.1.1 by Max Bowsher
update-sourcecode: Locate branches in the root of the sourcecode directory only.
135
    branches = []
136
    for name in os.listdir(directory):
137
        if name in ('.', '..'):
138
            continue
139
        try:
140
            Branch.open(os.path.join(directory, name))
141
            branches.append(name)
142
        except NotBranchError:
143
            pass
144
    return branches
9370.1.10 by Jonathan Lange
Add a script that updates sourcecode.
145
146
9935.1.1 by Gary Poster
update our sourcecode config files to include revision numbers; update our sourcecode updater to use the revision numbers.
147
def get_revision_id(revision, from_branch, tip=False):
9935.1.2 by Gary Poster
respond to flacoste review
148
    """Return revision id for a revision number and a branch.
149
150
    If the revision is empty, the revision_id will be None.
151
152
    If ``tip`` is True, the revision value will be ignored.
153
    """
154
    if not tip and revision:
9935.1.1 by Gary Poster
update our sourcecode config files to include revision numbers; update our sourcecode updater to use the revision numbers.
155
        spec = RevisionSpec.from_string(revision)
156
        return spec.as_revision_id(from_branch)
157
    # else return None
158
159
9935.1.2 by Gary Poster
respond to flacoste review
160
def _format_revision_name(revision, tip=False):
161
    """Formatting helper to return human-readable identifier for revision.
162
163
    If ``tip`` is True, the revision value will be ignored.
164
    """
165
    if not tip and revision:
166
        return 'revision %s' % (revision,)
167
    else:
168
        return 'tip'
169
10625.2.1 by Jelmer Vernooij
Automatically upgrade Bazaar branches when encountering IncompatibleRepositories
170
9370.1.14 by Jonathan Lange
Share transports when getting new branches.
171
def get_branches(sourcecode_directory, new_branches,
10344.3.1 by Bjorn Tillenius
Add a 'quiet' flag to update-sourcecode.py.
172
                 possible_transports=None, tip=False, quiet=False):
9370.1.10 by Jonathan Lange
Add a script that updates sourcecode.
173
    """Get the new branches into sourcecode."""
9935.1.1 by Gary Poster
update our sourcecode config files to include revision numbers; update our sourcecode updater to use the revision numbers.
174
    for project, (branch_url, revision, optional) in new_branches.iteritems():
9370.1.10 by Jonathan Lange
Add a script that updates sourcecode.
175
        destination = os.path.join(sourcecode_directory, project)
9527.1.1 by William Grant
Opening an optional sourcecode is allowed to fail, but nothing else.
176
        try:
177
            remote_branch = Branch.open(
178
                branch_url, possible_transports=possible_transports)
179
        except BzrError:
180
            if optional:
181
                report_exception(sys.exc_info(), sys.stderr)
182
                continue
183
            else:
184
                raise
9370.1.14 by Jonathan Lange
Share transports when getting new branches.
185
        possible_transports.append(
186
            remote_branch.bzrdir.root_transport)
10344.3.1 by Bjorn Tillenius
Add a 'quiet' flag to update-sourcecode.py.
187
        if not quiet:
188
            print 'Getting %s from %s at %s' % (
189
                    project, branch_url, _format_revision_name(revision, tip))
9370.1.17 by Jonathan Lange
Act on comments from mwh
190
        # If the 'optional' flag is set, then it's a branch that shares
191
        # history with Launchpad, so we should share repositories. Otherwise,
192
        # we should avoid sharing repositories to avoid format
193
        # incompatibilities.
194
        force_new_repo = not optional
9935.1.1 by Gary Poster
update our sourcecode config files to include revision numbers; update our sourcecode updater to use the revision numbers.
195
        revision_id = get_revision_id(revision, remote_branch, tip)
9527.1.1 by William Grant
Opening an optional sourcecode is allowed to fail, but nothing else.
196
        remote_branch.bzrdir.sprout(
9935.1.1 by Gary Poster
update our sourcecode config files to include revision numbers; update our sourcecode updater to use the revision numbers.
197
            destination, revision_id=revision_id, create_tree_if_local=True,
9527.1.1 by William Grant
Opening an optional sourcecode is allowed to fail, but nothing else.
198
            source_branch=remote_branch, force_new_repo=force_new_repo,
199
            possible_transports=possible_transports)
9370.1.14 by Jonathan Lange
Share transports when getting new branches.
200
201
13111.1.1 by Aaron Bentley
Make update-sourcecode fast through caching.
202
def find_stale(updated, cache, sourcecode_directory, quiet):
203
    """Find branches whose revision info doesn't match the cache."""
204
    new_updated = dict(updated)
205
    for project, (branch_url, revision, optional) in updated.iteritems():
206
        cache_revision_info = cache.get(project)
207
        if cache_revision_info is None:
208
            continue
209
        if cache_revision_info[0] != int(revision):
210
            continue
211
        destination = os.path.join(sourcecode_directory, project)
212
        try:
213
            branch = Branch.open(destination)
214
        except BzrError:
215
            continue
216
        if list(branch.last_revision_info()) != cache_revision_info:
217
            continue
218
        if not quiet:
219
            print '%s is already up to date.' % project
220
        del new_updated[project]
221
    return new_updated
222
223
224
def update_cache(cache, cache_filename, changed, sourcecode_directory, quiet):
225
    """Update the cache with the changed branches."""
13168.7.1 by Aaron Bentley
Fix cache update detection.
226
    old_cache = dict(cache)
13111.1.1 by Aaron Bentley
Make update-sourcecode fast through caching.
227
    for project, (branch_url, revision, optional) in changed.iteritems():
228
        destination = os.path.join(sourcecode_directory, project)
229
        branch = Branch.open(destination)
13168.7.1 by Aaron Bentley
Fix cache update detection.
230
        cache[project] = list(branch.last_revision_info())
231
    if cache == old_cache:
232
        return
13111.1.1 by Aaron Bentley
Make update-sourcecode fast through caching.
233
    with open(cache_filename, 'wb') as cache_file:
13873.1.2 by Henning Eggers
Sort sourcedeps.cache automatically.
234
        json.dump(cache, cache_file, indent=4, sort_keys=True)
13168.7.1 by Aaron Bentley
Fix cache update detection.
235
    if not quiet:
236
        print 'Cache updated.  Please commit "%s".' % cache_filename
13111.1.1 by Aaron Bentley
Make update-sourcecode fast through caching.
237
238
9370.1.14 by Jonathan Lange
Share transports when getting new branches.
239
def update_branches(sourcecode_directory, update_branches,
10344.3.1 by Bjorn Tillenius
Add a 'quiet' flag to update-sourcecode.py.
240
                    possible_transports=None, tip=False, quiet=False):
9370.1.10 by Jonathan Lange
Add a script that updates sourcecode.
241
    """Update the existing branches in sourcecode."""
9370.1.14 by Jonathan Lange
Share transports when getting new branches.
242
    if possible_transports is None:
243
        possible_transports = []
9370.1.16 by Jonathan Lange
Filter XXX messages for sanity.
244
    # XXX: JonathanLange 2009-11-09: Rather than updating one branch after
245
    # another, we could instead try to get them in parallel.
9935.1.1 by Gary Poster
update our sourcecode config files to include revision numbers; update our sourcecode updater to use the revision numbers.
246
    for project, (branch_url, revision, optional) in (
247
        update_branches.iteritems()):
248
        # Update project from branch_url.
9370.1.10 by Jonathan Lange
Add a script that updates sourcecode.
249
        destination = os.path.join(sourcecode_directory, project)
10344.3.1 by Bjorn Tillenius
Add a 'quiet' flag to update-sourcecode.py.
250
        if not quiet:
251
            print 'Updating %s to %s' % (
252
                    project, _format_revision_name(revision, tip))
9370.1.13 by Jonathan Lange
Share transports, making the whole thing a little faster.
253
        local_tree = WorkingTree.open(destination)
9370.1.17 by Jonathan Lange
Act on comments from mwh
254
        try:
9527.1.1 by William Grant
Opening an optional sourcecode is allowed to fail, but nothing else.
255
            remote_branch = Branch.open(
256
                branch_url, possible_transports=possible_transports)
9370.1.17 by Jonathan Lange
Act on comments from mwh
257
        except BzrError:
258
            if optional:
259
                report_exception(sys.exc_info(), sys.stderr)
9527.1.1 by William Grant
Opening an optional sourcecode is allowed to fail, but nothing else.
260
                continue
9370.1.17 by Jonathan Lange
Act on comments from mwh
261
            else:
262
                raise
9527.1.1 by William Grant
Opening an optional sourcecode is allowed to fail, but nothing else.
263
        possible_transports.append(
264
            remote_branch.bzrdir.root_transport)
9935.1.1 by Gary Poster
update our sourcecode config files to include revision numbers; update our sourcecode updater to use the revision numbers.
265
        revision_id = get_revision_id(revision, remote_branch, tip)
10625.2.1 by Jelmer Vernooij
Automatically upgrade Bazaar branches when encountering IncompatibleRepositories
266
        try:
267
            result = local_tree.pull(
268
                remote_branch, stop_revision=revision_id, overwrite=True,
269
                possible_transports=possible_transports)
270
        except IncompatibleRepositories:
13168.7.2 by Aaron Bentley
Fix lint
271
            # XXX JRV 20100407: Ideally remote_branch.bzrdir._format
10625.2.1 by Jelmer Vernooij
Automatically upgrade Bazaar branches when encountering IncompatibleRepositories
272
            # should be passed into upgrade() to ensure the format is the same
13168.7.2 by Aaron Bentley
Fix lint
273
            # locally and remotely. Unfortunately smart server branches
274
            # have their _format set to RemoteFormat rather than an actual
10625.2.1 by Jelmer Vernooij
Automatically upgrade Bazaar branches when encountering IncompatibleRepositories
275
            # format instance.
276
            upgrade(destination)
277
            # Upgraded, repoen working tree
278
            local_tree = WorkingTree.open(destination)
279
            result = local_tree.pull(
280
                remote_branch, stop_revision=revision_id, overwrite=True,
281
                possible_transports=possible_transports)
282
        if result.old_revid == result.new_revid:
10344.3.1 by Bjorn Tillenius
Add a 'quiet' flag to update-sourcecode.py.
283
            if not quiet:
284
                print '  (No change)'
9935.1.1 by Gary Poster
update our sourcecode config files to include revision numbers; update our sourcecode updater to use the revision numbers.
285
        else:
286
            if result.old_revno < result.new_revno:
287
                change = 'Updated'
288
            else:
289
                change = 'Reverted'
10344.3.1 by Bjorn Tillenius
Add a 'quiet' flag to update-sourcecode.py.
290
            if not quiet:
291
                print '  (%s from %s to %s)' % (
292
                    change, result.old_revno, result.new_revno)
293
294
295
def remove_branches(sourcecode_directory, removed_branches, quiet=False):
9370.1.10 by Jonathan Lange
Add a script that updates sourcecode.
296
    """Remove sourcecode that's no longer there."""
297
    for project in removed_branches:
298
        destination = os.path.join(sourcecode_directory, project)
10344.3.1 by Bjorn Tillenius
Add a 'quiet' flag to update-sourcecode.py.
299
        if not quiet:
300
            print 'Removing %s' % project
9370.1.10 by Jonathan Lange
Add a script that updates sourcecode.
301
        try:
302
            shutil.rmtree(destination)
303
        except OSError:
304
            os.unlink(destination)
305
306
13111.1.1 by Aaron Bentley
Make update-sourcecode fast through caching.
307
def update_sourcecode(sourcecode_directory, config_filename, cache_filename,
308
                      public_only, tip, dry_run, quiet=False):
9370.1.10 by Jonathan Lange
Add a script that updates sourcecode.
309
    """Update the sourcecode."""
310
    config_file = open(config_filename)
9570.6.1 by Michael Hudson
add a --public-only option to update-sourcecode
311
    config = interpret_config(parse_config_file(config_file), public_only)
9370.1.10 by Jonathan Lange
Add a script that updates sourcecode.
312
    config_file.close()
13111.1.1 by Aaron Bentley
Make update-sourcecode fast through caching.
313
    cache = load_cache(cache_filename)
9370.1.10 by Jonathan Lange
Add a script that updates sourcecode.
314
    branches = find_branches(sourcecode_directory)
315
    new, updated, removed = plan_update(branches, config)
9370.1.14 by Jonathan Lange
Share transports when getting new branches.
316
    possible_transports = []
9570.6.2 by Michael Hudson
gar, logic is hard
317
    if dry_run:
318
        print 'Branches to fetch:', new.keys()
319
        print 'Branches to update:', updated.keys()
320
        print 'Branches to remove:', list(removed)
321
    else:
10344.3.1 by Bjorn Tillenius
Add a 'quiet' flag to update-sourcecode.py.
322
        get_branches(
323
            sourcecode_directory, new, possible_transports, tip, quiet)
13111.1.1 by Aaron Bentley
Make update-sourcecode fast through caching.
324
        updated = find_stale(updated, cache, sourcecode_directory, quiet)
10344.3.1 by Bjorn Tillenius
Add a 'quiet' flag to update-sourcecode.py.
325
        update_branches(
326
            sourcecode_directory, updated, possible_transports, tip, quiet)
13111.1.1 by Aaron Bentley
Make update-sourcecode fast through caching.
327
        changed = dict(updated)
328
        changed.update(new)
329
        update_cache(
330
            cache, cache_filename, changed, sourcecode_directory, quiet)
10344.3.1 by Bjorn Tillenius
Add a 'quiet' flag to update-sourcecode.py.
331
        remove_branches(sourcecode_directory, removed, quiet)
9370.1.10 by Jonathan Lange
Add a script that updates sourcecode.
332
333
9370.1.17 by Jonathan Lange
Act on comments from mwh
334
# XXX: JonathanLange 2009-09-11: By default, the script will operate on the
335
# current checkout. Most people only have symlinks to sourcecode in their
336
# checkouts. This is fine for updating, but breaks for removing (you can't
337
# shutil.rmtree a symlink) and breaks for adding, since it adds the new branch
338
# to the checkout, rather than to the shared sourcecode area. Ideally, the
339
# script would see that the sourcecode directory is full of symlinks and then
340
# follow these symlinks to find the shared source directory. If the symlinks
341
# differ from each other (because of developers fiddling with things), we can
342
# take a survey of all of them, and choose the most popular.
343
344
9370.1.10 by Jonathan Lange
Add a script that updates sourcecode.
345
def main(args):
9570.6.1 by Michael Hudson
add a --public-only option to update-sourcecode
346
    parser = optparse.OptionParser("usage: %prog [options] [root [conffile]]")
347
    parser.add_option(
348
        '--public-only', action='store_true',
349
        help='Only fetch/update the public sourcecode branches.')
9570.6.2 by Michael Hudson
gar, logic is hard
350
    parser.add_option(
9935.1.1 by Gary Poster
update our sourcecode config files to include revision numbers; update our sourcecode updater to use the revision numbers.
351
        '--tip', action='store_true',
352
        help='Ignore revision constraints for all branches and pull tip')
353
    parser.add_option(
9570.6.2 by Michael Hudson
gar, logic is hard
354
        '--dry-run', action='store_true',
9935.1.1 by Gary Poster
update our sourcecode config files to include revision numbers; update our sourcecode updater to use the revision numbers.
355
        help='Do nothing, but report what would have been done.')
10344.3.1 by Bjorn Tillenius
Add a 'quiet' flag to update-sourcecode.py.
356
    parser.add_option(
357
        '--quiet', action='store_true',
358
        help="Don't print informational messages.")
9570.6.1 by Michael Hudson
add a --public-only option to update-sourcecode
359
    options, args = parser.parse_args(args)
9370.1.10 by Jonathan Lange
Add a script that updates sourcecode.
360
    root = get_launchpad_root()
361
    if len(args) > 1:
362
        sourcecode_directory = args[1]
363
    else:
364
        sourcecode_directory = os.path.join(root, 'sourcecode')
9370.1.19 by Jonathan Lange
Remove a bunch of shell script.
365
    if len(args) > 2:
366
        config_filename = args[2]
367
    else:
368
        config_filename = os.path.join(root, 'utilities', 'sourcedeps.conf')
13111.1.1 by Aaron Bentley
Make update-sourcecode fast through caching.
369
    cache_filename = os.path.join(
370
        root, 'utilities', 'sourcedeps.cache')
9570.6.1 by Michael Hudson
add a --public-only option to update-sourcecode
371
    if len(args) > 3:
372
        parser.error("Too many arguments.")
10344.3.1 by Bjorn Tillenius
Add a 'quiet' flag to update-sourcecode.py.
373
    if not options.quiet:
374
        print 'Sourcecode: %s' % (sourcecode_directory,)
375
        print 'Config: %s' % (config_filename,)
10271.1.3 by Max Bowsher
update-sourcecode: Call bzrlib.trace.enable_default_logging().
376
    enable_default_logging()
9614.1.2 by Martin Pool
Review tweaks
377
    # Tell bzr to use the terminal (if any) to show progress bars
378
    ui.ui_factory = ui.make_ui_for_terminal(
379
        sys.stdin, sys.stdout, sys.stderr)
9370.1.12 by Jonathan Lange
Use cmd_pull rather than shelling out.
380
    load_plugins()
9570.6.1 by Michael Hudson
add a --public-only option to update-sourcecode
381
    update_sourcecode(
13111.1.1 by Aaron Bentley
Make update-sourcecode fast through caching.
382
        sourcecode_directory, config_filename, cache_filename,
10344.3.1 by Bjorn Tillenius
Add a 'quiet' flag to update-sourcecode.py.
383
        options.public_only, options.tip, options.dry_run, options.quiet)
9370.1.10 by Jonathan Lange
Add a script that updates sourcecode.
384
    return 0