~launchpad-pqm/launchpad/devel

10637.3.1 by Guilherme Salgado
Use the default python version instead of a hard-coded version
1
#!/usr/bin/python -S
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
2
# pylint: disable-msg=W0403
3
10373.3.2 by Jeroen Vermeulen
Integrate cleanup of playground sample data for Soyuz testing.
4
# Copyright 2010 Canonical Ltd.  This software is licensed under the
5
# GNU Affero General Public License version 3 (see the file LICENSE).
6
#
7
# This code is based on William Grant's make-ubuntu-sane.py script, but
8
# reorganized to fit Launchpad coding guidelines, and extended.  The
9
# code is included under Canonical copyright with his permission
10
# (2010-02-24).
11
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
12
"""Clean up sample data so it will allow Soyuz to run locally.
13
14
DO NOT RUN ON PRODUCTION SYSTEMS.  This script deletes lots of
15
Ubuntu-related data.
10373.3.3 by Jeroen Vermeulen
Make the script take over much more of the manual work.
16
17
This script creates a user "ppa-user" (email ppa-user@example.com,
18
password test) who is able to create PPAs.
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
19
"""
20
21
__metaclass__ = type
22
23
import _pythonpath
24
14612.2.6 by William Grant
utilities
25
import os
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
26
import re
10373.3.5 by Jeroen Vermeulen
Automating lots of manual steps for getting Soyuz to work.
27
import subprocess
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
28
import sys
10373.3.4 by Jeroen Vermeulen
Make soyuz sampledata script do more of the work.
29
from textwrap import dedent
14612.2.6 by William Grant
utilities
30
31
from storm.store import Store
10373.3.3 by Jeroen Vermeulen
Make the script take over much more of the manual work.
32
import transaction
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
33
from zope.component import getUtility
34
from zope.event import notify
35
from zope.lifecycleevent import ObjectCreatedEvent
36
from zope.security.proxy import removeSecurityProxy
37
13130.1.21 by Curtis Hovey
Fixed cronscript imports.
38
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
10373.3.3 by Jeroen Vermeulen
Make the script take over much more of the manual work.
39
from lp.registry.interfaces.codeofconduct import ISignedCodeOfConductSet
10427.4.6 by Jeroen Vermeulen
Final polish. I hope.
40
from lp.registry.interfaces.person import IPersonSet
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
41
from lp.registry.interfaces.series import SeriesStatus
10373.3.3 by Jeroen Vermeulen
Make the script take over much more of the manual work.
42
from lp.registry.model.codeofconduct import SignedCodeOfConduct
13970.7.6 by William Grant
SoyuzSampledataSetup is now a LaunchpadScript.
43
from lp.services.scripts.base import LaunchpadScript
14612.2.6 by William Grant
utilities
44
from lp.services.webapp.interfaces import (
45
    IStoreSelector,
46
    MAIN_STORE,
47
    MASTER_FLAVOR,
48
    SLAVE_FLAVOR,
49
    )
11411.6.11 by Julian Edwards
Move SourcePackageFormat
50
from lp.soyuz.enums import SourcePackageFormat
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
51
from lp.soyuz.interfaces.component import IComponentSet
10373.3.3 by Jeroen Vermeulen
Make the script take over much more of the manual work.
52
from lp.soyuz.interfaces.processor import IProcessorFamilySet
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
53
from lp.soyuz.interfaces.section import ISectionSet
10373.3.2 by Jeroen Vermeulen
Integrate cleanup of playground sample data for Soyuz testing.
54
from lp.soyuz.interfaces.sourcepackageformat import (
11411.6.11 by Julian Edwards
Move SourcePackageFormat
55
    ISourcePackageFormatSelectionSet,
56
    )
14612.2.6 by William Grant
utilities
57
from lp.soyuz.model.component import ComponentSelection
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
58
from lp.soyuz.model.section import SectionSelection
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
59
from lp.soyuz.scripts.initialize_distroseries import InitializeDistroSeries
10373.3.3 by Jeroen Vermeulen
Make the script take over much more of the manual work.
60
from lp.testing.factory import LaunchpadObjectFactory
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
61
62
10373.3.5 by Jeroen Vermeulen
Automating lots of manual steps for getting Soyuz to work.
63
user_name = 'ppa-user'
64
default_email = '%s@example.com' % user_name
65
66
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
67
class DoNotRunOnProduction(Exception):
68
    """Error: do not run this script on production (-like) systems."""
69
70
71
def get_max_id(store, table_name):
72
    """Find highest assigned id in given table."""
73
    max_id = store.execute("SELECT max(id) FROM %s" % table_name).get_one()
74
    if max_id is None:
75
        return None
76
    else:
77
        return max_id[0]
78
79
10373.3.11 by Jeroen Vermeulen
Merge devel
80
def get_store(flavor=MASTER_FLAVOR):
81
    """Obtain an ORM store."""
82
    return getUtility(IStoreSelector).get(MAIN_STORE, flavor)
83
84
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
85
def check_preconditions(options):
10373.3.2 by Jeroen Vermeulen
Integrate cleanup of playground sample data for Soyuz testing.
86
    """Try to ensure that it's safe to run.
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
87
88
    This script must not run on a production server, or anything
89
    remotely like it.
90
    """
10373.3.11 by Jeroen Vermeulen
Merge devel
91
    store = get_store(SLAVE_FLAVOR)
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
92
93
    # Just a guess, but dev systems aren't likely to have ids this high
94
    # in this table.  Production data does.
95
    real_data = (get_max_id(store, "TranslationMessage") >= 1000000)
96
    if real_data and not options.force:
97
        raise DoNotRunOnProduction(
98
            "Refusing to delete Ubuntu data unless you --force me.")
99
100
    # For some configs it's just absolutely clear this script shouldn't
101
    # run.  Don't even accept --force there.
102
    forbidden_configs = re.compile('(edge|lpnet|production)')
10373.3.5 by Jeroen Vermeulen
Automating lots of manual steps for getting Soyuz to work.
103
    current_config = os.getenv('LPCONFIG', 'an unknown config')
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
104
    if forbidden_configs.match(current_config):
105
        raise DoNotRunOnProduction(
106
            "I won't delete Ubuntu data on %s and you can't --force me."
107
            % current_config)
108
109
10373.3.3 by Jeroen Vermeulen
Make the script take over much more of the manual work.
110
def get_person_set():
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
111
    """Return `IPersonSet` utility."""
10373.3.3 by Jeroen Vermeulen
Make the script take over much more of the manual work.
112
    return getUtility(IPersonSet)
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
113
114
115
def retire_series(distribution):
116
    """Mark all `DistroSeries` for `distribution` as obsolete."""
117
    for series in distribution.series:
118
        series.status = SeriesStatus.OBSOLETE
119
120
121
def retire_active_publishing_histories(histories, requester):
122
    """Retire all active publishing histories in the given collection."""
123
    # Avoid circular import.
124
    from lp.soyuz.interfaces.publishing import active_publishing_status
125
    for history in histories(status=active_publishing_status):
126
        history.requestDeletion(
127
            requester, "Cleaned up because of missing Librarian files.")
128
129
130
def retire_distro_archives(distribution, culprit):
131
    """Retire all items in `distribution`'s archives."""
132
    for archive in distribution.all_distro_archives:
133
        retire_active_publishing_histories(
134
            archive.getPublishedSources, culprit)
135
        retire_active_publishing_histories(
136
            archive.getAllPublishedBinaries, culprit)
137
138
139
def retire_ppas(distribution):
140
    """Disable all PPAs for `distribution`."""
141
    for ppa in distribution.getAllPPAs():
142
        removeSecurityProxy(ppa).publish = False
143
144
10373.3.3 by Jeroen Vermeulen
Make the script take over much more of the manual work.
145
def add_architecture(distroseries, architecture_name):
146
    """Add a DistroArchSeries for the given architecture to `distroseries`."""
147
    # Avoid circular import.
148
    from lp.soyuz.model.distroarchseries import DistroArchSeries
149
10373.3.11 by Jeroen Vermeulen
Merge devel
150
    store = get_store(MASTER_FLAVOR)
10373.3.3 by Jeroen Vermeulen
Make the script take over much more of the manual work.
151
    family = getUtility(IProcessorFamilySet).getByName(architecture_name)
152
    archseries = DistroArchSeries(
153
        distroseries=distroseries, processorfamily=family,
154
        owner=distroseries.owner, official=True,
155
        architecturetag=architecture_name)
156
    store.add(archseries)
157
158
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
159
def create_sections(distroseries):
160
    """Set up some sections for `distroseries`."""
161
    section_names = (
162
        'admin', 'cli-mono', 'comm', 'database', 'devel', 'debug', 'doc',
163
        'editors', 'electronics', 'embedded', 'fonts', 'games', 'gnome',
164
        'graphics', 'gnu-r', 'gnustep', 'hamradio', 'haskell', 'httpd',
165
        'interpreters', 'java', 'kde', 'kernel', 'libs', 'libdevel', 'lisp',
166
        'localization', 'mail', 'math', 'misc', 'net', 'news', 'ocaml',
167
        'oldlibs', 'otherosfs', 'perl', 'php', 'python', 'ruby', 'science',
168
        'shells', 'sound', 'tex', 'text', 'utils', 'vcs', 'video', 'web',
169
        'x11', 'xfce', 'zope')
170
    store = Store.of(distroseries)
171
    for section_name in section_names:
172
        section = getUtility(ISectionSet).ensure(section_name)
173
        if section not in distroseries.sections:
174
            store.add(
175
                SectionSelection(distroseries=distroseries, section=section))
176
177
178
def create_components(distroseries, uploader):
179
    """Set up some components for `distroseries`."""
180
    component_names = ('main', 'restricted', 'universe', 'multiverse')
181
    store = Store.of(distroseries)
182
    main_archive = distroseries.distribution.main_archive
183
    for component_name in component_names:
184
        component = getUtility(IComponentSet).ensure(component_name)
185
        if component not in distroseries.components:
186
            store.add(
187
                ComponentSelection(
188
                    distroseries=distroseries, component=component))
189
        main_archive.newComponentUploader(uploader, component)
190
        main_archive.newQueueAdmin(uploader, component)
191
192
193
def create_series(parent, full_name, version, status):
194
    """Set up a `DistroSeries`."""
195
    distribution = parent.distribution
7675.1070.6 by Raphael Badin
Fixed tests.
196
    registrant = parent.owner
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
197
    name = full_name.split()[0].lower()
198
    title = "The " + full_name
199
    displayname = full_name.split()[0]
200
    new_series = distribution.newSeries(name=name, title=title,
201
        displayname=displayname, summary='Ubuntu %s is good.' % version,
202
        description='%s is awesome.' % version, version=version,
13045.14.3 by Gavin Panella
Change many more sites from using parent_series to previous_series.
203
        previous_series=None, registrant=registrant)
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
204
    new_series.status = status
205
    notify(ObjectCreatedEvent(new_series))
206
13168.13.30 by Raphael Badin
Setup previous_series properly in tests.
207
    new_series.previous_series = parent
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
208
    ids = InitializeDistroSeries(new_series, [parent.id])
209
    ids.initialize()
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
210
    return new_series
211
212
213
def create_sample_series(original_series, log):
214
    """Set up sample `DistroSeries`.
215
216
    :param original_series: The parent for the first new series to be
217
        created.  The second new series will have the first as a parent,
218
        and so on.
219
    """
220
    series_descriptions = [
221
        ('Dapper Drake', SeriesStatus.SUPPORTED, '6.06'),
222
        ('Edgy Eft', SeriesStatus.OBSOLETE, '6.10'),
223
        ('Feisty Fawn', SeriesStatus.OBSOLETE, '7.04'),
224
        ('Gutsy Gibbon', SeriesStatus.OBSOLETE, '7.10'),
225
        ('Hardy Heron', SeriesStatus.SUPPORTED, '8.04'),
12473.2.2 by William Grant
Add Natty and Onerous, and allow 3.0 formats in Karmic and later.
226
        ('Intrepid Ibex', SeriesStatus.OBSOLETE, '8.10'),
227
        ('Jaunty Jackalope', SeriesStatus.OBSOLETE, '9.04'),
11091.5.2 by Aaron Bentley
Update soyuz sample data
228
        ('Karmic Koala', SeriesStatus.SUPPORTED, '9.10'),
12473.2.2 by William Grant
Add Natty and Onerous, and allow 3.0 formats in Karmic and later.
229
        ('Lucid Lynx', SeriesStatus.SUPPORTED, '10.04'),
230
        ('Maverick Meerkat', SeriesStatus.CURRENT, '10.10'),
231
        ('Natty Narwhal', SeriesStatus.DEVELOPMENT, '11.04'),
232
        ('Onerous Ocelot', SeriesStatus.FUTURE, '11.10'),
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
233
        ]
234
235
    parent = original_series
236
    for full_name, status, version in series_descriptions:
237
        log.info('Creating %s...' % full_name)
238
        parent = create_series(parent, full_name, version, status)
12473.2.2 by William Grant
Add Natty and Onerous, and allow 3.0 formats in Karmic and later.
239
        # Karmic is the first series in which the 3.0 formats are
240
        # allowed. Subsequent series will inherit them.
241
        if version == '9.10':
242
            spfss = getUtility(ISourcePackageFormatSelectionSet)
243
            spfss.add(parent, SourcePackageFormat.FORMAT_3_0_QUILT)
244
            spfss.add(parent, SourcePackageFormat.FORMAT_3_0_NATIVE)
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
245
246
12473.2.3 by William Grant
Add a component to grumpy to placate the publisher.
247
def add_series_component(series):
248
    """Permit a component in the given series."""
249
    component = getUtility(IComponentSet)['main']
250
    get_store(MASTER_FLAVOR).add(
251
        ComponentSelection(
252
            distroseries=series, component=component))
253
254
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
255
def clean_up(distribution, log):
256
    # First we eliminate all active publishings in the Ubuntu main archives.
257
    # None of the librarian files exist, so it kills the publisher.
258
259
    # Could use IPublishingSet.requestDeletion() on the published sources to
260
    # get rid of the binaries too, but I don't trust that there aren't
261
    # published binaries without corresponding sources.
262
263
    log.info("Deleting all items in official archives...")
10373.3.3 by Jeroen Vermeulen
Make the script take over much more of the manual work.
264
    retire_distro_archives(distribution, get_person_set().getByName('name16'))
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
265
266
    # Disable publishing of all PPAs, as they probably have broken
267
    # publishings too.
268
    log.info("Disabling all PPAs...")
269
    retire_ppas(distribution)
270
271
    retire_series(distribution)
272
12473.2.3 by William Grant
Add a component to grumpy to placate the publisher.
273
    # grumpy has no components, which upsets the publisher.
274
    add_series_component(distribution['grumpy'])
275
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
276
10373.3.2 by Jeroen Vermeulen
Integrate cleanup of playground sample data for Soyuz testing.
277
def set_source_package_format(distroseries):
278
    """Register a series' source package format selection."""
279
    utility = getUtility(ISourcePackageFormatSelectionSet)
280
    format = SourcePackageFormat.FORMAT_1_0
281
    if utility.getBySeriesAndFormat(distroseries, format) is None:
282
        utility.add(distroseries, format)
283
284
13045.14.3 by Gavin Panella
Change many more sites from using parent_series to previous_series.
285
def populate(distribution, previous_series_name, uploader_name, options, log):
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
286
    """Set up sample data on `distribution`."""
13045.14.3 by Gavin Panella
Change many more sites from using parent_series to previous_series.
287
    previous_series = distribution.getSeries(previous_series_name)
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
288
289
    log.info("Configuring sections...")
13045.14.3 by Gavin Panella
Change many more sites from using parent_series to previous_series.
290
    create_sections(previous_series)
291
    add_architecture(previous_series, 'amd64')
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
292
293
    log.info("Configuring components and permissions...")
10373.3.3 by Jeroen Vermeulen
Make the script take over much more of the manual work.
294
    uploader = get_person_set().getByName(uploader_name)
13045.14.3 by Gavin Panella
Change many more sites from using parent_series to previous_series.
295
    create_components(previous_series, uploader)
296
297
    set_source_package_format(previous_series)
298
299
    create_sample_series(previous_series, log)
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
300
301
10373.3.4 by Jeroen Vermeulen
Make soyuz sampledata script do more of the work.
302
def sign_code_of_conduct(person, log):
303
    """Sign Ubuntu Code of Conduct for `person`, if necessary."""
304
    if person.is_ubuntu_coc_signer:
305
        # Already signed.
306
        return
307
308
    log.info("Signing Ubuntu code of conduct.")
309
    signedcocset = getUtility(ISignedCodeOfConductSet)
310
    person_id = person.id
311
    if signedcocset.searchByUser(person_id).count() == 0:
312
        fake_gpg_key = LaunchpadObjectFactory().makeGPGKey(person)
313
        Store.of(person).add(SignedCodeOfConduct(
314
            owner=person, signingkey=fake_gpg_key,
315
            signedcode="Normally a signed CoC would go here.", active=True))
316
317
318
def create_ppa_user(username, options, approver, log):
10373.3.3 by Jeroen Vermeulen
Make the script take over much more of the manual work.
319
    """Create new user, with password "test," and sign code of conduct."""
320
    person = get_person_set().getByName(username)
321
    if person is None:
10427.4.2 by Jeroen Vermeulen
Moved user/gpg creation into make-lp-user.
322
        have_email = (options.email != default_email)
323
        command_line = [
12494.1.98 by Gavin Panella
Fix almost all test failures. Most were due to the extra required argument to the InitialiseDistroSeries constructor.
324
            'utilities/make-lp-user', username, 'ubuntu-team']
10427.4.2 by Jeroen Vermeulen
Moved user/gpg creation into make-lp-user.
325
        if have_email:
326
            command_line += ['--email', options.email]
327
328
        pipe = subprocess.Popen(command_line, stderr=subprocess.PIPE)
329
        stdout, stderr = pipe.communicate()
330
        if stderr != '':
331
            print stderr
332
        if pipe.returncode != 0:
333
            sys.exit(2)
334
335
    transaction.commit()
10373.3.3 by Jeroen Vermeulen
Make the script take over much more of the manual work.
336
10427.4.6 by Jeroen Vermeulen
Final polish. I hope.
337
    person = getUtility(IPersonSet).getByName(username)
10373.3.4 by Jeroen Vermeulen
Make soyuz sampledata script do more of the work.
338
    sign_code_of_conduct(person, log)
10373.3.3 by Jeroen Vermeulen
Make the script take over much more of the manual work.
339
10373.3.5 by Jeroen Vermeulen
Automating lots of manual steps for getting Soyuz to work.
340
    return person
341
342
10373.3.10 by Jeroen Vermeulen
Creating PPA as well.
343
def create_ppa(distribution, person, name):
344
    """Create a PPA for `person`."""
10373.3.12 by Jeroen Vermeulen
Set external dependencies.
345
    ppa = LaunchpadObjectFactory().makeArchive(
10373.3.10 by Jeroen Vermeulen
Creating PPA as well.
346
        distribution=distribution, owner=person, name=name, virtualized=False,
347
        description="Automatically created test PPA.")
10373.3.12 by Jeroen Vermeulen
Set external dependencies.
348
    ppa.external_dependencies = (
12473.2.1 by William Grant
An archive's external dependencies can have a series variable. Use it.
349
        "deb http://archive.ubuntu.com/ubuntu %(series)s "
350
        "main restricted universe multiverse\n")
10373.3.12 by Jeroen Vermeulen
Set external dependencies.
351
10373.3.10 by Jeroen Vermeulen
Creating PPA as well.
352
13970.7.6 by William Grant
SoyuzSampledataSetup is now a LaunchpadScript.
353
class SoyuzSampledataSetup(LaunchpadScript):
354
355
    description = "Set up fresh Ubuntu series and %s identity." % user_name
356
357
    def add_my_options(self):
358
        self.parser.add_option(
359
            '-f', '--force', action='store_true', dest='force',
360
            help="DANGEROUS: run even if the database looks production-like.")
361
        self.parser.add_option(
362
            '-e', '--email', action='store', dest='email',
363
            default=default_email,
364
            help=(
365
                "Email address to use for %s.  Should match your GPG key."
366
                % user_name))
367
368
    def main(self):
369
        check_preconditions(self.options.force)
370
371
        ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
372
        clean_up(ubuntu, self.logger)
373
374
        # Use Hoary as the root, as Breezy and Grumpy are broken.
375
        populate(ubuntu, 'hoary', 'ubuntu-team', self.options, self.logger)
376
377
        admin = get_person_set().getByName('name16')
378
        person = create_ppa_user(user_name, self.options, admin, self.logger)
379
380
        create_ppa(ubuntu, person, 'test-ppa')
381
382
        transaction.commit()
383
        self.logger.info("Done.")
384
385
        print dedent("""
386
            Now start your local Launchpad with "make run_codehosting" and log
387
            into https://launchpad.dev/ as "%(email)s" with "test" as the
388
            password.
389
            Your user name will be %(user_name)s."""
390
            % {
391
                'email': self.options.email,
392
                'user_name': user_name,
393
                })
10373.3.5 by Jeroen Vermeulen
Automating lots of manual steps for getting Soyuz to work.
394
10373.3.2 by Jeroen Vermeulen
Integrate cleanup of playground sample data for Soyuz testing.
395
10373.3.1 by Jeroen Vermeulen
Import make-ubuntu-sane.py into the LP tree, with cleanups.
396
if __name__ == "__main__":
13970.7.6 by William Grant
SoyuzSampledataSetup is now a LaunchpadScript.
397
    SoyuzSampledataSetup('soyuz-sampledata-setup').lock_and_run()