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