← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~wgrant/launchpad/port-to-LaunchpadScript into lp:launchpad

 

William Grant has proposed merging lp:~wgrant/launchpad/port-to-LaunchpadScript into lp:launchpad with lp:~wgrant/launchpad/delete-old-scripts as a prerequisite.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~wgrant/launchpad/port-to-LaunchpadScript/+merge/75892

Port a dozen scripts to LaunchpadScript, removing their direct initZopeless dependency and simplifying their code. A few Soyuz tests needed updating to cope with different locking log messages.

initZopeless' end is nigh.
-- 
https://code.launchpad.net/~wgrant/launchpad/port-to-LaunchpadScript/+merge/75892
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/port-to-LaunchpadScript into lp:launchpad.
=== modified file 'cronscripts/check-teamparticipation.py'
--- cronscripts/check-teamparticipation.py	2011-02-21 00:06:55 +0000
+++ cronscripts/check-teamparticipation.py	2011-09-18 11:00:30 +0000
@@ -20,25 +20,13 @@
 
 import _pythonpath
 
-import optparse
-import sys
+import transaction
 
 from canonical.database.sqlbase import cursor
-from canonical.launchpad.scripts import (
-    execute_zcml_for_scripts, logger_options, logger)
-from canonical.lp import initZopeless
-
-
-if __name__ == '__main__':
-    parser = optparse.OptionParser(
-        description="Check for invalid/missing TeamParticipation entries.")
-    logger_options(parser)
-    options, args = parser.parse_args(sys.argv[1:])
-    log = logger(options, 'check-teamparticipation')
-
-    execute_zcml_for_scripts()
-    ztm = initZopeless()
-
+from lp.services.scripts.base import LaunchpadScript, LaunchpadScriptFailure
+
+
+def check_teamparticipation(log):
     # Check self-participation.
     query = """
         SELECT id, name
@@ -46,7 +34,6 @@
             SELECT person FROM Teamparticipation WHERE person = team
             ) AND merged IS NULL
         """
-    ztm.begin()
     cur = cursor()
     cur.execute(query)
     non_self_participants = cur.fetchall()
@@ -64,13 +51,13 @@
         """)
     circular_references = cur.fetchall()
     if len(circular_references) > 0:
-        log.warn("Circular references found: %s" % circular_references)
-        sys.exit(1)
+        raise LaunchpadScriptFailure(
+            "Circular references found: %s" % circular_references)
 
     # Check if there are any missing/spurious TeamParticipation entries.
     cur.execute("SELECT id FROM Person WHERE teamowner IS NOT NULL")
     team_ids = cur.fetchall()
-    ztm.abort()
+    transaction.abort()
 
     def get_participants(team):
         """Recurse through the team's members to get all its participants."""
@@ -86,7 +73,6 @@
     team_ids = team_ids[50:]
     while batch:
         for [id] in batch:
-            ztm.begin()
             team = Person.get(id)
             expected = get_participants(team)
             found = set(team.allmembers)
@@ -102,6 +88,16 @@
                                    for person in reverse_difference)
                 log.warn("%s (%s): spurious TeamParticipation entries for %s."
                          % (team.name, team.id, people))
-            ztm.abort()
+            transaction.abort()
         batch = team_ids[:50]
         team_ids = team_ids[50:]
+
+
+class CheckTeamParticipationScript(LaunchpadScript):
+    description = "Check for invalid/missing TeamParticipation entries."
+
+    def main(self):
+        check_teamparticipation(self.logger)
+
+if __name__ == '__main__':
+    CheckTeamParticipationScript("check-teamparticipation").run()

=== modified file 'lib/lp/soyuz/doc/archive-override-check.txt'
--- lib/lp/soyuz/doc/archive-override-check.txt	2011-06-09 10:50:25 +0000
+++ lib/lp/soyuz/doc/archive-override-check.txt	2011-09-18 11:00:30 +0000
@@ -31,12 +31,11 @@
   >>> process.returncode
   0
   >>> print stderr
-  DEBUG   Acquiring lock
-  DEBUG   Initializing connection.
+  INFO    Creating lockfile: ...
   DEBUG   Considering: ubuntu/warty/RELEASE/CURRENT.
   DEBUG   ... published sources
   DEBUG   Rolling back any remaining transactions.
-  DEBUG   Releasing lock
+  DEBUG   Removing lock file: ...
   <BLANKLINE>
 
 

=== modified file 'lib/lp/soyuz/doc/buildd-mass-retry.txt'
--- lib/lp/soyuz/doc/buildd-mass-retry.txt	2011-06-09 10:50:25 +0000
+++ lib/lp/soyuz/doc/buildd-mass-retry.txt	2011-09-18 11:00:30 +0000
@@ -37,7 +37,7 @@
   >>> process.returncode
   0
   >>> print stderr
-  DEBUG   Intitialising connection.
+  INFO    Creating lockfile: ...
   INFO    Initializing Build Mass-Retry for 'The Hoary Hedgehog Release/RELEASE'
   INFO    Processing builds in 'Failed to build'
   INFO    Processing builds in 'Dependency wait'
@@ -45,6 +45,7 @@
   INFO    Processing builds in 'Chroot problem'
   INFO    Success.
   INFO    Dry-run.
+  DEBUG   Removing lock file: ...
   <BLANKLINE>
 
 Superseded builds won't be retried; buildd-manager will just skip the build
@@ -74,7 +75,7 @@
   >>> process.returncode
   0
   >>> print stderr
-  DEBUG   Intitialising connection.
+  INFO    Creating lockfile: ...
   INFO    Initializing Build Mass-Retry for 'The Hoary Hedgehog Release/RELEASE'
   INFO    Processing builds in 'Failed to build'
   INFO    Processing builds in 'Dependency wait'
@@ -82,6 +83,7 @@
   INFO    Processing builds in 'Chroot problem'
   INFO    Success.
   INFO    Dry-run.
+  DEBUG   Removing lock file: ...
   <BLANKLINE>
 
   >>> pub.status = PackagePublishingStatus.PUBLISHED
@@ -98,13 +100,14 @@
   >>> process.returncode
   0
   >>> print stderr
-  DEBUG   Intitialising connection.
+  INFO    Creating lockfile: ...
   INFO    Initializing Build Mass-Retry for 'The Hoary Hedgehog Release for hppa (hppa)/RELEASE'
   INFO    Processing builds in 'Failed to build'
   INFO    Processing builds in 'Dependency wait'
   INFO    Processing builds in 'Chroot problem'
   INFO    Success.
   INFO    Dry-run.
+  DEBUG   Removing lock file: ...
   <BLANKLINE>
 
 
@@ -118,9 +121,10 @@
   >>> process.returncode
   0
   >>> print stderr
-  DEBUG   Intitialising connection.
+  INFO    Creating lockfile: ...
   INFO    Initializing Build Mass-Retry for 'The Hoary Hedgehog Release/RELEASE'
   INFO    Processing builds in 'Failed to build'
   INFO    Success.
   INFO    Dry-run.
+  DEBUG   Removing lock file: ...
   <BLANKLINE>

=== modified file 'lib/lp/soyuz/doc/soyuz-upload.txt'
--- lib/lp/soyuz/doc/soyuz-upload.txt	2011-08-09 06:30:01 +0000
+++ lib/lp/soyuz/doc/soyuz-upload.txt	2011-09-18 11:00:30 +0000
@@ -632,8 +632,7 @@
     >>> process.returncode
     0
     >>> print stderr
-    DEBUG   Acquiring lock
-    DEBUG   Initializing connection.
+    INFO    Creating lockfile: ...
     DEBUG   Considering Sources:
     DEBUG   Processing /var/tmp/archive/ubuntutest/dists/breezy-autotest/restricted/source/Sources.gz
     DEBUG   Processing /var/tmp/archive/ubuntutest/dists/breezy-autotest/main/source/Sources.gz
@@ -642,6 +641,7 @@
     DEBUG   Building not build from source list (NBS):
     DEBUG   Building all superseded by any list (ASBA):
     DEBUG   No NBS found
+    DEBUG   Removing lock file: ...
     <BLANKLINE>
 
 

=== modified file 'lib/lp/soyuz/scripts/tests/test_sync_source.py'
--- lib/lp/soyuz/scripts/tests/test_sync_source.py	2011-08-26 00:03:38 +0000
+++ lib/lp/soyuz/scripts/tests/test_sync_source.py	2011-09-18 11:00:30 +0000
@@ -320,7 +320,9 @@
 
         self.assertEqual(
             err.splitlines(),
-            ['W: Could not find blacklist file on '
+            ['INFO    Creating lockfile: '
+             '/var/lock/launchpad-sync-source.lock',
+             'W: Could not find blacklist file on '
              '/srv/launchpad.net/dak/sync-blacklist.txt',
              'INFO      - <bar_1.0-1.diff.gz: cached>',
              'INFO      - <bar_1.0.orig.tar.gz: cached>',
@@ -392,7 +394,9 @@
 
         self.assertEqual(
             err.splitlines(),
-            ['W: Could not find blacklist file on '
+            ['INFO    Creating lockfile: '
+             '/var/lock/launchpad-sync-source.lock',
+             'W: Could not find blacklist file on '
              '/srv/launchpad.net/dak/sync-blacklist.txt',
              'INFO      - <sample1_1.0.orig-component3.tar.gz: cached>',
              'INFO      - <sample1_1.0-1.dsc: cached>',

=== modified file 'scripts/bug-export.py'
--- scripts/bug-export.py	2010-11-08 12:52:43 +0000
+++ scripts/bug-export.py	2011-09-18 11:00:30 +0000
@@ -7,45 +7,47 @@
 import _pythonpath
 
 import sys
-import optparse
+
+import transaction
 
 from zope.component import getUtility
-from canonical.lp import initZopeless
 from lp.registry.interfaces.product import IProductSet
-from canonical.launchpad.scripts import execute_zcml_for_scripts
 
 from lp.bugs.scripts.bugexport import export_bugtasks
-
-def main(argv):
-    parser = optparse.OptionParser(
-        description="Export bugs for a Launchpad product as XML")
-    parser.add_option('-o', '--output', metavar='FILE', action='store',
-                      help='Export bugs to this file',
-                      type='string', dest='output', default=None)
-    parser.add_option('-p', '--product', metavar='PRODUCT', action='store',
-                      help='Which product to export',
-                      type='string', dest='product', default=None)
-    parser.add_option('--include-private', action='store_true',
-                      help='Include private bugs in dump',
-                      dest='include_private', default=False)
-
-    options, args = parser.parse_args(argv[1:])
-
-    if options.product is None:
-        parser.error('No product specified')
-    output = sys.stdout
-    if options.output is not None:
-        output = open(options.output, 'wb')
-
-    execute_zcml_for_scripts()
-    ztm = initZopeless()
-
-    product = getUtility(IProductSet).getByName(options.product)
-    if product is None:
-        parser.error('Product %s does not exist' % options.product)
-
-    export_bugtasks(ztm, product, output,
-                    include_private=options.include_private)
+from lp.services.scripts.base import LaunchpadScript
+
+
+class BugExportScript(LaunchpadScript):
+
+    description = "Export bugs for a Launchpad product as XML"
+
+    def add_my_options(self):
+        self.parser.add_option(
+            '-o', '--output', metavar='FILE', action='store',
+            help='Export bugs to this file', type='string', dest='output')
+        self.parser.add_option(
+            '-p', '--product', metavar='PRODUCT', action='store',
+            help='Which product to export', type='string', dest='product')
+        self.parser.add_option(
+            '--include-private', action='store_true',
+            help='Include private bugs in dump', dest='include_private',
+            default=False)
+
+    def main(self):
+        if self.options.product is None:
+            self.parser.error('No product specified')
+        output = sys.stdout
+        if self.options.output is not None:
+            output = open(self.options.output, 'wb')
+
+        product = getUtility(IProductSet).getByName(self.options.product)
+        if product is None:
+            self.parser.error(
+                'Product %s does not exist' % self.options.product)
+
+        export_bugtasks(
+            transaction, product, output,
+            include_private=self.options.include_private)
 
 if __name__ == '__main__':
-    sys.exit(main(sys.argv))
+    BugExportScript("bug-export").run()

=== modified file 'scripts/ftpmaster-tools/archive-cruft-check.py'
--- scripts/ftpmaster-tools/archive-cruft-check.py	2011-06-09 10:50:25 +0000
+++ scripts/ftpmaster-tools/archive-cruft-check.py	2011-09-18 11:00:30 +0000
@@ -10,70 +10,47 @@
 A kind of archive garbage collector, supersede NBS binaries (not build
 from source).
 """
+
 import _pythonpath
-import optparse
-import sys
 
 from canonical.config import config
-from canonical.launchpad.scripts import (
-    execute_zcml_for_scripts, logger, logger_options)
+from lp.services.scripts.base import LaunchpadScript, LaunchpadScriptFailure
 from lp.soyuz.scripts.ftpmaster import (
     ArchiveCruftChecker, ArchiveCruftCheckerError)
-from canonical.lp import initZopeless
-from contrib.glock import GlobalLock
-
-
-def main():
-    # Parse command-line arguments
-    parser = optparse.OptionParser()
-
-    logger_options(parser)
-
-    parser.add_option(
-        "-d", "--distro", dest="distro", help="remove from DISTRO")
-    parser.add_option(
-        "-n", "--no-action", dest="action", default=True,
-        action="store_false", help="don't do anything")
-    parser.add_option(
-        "-s", "--suite", dest="suite", help="only act on SUITE")
-
-    (options, args) = parser.parse_args()
-
-    log = logger(options, "archive-cruft-check")
-
-    log.debug("Acquiring lock")
-    lock = GlobalLock('/var/lock/launchpad-archive-cruft-check.lock')
-    lock.acquire(blocking=True)
-
-    log.debug("Initializing connection.")
-    execute_zcml_for_scripts()
-    ztm = initZopeless(dbuser=config.archivepublisher.dbuser)
-
-
-    if len(args) > 0:
-        archive_path = args[0]
-    else:
-        log.error('ARCHIVEPATH is require')
-        return 1
-
-    checker = ArchiveCruftChecker(
-        log, distribution_name=options.distro, suite=options.suite,
-        archive_path=archive_path)
-
-    try:
-        checker.initialize()
-    except ArchiveCruftCheckerError, info:
-        log.error(info)
-        return 1
-
-# XXX cprov 2007-06-26 bug=121784: Disabling by distro-team request.
-#    if checker.nbs_to_remove and options.action:
-#        checker.doRemovals()
-#        ztm.commit()
-
-    lock.release()
-    return 0
-
+
+
+class ArchiveCruftCheckerScript(LaunchpadScript):
+
+    usage = "Usage: archive-cruft-check.py [options] <ARCHIVE_PATH>"
+
+    def add_my_options(self):
+        self.parser.add_option(
+            "-d", "--distro", dest="distro", help="remove from DISTRO")
+        self.parser.add_option(
+            "-n", "--no-action", dest="action", default=True,
+            action="store_false", help="don't do anything")
+        self.parser.add_option(
+            "-s", "--suite", dest="suite", help="only act on SUITE")
+
+    def main(self):
+        if len(self.args) != 1:
+            self.parser.error('ARCHIVEPATH is require')
+        archive_path = self.args[0]
+
+        checker = ArchiveCruftChecker(
+            self.logger, distribution_name=self.options.distro,
+            suite=self.options.suite, archive_path=archive_path)
+
+        try:
+            checker.initialize()
+        except ArchiveCruftCheckerError, info:
+            raise LaunchpadScriptFailure(info)
+
+        # XXX cprov 2007-06-26 bug=121784: Disabling by distro-team request.
+        #    if checker.nbs_to_remove and options.action:
+        #        checker.doRemovals()
+        #        ztm.commit()
 
 if __name__ == '__main__':
-    sys.exit(main())
+    ArchiveCruftCheckerScript(
+        'archive-cruft-check', config.archivepublisher.dbuser).lock_and_run()

=== modified file 'scripts/ftpmaster-tools/archive-override-check.py'
--- scripts/ftpmaster-tools/archive-override-check.py	2011-06-09 10:50:25 +0000
+++ scripts/ftpmaster-tools/archive-override-check.py	2011-09-18 11:00:30 +0000
@@ -11,77 +11,55 @@
 
 import _pythonpath
 
-from optparse import OptionParser
-import sys
-
+import transaction
 from zope.component import getUtility
-# Still needed fake import to stop circular imports.
-import canonical.launchpad.interfaces
 
 from canonical.config import config
-from canonical.launchpad.scripts import (
-    execute_zcml_for_scripts, logger, logger_options)
 from lp.app.errors import NotFoundError
-from canonical.lp import initZopeless
 from lp.registry.interfaces.distribution import IDistributionSet
 from lp.registry.interfaces.pocket import PackagePublishingPocket
+from lp.services.scripts.base import LaunchpadScript
 from lp.soyuz.scripts.ftpmaster import PubSourceChecker
 from lp.soyuz.enums import PackagePublishingStatus
 
-from contrib.glock import GlobalLock
-
-def main():
-    # Parse command-line arguments
-    parser = OptionParser()
-    logger_options(parser)
-
-    parser.add_option("-d", "--distribution", action="store",
-                      dest="distribution", metavar="DISTRO", default="ubuntu",
-                      help="Distribution to consider")
-
-    parser.add_option("-s", "--suite", action="store",
-                      dest="suite", metavar="SUITE", default=None,
-                      help=("Suite to consider, if not passed consider the "
-                            "currentseries and the RELEASE pocket"))
-
-    (options, args) = parser.parse_args()
-
-    log = logger(options, "archive-override-check")
-
-    log.debug("Acquiring lock")
-    lock = GlobalLock('/var/lock/archive-override-check.lock')
-    lock.acquire(blocking=True)
-
-    log.debug("Initializing connection.")
-    execute_zcml_for_scripts()
-    ztm = initZopeless(dbuser=config.archivepublisher.dbuser)
-
-    try:
+
+class ArchiveOverrideCheckScript(LaunchpadScript):
+
+    def add_my_options(self):
+        self.parser.add_option(
+            "-d", "--distribution", action="store",
+            dest="distribution", metavar="DISTRO", default="ubuntu",
+            help="Distribution to consider")
+        self.parser.add_option(
+            "-s", "--suite", action="store",
+            dest="suite", metavar="SUITE", default=None,
+            help=("Suite to consider, if not passed consider the "
+                  "currentseries and the RELEASE pocket"))
+
+    def main(self):
         try:
-            distribution = getUtility(IDistributionSet)[options.distribution]
-            if options.suite is None:
-                distroseries = distribution.currentseries
-                pocket = PackagePublishingPocket.RELEASE
-            else:
-                distroseries, pocket = distribution.getDistroSeriesAndPocket(
-                    options.suite)
-
-            log.debug("Considering: %s/%s/%s/%s."
-                      % (distribution.name, distroseries.name, pocket.name,
-                         distroseries.status.name))
-
-            checkOverrides(distroseries, pocket, log)
-
-        except NotFoundError, info:
-            log.error('Not found: %s' % info)
-
-    finally:
-        log.debug("Rolling back any remaining transactions.")
-        ztm.abort()
-        log.debug("Releasing lock")
-        lock.release()
-
-    return 0
+            try:
+                distribution = getUtility(IDistributionSet)[
+                    self.options.distribution]
+                if self.options.suite is None:
+                    distroseries = distribution.currentseries
+                    pocket = PackagePublishingPocket.RELEASE
+                else:
+                    distroseries, pocket = (
+                        distribution.getDistroSeriesAndPocket(
+                            self.options.suite))
+
+                self.logger.debug(
+                    "Considering: %s/%s/%s/%s."
+                    % (distribution.name, distroseries.name, pocket.name,
+                       distroseries.status.name))
+
+                checkOverrides(distroseries, pocket, self.logger)
+            except NotFoundError, info:
+                self.logger.error('Not found: %s' % info)
+        finally:
+            self.logger.debug("Rolling back any remaining transactions.")
+            transaction.abort()
 
 
 def checkOverrides(distroseries, pocket, log):
@@ -116,5 +94,6 @@
             print report
 
 if __name__ == '__main__':
-    sys.exit(main())
-
+    script = ArchiveOverrideCheckScript(
+        'archive-override-check', config.archivepublisher.dbuser)
+    script.lock_and_run()

=== modified file 'scripts/ftpmaster-tools/buildd-mass-retry.py'
--- scripts/ftpmaster-tools/buildd-mass-retry.py	2011-06-09 10:50:25 +0000
+++ scripts/ftpmaster-tools/buildd-mass-retry.py	2011-09-18 11:00:30 +0000
@@ -13,142 +13,127 @@
 
 import _pythonpath
 
-from optparse import OptionParser
-import sys
-
+import transaction
 from zope.component import getUtility
 
-# Still needed fake import to stop circular imports.
-import canonical.launchpad.interfaces
-
-from canonical.database.sqlbase import ISOLATION_LEVEL_READ_COMMITTED
 from lp.app.errors import NotFoundError
-from canonical.launchpad.scripts import (
-    execute_zcml_for_scripts, logger_options, logger)
-from canonical.lp import initZopeless
 from lp.buildmaster.enums import BuildStatus
 from lp.registry.interfaces.distribution import IDistributionSet
 from lp.registry.interfaces.pocket import PackagePublishingPocket
-
-
-def main():
-    parser = OptionParser()
-    logger_options(parser)
-
-    parser.add_option("-d", "--distribution",
-                      dest="distribution", metavar="DISTRIBUTION",
-                      default="ubuntu", help="distribution name")
-
-    parser.add_option("-s", "--suite",
-                      dest="suite", metavar="SUITE", default=None,
-                      help="suite name")
-
-    parser.add_option("-a", "--architecture",
-                      dest="architecture", metavar="ARCH", default=None,
-                      help="architecture tag")
-
-    parser.add_option("-N", "--dry-run", action="store_true",
-                      dest="dryrun", metavar="DRY_RUN", default=False,
-                      help="Whether to treat this as a dry-run or not.")
-
-    parser.add_option("-F", "--failed", action="store_true",
-                      dest="failed", default=False,
-                      help="Reset builds in FAILED state.")
-
-    parser.add_option("-D", "--dep-wait", action="store_true",
-                      dest="depwait", default=False,
-                      help="Reset builds in DEPWAIT state.")
-
-    parser.add_option("-C", "--chroot-wait", action="store_true",
-                      dest="chrootwait", default=False,
-                      help="Reset builds in CHROOTWAIT state.")
-
-    (options, args) = parser.parse_args()
-
-    log = logger(options, "build-mass-retry")
-
-    log.debug("Intitialising connection.")
-    execute_zcml_for_scripts()
-    ztm = initZopeless(dbuser="fiera",
-                       isolation=ISOLATION_LEVEL_READ_COMMITTED)
-
-    try:
-        distribution = getUtility(IDistributionSet)[options.distribution]
-    except NotFoundError, info:
-        log.error("Distribution not found: %s" % info)
-        return 1
-
-    try:
-        if options.suite is not None:
-            series, pocket = distribution.getDistroSeriesAndPocket(
-                options.suite)
+from lp.services.scripts.base import LaunchpadScript, LaunchpadScriptFailure
+
+
+class BuilddMassRetryScript(LaunchpadScript):
+
+    dbuser = "fiera"
+
+    def add_my_options(self):
+        self.parser.add_option(
+            "-d", "--distribution", dest="distribution",
+            metavar="DISTRIBUTION", default="ubuntu",
+            help="distribution name")
+
+        self.parser.add_option(
+            "-s", "--suite", dest="suite", metavar="SUITE", help="suite name")
+
+        self.parser.add_option(
+            "-a", "--architecture", dest="architecture", metavar="ARCH",
+            help="architecture tag")
+
+        self.parser.add_option(
+            "-N", "--dry-run", action="store_true", dest="dryrun",
+            metavar="DRY_RUN", default=False,
+            help="Whether to treat this as a dry-run or not.")
+
+        self.parser.add_option(
+            "-F", "--failed", action="store_true", dest="failed",
+            default=False, help="Reset builds in FAILED state.")
+
+        self.parser.add_option(
+            "-D", "--dep-wait", action="store_true", dest="depwait",
+            default=False, help="Reset builds in DEPWAIT state.")
+
+        self.parser.add_option(
+            "-C", "--chroot-wait", action="store_true", dest="chrootwait",
+            default=False, help="Reset builds in CHROOTWAIT state.")
+
+    def main(self):
+        try:
+            distribution = getUtility(IDistributionSet)[
+                self.options.distribution]
+        except NotFoundError, info:
+            raise LaunchpadScriptFailure("Distribution not found: %s" % info)
+
+        try:
+            if self.options.suite is not None:
+                series, pocket = distribution.getDistroSeriesAndPocket(
+                    self.options.suite)
+            else:
+                series = distribution.currentseries
+                pocket = PackagePublishingPocket.RELEASE
+        except NotFoundError, info:
+            raise LaunchpadScriptFailure("Suite not found: %s" % info)
+
+        # store distroseries as the current IHasBuildRecord provider
+        build_provider = series
+
+        if self.options.architecture:
+            try:
+                dar = series[self.options.architecture]
+            except NotFoundError, info:
+                raise LaunchpadScriptFailure(info)
+
+            # store distroarchseries as the current IHasBuildRecord provider
+            build_provider = dar
+
+        self.logger.info(
+            "Initializing Build Mass-Retry for '%s/%s'"
+            % (build_provider.title, pocket.name))
+
+        requested_states_map = {
+            BuildStatus.FAILEDTOBUILD: self.options.failed,
+            BuildStatus.MANUALDEPWAIT: self.options.depwait,
+            BuildStatus.CHROOTWAIT: self.options.chrootwait,
+            }
+
+        # XXX cprov 2006-08-31: one query per requested state
+        # could organise it in a single one nicely if I have
+        # an empty SQLResult instance, than only iteration + union()
+        # would work.
+        for target_state, requested in requested_states_map.items():
+            if not requested:
+                continue
+
+            self.logger.info("Processing builds in '%s'" % target_state.title)
+            target_builds = build_provider.getBuildRecords(
+                build_state=target_state, pocket=pocket)
+
+            for build in target_builds:
+                # Skip builds for superseded sources; they won't ever
+                # actually build.
+                if not build.current_source_publication:
+                    self.logger.debug(
+                        'Skipping superseded %s (%s)'
+                        % (build.title, build.id))
+                    continue
+
+                if not build.can_be_retried:
+                    self.logger.warn(
+                        'Can not retry %s (%s)' % (build.title, build.id))
+                    continue
+
+                self.logger.info('Retrying %s (%s)' % (build.title, build.id))
+                build.retry()
+
+        self.logger.info("Success.")
+
+        if self.options.dryrun:
+            transaction.abort()
+            self.logger.info('Dry-run.')
         else:
-            series = distribution.currentseries
-            pocket = PackagePublishingPocket.RELEASE
-    except NotFoundError, info:
-        log.error("Suite not found: %s" % info)
-        return 1
-
-    # store distroseries as the current IHasBuildRecord provider
-    build_provider = series
-
-    if options.architecture:
-        try:
-            dar = series[options.architecture]
-        except NotFoundError, info:
-            log.error(info)
-            return 1
-
-        # store distroarchseries as the current IHasBuildRecord provider
-        build_provider = dar
-
-    log.info("Initializing Build Mass-Retry for '%s/%s'"
-              % (build_provider.title, pocket.name))
-
-    requested_states_map = {
-        BuildStatus.FAILEDTOBUILD : options.failed,
-        BuildStatus.MANUALDEPWAIT : options.depwait,
-        BuildStatus.CHROOTWAIT : options.chrootwait,
-        }
-
-    # XXX cprov 2006-08-31: one query per requested state
-    # could organise it in a single one nicely if I have
-    # an empty SQLResult instance, than only iteration + union()
-    # would work.
-    for target_state, requested in requested_states_map.items():
-        if not requested:
-            continue
-
-        log.info("Processing builds in '%s'" % target_state.title)
-        target_builds = build_provider.getBuildRecords(
-            build_state=target_state, pocket=pocket)
-
-        for build in target_builds:
-            # Skip builds for superseded sources; they won't ever
-            # actually build.
-            if not build.current_source_publication:
-                log.debug(
-                    'Skipping superseded %s (%s)' % (build.title, build.id))
-                continue
-
-            if not build.can_be_retried:
-                log.warn('Can not retry %s (%s)' % (build.title, build.id))
-                continue
-
-            log.info('Retrying %s (%s)' % (build.title, build.id))
-            build.retry()
-
-    log.info("Success.")
-
-    if options.dryrun:
-        ztm.abort()
-        log.info('Dry-run.')
-    else:
-        ztm.commit()
-        log.info("Committed")
-
-    return 0
+            transaction.commit()
+            self.logger.info("Committed")
 
 
 if __name__ == '__main__':
-    sys.exit(main())
+    BuilddMassRetryScript('buildd-mass-retry', 'fiera').lock_and_run()

=== modified file 'scripts/ftpmaster-tools/initialize-from-parent.py'
--- scripts/ftpmaster-tools/initialize-from-parent.py	2011-07-22 15:21:19 +0000
+++ scripts/ftpmaster-tools/initialize-from-parent.py	2011-09-18 11:00:30 +0000
@@ -5,102 +5,75 @@
 
 """Initialize a new distroseries from its parent series."""
 
-from optparse import OptionParser
-import sys
-
 import _pythonpath
-from contrib.glock import GlobalLock
+
+import transaction
 from zope.component import getUtility
 
 from canonical.config import config
-from canonical.launchpad.scripts import (
-    execute_zcml_for_scripts,
-    logger,
-    logger_options,
-    )
-from canonical.lp import initZopeless
 from lp.app.errors import NotFoundError
 from lp.registry.interfaces.distribution import IDistributionSet
+from lp.services.scripts.base import LaunchpadScript, LaunchpadScriptFailure
 from lp.soyuz.scripts.initialize_distroseries import (
     InitializationError,
     InitializeDistroSeries,
     )
 
 
-def main():
-    # Parse command-line arguments
-    parser = OptionParser()
-    logger_options(parser)
-
-    parser.add_option("-N", "--dry-run", action="store_true",
-                      dest="dryrun", metavar="DRY_RUN", default=False,
-                      help="Whether to treat this as a dry-run or not.")
-
-    parser.add_option("-d", "--distro", dest="distribution", metavar="DISTRO",
-                      default="ubuntu",
-                      help="Distribution name")
-
-    parser.add_option(
-        "-a", "--arches", dest="arches",
-        help="A comma-seperated list of arches to limit the child "
-        "distroseries to inheriting")
-
-    (options, args) = parser.parse_args()
-
-    log = logger(options, "initialize")
-
-    if len(args) != 1:
-        log.error("Need to be given exactly one non-option argument. "
-                  "Namely the distroseries to initialize.")
-        return 1
-
-    distroseries_name = args[0]
-
-    log.debug("Acquiring lock")
-    lock = GlobalLock('/var/lock/launchpad-initialize.lock')
-    lock.acquire(blocking=True)
-
-    log.debug("Initializing connection.")
-
-    execute_zcml_for_scripts()
-    ztm = initZopeless(dbuser=config.initializedistroseries.dbuser)
-
-    try:
-        # 'ubuntu' is the default option.distribution value
-        distribution = getUtility(IDistributionSet)[options.distribution]
-        distroseries = distribution[distroseries_name]
-    except NotFoundError, info:
-        log.error('%s not found' % info)
-        return 1
-
-    try:
-        log.debug('Check empty mutable queues in parentseries')
-        log.debug('Check for no pending builds in parentseries')
-        log.debug('Copying distroarchseries from parent(s) '
-                      'and setting nominatedarchindep.')
-        arches = ()
-        if options.arches is not None:
-            arches = tuple(options.arches.split(','))
-        ids = InitializeDistroSeries(distroseries, arches=arches)
-        ids.check()
-        log.debug('initializing from parent(s), copying publishing records.')
-        ids.initialize()
-    except InitializationError, e:
-        ztm.abort()
-        log.error(e)
-        return 1
-
-    if options.dryrun:
-        log.debug('Dry-Run mode, transaction aborted.')
-        ztm.abort()
-    else:
-        log.debug('Committing transaction.')
-        ztm.commit()
-
-    log.debug("Releasing lock")
-    lock.release()
-    return 0
+class InitializeFromParentScript(LaunchpadScript):
+
+    usage = "Usage: %prog [options] <SERIES>"
+
+    def add_my_options(self):
+        self.parser.add_option(
+            "-N", "--dry-run", action="store_true",
+            dest="dryrun", metavar="DRY_RUN", default=False,
+            help="Whether to treat this as a dry-run or not.")
+        self.parser.add_option(
+            "-d", "--distro", dest="distribution",
+            metavar="DISTRO", default="ubuntu", help="Distribution name")
+        self.parser.add_option(
+            "-a", "--arches", dest="arches",
+            help="A comma-seperated list of arches to limit the child "
+            "distroseries to inheriting")
+
+    def main(self):
+        if len(self.args) != 1:
+            self.parser.error("SERIES is required")
+
+        distroseries_name = self.args[0]
+
+        try:
+            # 'ubuntu' is the default option.distribution value
+            distribution = getUtility(IDistributionSet)[
+                self.options.distribution]
+            distroseries = distribution[distroseries_name]
+        except NotFoundError, info:
+            raise LaunchpadScriptFailure('%s not found' % info)
+
+        try:
+            arches = ()
+            if self.options.arches is not None:
+                arches = tuple(self.options.arches.split(','))
+            ids = InitializeDistroSeries(distroseries, arches=arches)
+            self.logger.debug("Checking preconditions")
+            ids.check()
+            self.logger.debug(
+                "Initializing from parent(s), copying publishing records.")
+            ids.initialize()
+        except InitializationError, e:
+            transaction.abort()
+            raise LaunchpadScriptFailure(e)
+
+        if self.options.dryrun:
+            self.logger.debug('Dry-Run mode, transaction aborted.')
+            transaction.abort()
+        else:
+            self.logger.debug('Committing transaction.')
+            transaction.commit()
 
 
 if __name__ == '__main__':
-    sys.exit(main())
+    script = InitializeFromParentScript(
+        'initialize-from-parent', config.initializedistroseries.dbuser)
+    script.lock_and_run()

=== modified file 'scripts/ftpmaster-tools/queue'
--- scripts/ftpmaster-tools/queue	2011-06-09 10:50:25 +0000
+++ scripts/ftpmaster-tools/queue	2011-09-18 11:00:30 +0000
@@ -10,115 +10,110 @@
 
 import _pythonpath
 
-import sys
-from optparse import OptionParser
+import transaction
 
 from canonical.config import config
-from canonical.database.sqlbase import ISOLATION_LEVEL_READ_COMMITTED
-from canonical.launchpad.scripts import execute_zcml_for_scripts
-from canonical.lp import initZopeless
-
-from canonical.launchpad.scripts import logger, logger_options
+from lp.services.scripts.base import LaunchpadScript, LaunchpadScriptFailure
 from lp.soyuz.scripts.queue import (
     CommandRunner, CommandRunnerError, name_queue_map)
 
 
-def main():
-    parser = OptionParser()
-    logger_options(parser)
-
-    parser.add_option("-Q", "--queue",
-                      dest="queue_name", metavar="QUEUE", default="new",
-                      help="Which queue to consider")
-
-    parser.add_option("-d", "--distribution",
-                      dest="distribution_name", metavar="DISTRO", default=None,
-                      help="Which distro to look in")
-
-    parser.add_option("-s", "--suite",
-                      dest="suite_name", metavar="DISTRORELEASE",
-                      default=None,
-                      help=("Which distrorelease to look in, defaults "
-                            "to distribution 'currentseries'."))
-
-    parser.add_option("-N", "--dry-run", action="store_true",
-                      dest="dryrun", metavar="DRY_RUN", default=False,
-                      help="Whether to treat this as a dry-run or not.")
-
-    parser.add_option("-M", "--no-mail", action="store_true",
-                      dest="nomail", metavar="NO_MAIL", default=False,
-                      help="Whether to send announce email or not.")
-
-    parser.add_option("-e", "--exact-match", action="store_true",
-                      dest="exact_match", metavar="EXACTMATCH",
-                      default=False,
-                      help="Whether treat filter as a exact match or not.")
-
-    parser.add_option("-i", "--ignore-errors", action="store_true",
-                      dest="ignore_errors", metavar="IGNOREERRORS",
-                      default=False,
-                      help="Ignore errors when performing a list of commands.")
-
-    parser.add_option("-f", "--file", metavar="FILE", default=None,
-                      help="file containing a sequence of command lines.")
-
-    parser.add_option("-c", "--component", dest="component_name",
-                      metavar="COMPONENT", default=None,
-                      help="When overriding, move package to COMPONENT")
-
-    parser.add_option("-x", "--section", dest="section_name",
-                      metavar="SECTION", default=None,
-                      help="When overriding, move package to SECTION")
-
-    parser.add_option("-p", "--priority", dest="priority_name",
-                      metavar="PRIORITY", default=None,
-                      help="When overriding, move package to PRIORITY")
-
-    options, args = parser.parse_args()
-    log = logger(options, "ftpmaster.queue")
-
-    if options.queue_name not in name_queue_map:
-        print 'Unable to map queue name "%s"' % options.queue_name
-        return 1
-
-    no_mail = options.dryrun or options.nomail
-    queue = name_queue_map[options.queue_name]
-
-    if options.file:
-        args_list = [args.strip().split() for args in
-                     open(options.file).readlines()]
-    else:
-        args_list = [args]
-
-    cmd_runner = CommandRunner(
-        queue, options.distribution_name, options.suite_name,
-        no_mail, options.component_name, options.section_name,
-        options.priority_name, log=log)
-
-    print ("Initializing connection to queue %s" % options.queue_name)
-
-    execute_zcml_for_scripts()
-    ztm = initZopeless(dbuser=config.uploadqueue.dbuser,
-                       isolation=ISOLATION_LEVEL_READ_COMMITTED)
-
-    for single_args in args_list:
-        try:
-            cmd_runner.execute(single_args, options.exact_match)
-        except CommandRunnerError, info:
-            print (info)
-            if options.ignore_errors:
-                continue
-            ztm.abort()
-            print 'Aborting current transaction'
-            return 1
+class QueueScript(LaunchpadScript):
+
+    usage = 'Usage: %prog [options] <command>'
+
+    def add_my_options(self):
+        self.parser.add_option(
+            "-Q", "--queue",
+            dest="queue_name", metavar="QUEUE", default="new",
+            help="Which queue to consider")
+
+        self.parser.add_option(
+            "-d", "--distribution",
+            dest="distribution_name", metavar="DISTRO", default=None,
+            help="Which distro to look in")
+
+        self.parser.add_option(
+            "-s", "--suite",
+            dest="suite_name", metavar="DISTRORELEASE", default=None,
+            help=("Which distrorelease to look in, defaults "
+                  "to distribution 'currentseries'."))
+
+        self.parser.add_option(
+            "-N", "--dry-run", action="store_true",
+            dest="dryrun", metavar="DRY_RUN", default=False,
+            help="Whether to treat this as a dry-run or not.")
+
+        self.parser.add_option(
+            "-M", "--no-mail", action="store_true",
+            dest="nomail", metavar="NO_MAIL", default=False,
+            help="Whether to send announce email or not.")
+
+        self.parser.add_option(
+            "-e", "--exact-match", action="store_true",
+            dest="exact_match", metavar="EXACTMATCH", default=False,
+            help="Whether treat filter as a exact match or not.")
+
+        self.parser.add_option(
+            "-i", "--ignore-errors", action="store_true",
+            dest="ignore_errors", metavar="IGNOREERRORS", default=False,
+            help="Ignore errors when performing a list of commands.")
+
+        self.parser.add_option(
+            "-f", "--file", metavar="FILE", default=None,
+            help="file containing a sequence of command lines.")
+
+        self.parser.add_option(
+            "-c", "--component", dest="component_name",
+            metavar="COMPONENT", default=None,
+            help="When overriding, move package to COMPONENT")
+
+        self.parser.add_option(
+            "-x", "--section", dest="section_name",
+            metavar="SECTION", default=None,
+            help="When overriding, move package to SECTION")
+
+        self.parser.add_option(
+            "-p", "--priority", dest="priority_name",
+            metavar="PRIORITY", default=None,
+            help="When overriding, move package to PRIORITY")
+
+    def main(self):
+        if self.options.queue_name not in name_queue_map:
+            self.parser.error(
+                'Unable to map queue name "%s"' % self.options.queue_name)
+
+        no_mail = self.options.dryrun or self.options.nomail
+        queue = name_queue_map[self.options.queue_name]
+
+        if self.options.file:
+            args_list = [self.args.strip().split() for args in
+                         open(self.options.file).readlines()]
         else:
-            if not options.dryrun:
-                ztm.commit()
+            args_list = [self.args]
+
+        cmd_runner = CommandRunner(
+            queue, self.options.distribution_name, self.options.suite_name,
+            no_mail, self.options.component_name, self.options.section_name,
+            self.options.priority_name, log=self.logger)
+
+        print "Initializing connection to queue %s" % self.options.queue_name
+
+        for single_args in args_list:
+            try:
+                cmd_runner.execute(single_args, self.options.exact_match)
+            except CommandRunnerError, info:
+                print (info)
+                if self.options.ignore_errors:
+                    continue
+                transaction.abort()
+                raise LaunchpadScriptFailure(
+                    'Error encountered -- aborting current transaction')
             else:
-                print "DRY RUN requested, not committing."
-
-    return 0
-
+                if not self.options.dryrun:
+                    transaction.commit()
+                else:
+                    print "DRY RUN requested, not committing."
 
 if __name__ == '__main__':
-    sys.exit(main())
+    QueueScript('queue', config.uploadqueue.dbuser).run()

=== modified file 'scripts/ftpmaster-tools/sync-source.py'
--- scripts/ftpmaster-tools/sync-source.py	2011-08-25 16:47:29 +0000
+++ scripts/ftpmaster-tools/sync-source.py	2011-09-18 11:00:30 +0000
@@ -14,9 +14,11 @@
 will become a matter of simply 'publishing' source from Debian unstable
 wherever) into Ubuntu dapper and the whole fake upload trick can go away.
 """
+
+import _pythonpath
+
 import commands
 import errno
-import optparse
 import os
 import re
 import shutil
@@ -25,10 +27,8 @@
 import tempfile
 import urllib
 
-import _pythonpath
 from _syncorigins import origins
 import apt_pkg
-from contrib.glock import GlobalLock
 import dak_utils
 from debian.deb822 import Dsc
 from zope.component import getUtility
@@ -37,13 +37,7 @@
     cursor,
     sqlvalues,
     )
-from canonical.launchpad.scripts import (
-    execute_zcml_for_scripts,
-    logger,
-    logger_options,
-    )
 from canonical.librarian.client import LibrarianClient
-from canonical.lp import initZopeless
 from lp.archiveuploader.utils import (
     DpkgSourceError,
     extract_dpkg_source,
@@ -51,6 +45,7 @@
 from lp.registry.interfaces.distribution import IDistributionSet
 from lp.registry.interfaces.person import IPersonSet
 from lp.registry.interfaces.pocket import PackagePublishingPocket
+from lp.services.scripts.base import LaunchpadScript
 from lp.soyuz.enums import (
     PackagePublishingStatus,
     re_bug_numbers,
@@ -72,7 +67,6 @@
 
 Blacklisted = None
 Library = None
-Lock = None
 Log = None
 Options = None
 
@@ -648,68 +642,6 @@
         print "Total:                    %s" % (stat_count)
 
 
-def options_setup():
-    global Log, Options
-
-    parser = optparse.OptionParser()
-    logger_options(parser)
-    parser.add_option("-a", "--all", dest="all",
-                      default=False, action="store_true",
-                      help="sync all packages")
-    parser.add_option("-b", "--requested-by", dest="requestor",
-                      help="who the sync was requested by")
-    parser.add_option("-f", "--force", dest="force",
-                      default=False, action="store_true",
-                      help="force sync over the top of Ubuntu changes")
-    parser.add_option("-F", "--force-more", dest="forcemore",
-                      default=False, action="store_true",
-                      help="force sync even when components don't match")
-    parser.add_option("-n", "--noaction", dest="action",
-                      default=True, action="store_false",
-                      help="don't do anything")
-
-    # XXX cprov 2007-07-03: Why the heck doesn't -v provide by logger provide
-    # Options.verbose?
-    parser.add_option("-V", "--moreverbose", dest="moreverbose",
-                      default=False, action="store_true",
-                      help="be even more verbose")
-
-    # Options controlling where to sync packages to:
-    parser.add_option("-c", "--to-component", dest="tocomponent",
-                      help="limit syncs to packages in COMPONENT")
-    parser.add_option("-d", "--to-distro", dest="todistro",
-                      default='ubuntu', help="sync to DISTRO")
-    parser.add_option("-s", "--to-suite", dest="tosuite",
-                      help="sync to SUITE (aka distroseries)")
-
-    # Options controlling where to sync packages from:
-    parser.add_option("-C", "--from-component", dest="fromcomponent",
-                      help="sync from COMPONENT")
-    parser.add_option("-D", "--from-distro", dest="fromdistro",
-                      default='debian', help="sync from DISTRO")
-    parser.add_option("-S", "--from-suite", dest="fromsuite",
-                      help="sync from SUITE (aka distroseries)")
-    parser.add_option("-B", "--blacklist", dest="blacklist_path",
-                      default="/srv/launchpad.net/dak/sync-blacklist.txt",
-                      help="Blacklist file path.")
-
-
-    (Options, arguments) = parser.parse_args()
-
-    distro = Options.fromdistro.lower()
-    if not Options.fromcomponent:
-        Options.fromcomponent = origins[distro]["default component"]
-    if not Options.fromsuite:
-        Options.fromsuite = origins[distro]["default suite"]
-
-    # Sanity checks on options
-    if not Options.all and not arguments:
-        dak_utils.fubar(
-            "Need -a/--all or at least one package name as an argument.")
-
-    return arguments
-
-
 def objectize_options():
     """Parse given options.
 
@@ -787,45 +719,77 @@
     return blacklist
 
 
-def init():
-    global Blacklisted, Library, Lock, Log, Options
-
-    apt_pkg.init()
-
-    arguments = options_setup()
-
-    Log = logger(Options, "sync-source")
-
-    Log.debug("Acquiring lock")
-    Lock = GlobalLock('/var/lock/launchpad-sync-source.lock')
-    Lock.acquire(blocking=True)
-
-    Log.debug("Initializing connection.")
-    execute_zcml_for_scripts()
-    initZopeless(dbuser="ro")
-
-    Library = LibrarianClient()
-
-    objectize_options()
-
-    Blacklisted = parseBlacklist(Options.blacklist_path)
-
-    return arguments
-
-
-def main():
-    arguments = init()
-
-    origin = origins[Options.fromdistro]
-    origin["suite"] = Options.fromsuite
-    origin["component"] = Options.fromcomponent
-
-    Sources = read_Sources("Sources", origin)
-    Suite = read_current_source(
-        Options.tosuite, Options.tocomponent, arguments)
-    current_binaries = read_current_binaries(Options.tosuite)
-    do_diff(Sources, Suite, origin, arguments, current_binaries)
+class SyncSourceScript(LaunchpadScript):
+
+    def add_my_options(self):
+        self.parser.add_option("-a", "--all", dest="all",
+                        default=False, action="store_true",
+                        help="sync all packages")
+        self.parser.add_option("-b", "--requested-by", dest="requestor",
+                        help="who the sync was requested by")
+        self.parser.add_option("-f", "--force", dest="force",
+                        default=False, action="store_true",
+                        help="force sync over the top of Ubuntu changes")
+        self.parser.add_option("-F", "--force-more", dest="forcemore",
+                        default=False, action="store_true",
+                        help="force sync even when components don't match")
+        self.parser.add_option("-n", "--noaction", dest="action",
+                        default=True, action="store_false",
+                        help="don't do anything")
+
+        # Options controlling where to sync packages to:
+        self.parser.add_option("-c", "--to-component", dest="tocomponent",
+                        help="limit syncs to packages in COMPONENT")
+        self.parser.add_option("-d", "--to-distro", dest="todistro",
+                        default='ubuntu', help="sync to DISTRO")
+        self.parser.add_option("-s", "--to-suite", dest="tosuite",
+                        help="sync to SUITE (aka distroseries)")
+
+        # Options controlling where to sync packages from:
+        self.parser.add_option("-C", "--from-component", dest="fromcomponent",
+                        help="sync from COMPONENT")
+        self.parser.add_option("-D", "--from-distro", dest="fromdistro",
+                        default='debian', help="sync from DISTRO")
+        self.parser.add_option("-S", "--from-suite", dest="fromsuite",
+                        help="sync from SUITE (aka distroseries)")
+        self.parser.add_option("-B", "--blacklist", dest="blacklist_path",
+                        default="/srv/launchpad.net/dak/sync-blacklist.txt",
+                        help="Blacklist file path.")
+
+    def main(self):
+        global Blacklisted, Library, Log, Options
+
+        Log = self.logger
+        Options = self.options
+
+        distro = Options.fromdistro.lower()
+        if not Options.fromcomponent:
+            Options.fromcomponent = origins[distro]["default component"]
+        if not Options.fromsuite:
+            Options.fromsuite = origins[distro]["default suite"]
+
+        # Sanity checks on options
+        if not Options.all and not self.args:
+            dak_utils.fubar(
+                "Need -a/--all or at least one package name as an argument.")
+
+        apt_pkg.init()
+        Library = LibrarianClient()
+
+        objectize_options()
+
+        Blacklisted = parseBlacklist(Options.blacklist_path)
+
+        origin = origins[Options.fromdistro]
+        origin["suite"] = Options.fromsuite
+        origin["component"] = Options.fromcomponent
+
+        Sources = read_Sources("Sources", origin)
+        Suite = read_current_source(
+            Options.tosuite, Options.tocomponent, self.args)
+        current_binaries = read_current_binaries(Options.tosuite)
+        do_diff(Sources, Suite, origin, self.args, current_binaries)
 
 
 if __name__ == '__main__':
-    main()
+    SyncSourceScript('sync-source', 'ro').lock_and_run()

=== modified file 'utilities/soyuz-sampledata-setup.py'
--- utilities/soyuz-sampledata-setup.py	2011-06-25 14:39:47 +0000
+++ utilities/soyuz-sampledata-setup.py	2011-09-18 11:00:30 +0000
@@ -22,7 +22,6 @@
 
 import _pythonpath
 
-from optparse import OptionParser
 import re
 import os
 import subprocess
@@ -37,11 +36,7 @@
 
 from storm.store import Store
 
-from canonical.lp import initZopeless
-
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
-from canonical.launchpad.scripts import execute_zcml_for_scripts
-from canonical.launchpad.scripts.logger import logger, logger_options
 from canonical.launchpad.webapp.interfaces import (
     IStoreSelector, MAIN_STORE, MASTER_FLAVOR, SLAVE_FLAVOR)
 
@@ -49,6 +44,7 @@
 from lp.registry.interfaces.person import IPersonSet
 from lp.registry.interfaces.series import SeriesStatus
 from lp.registry.model.codeofconduct import SignedCodeOfConduct
+from lp.services.scripts.base import LaunchpadScript
 from lp.soyuz.enums import SourcePackageFormat
 from lp.soyuz.interfaces.component import IComponentSet
 from lp.soyuz.interfaces.processor import IProcessorFamilySet
@@ -109,28 +105,6 @@
             % current_config)
 
 
-def parse_args(arguments):
-    """Parse command-line arguments.
-
-    :return: (options, args, logger)
-    """
-    parser = OptionParser(
-        description="Set up fresh Ubuntu series and %s identity." % user_name)
-    parser.add_option('-f', '--force', action='store_true', dest='force',
-        help="DANGEROUS: run even if the database looks production-like.")
-    parser.add_option('-e', '--email', action='store', dest='email',
-        default=default_email,
-        help=(
-            "Email address to use for %s.  Should match your GPG key."
-            % user_name))
-
-    logger_options(parser)
-
-    options, args = parser.parse_args(arguments)
-
-    return options, args, logger(options)
-
-
 def get_person_set():
     """Return `IPersonSet` utility."""
     return getUtility(IPersonSet)
@@ -374,38 +348,48 @@
         "main restricted universe multiverse\n")
 
 
-def main(argv):
-    options, args, log = parse_args(argv[1:])
-
-    execute_zcml_for_scripts()
-    txn = initZopeless(dbuser='launchpad')
-
-    check_preconditions(options.force)
-
-    ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
-    clean_up(ubuntu, log)
-
-    # Use Hoary as the root, as Breezy and Grumpy are broken.
-    populate(ubuntu, 'hoary', 'ubuntu-team', options, log)
-
-    admin = get_person_set().getByName('name16')
-    person = create_ppa_user(user_name, options, admin, log)
-
-    create_ppa(ubuntu, person, 'test-ppa')
-
-    txn.commit()
-    log.info("Done.")
-
-    print dedent("""
-        Now start your local Launchpad with "make run_codehosting" and log
-        into https://launchpad.dev/ as "%(email)s" with "test" as the
-        password.
-        Your user name will be %(user_name)s."""
-        % {
-            'email': options.email,
-            'user_name': user_name,
-            })
+class SoyuzSampledataSetup(LaunchpadScript):
+
+    description = "Set up fresh Ubuntu series and %s identity." % user_name
+
+    def add_my_options(self):
+        self.parser.add_option(
+            '-f', '--force', action='store_true', dest='force',
+            help="DANGEROUS: run even if the database looks production-like.")
+        self.parser.add_option(
+            '-e', '--email', action='store', dest='email',
+            default=default_email,
+            help=(
+                "Email address to use for %s.  Should match your GPG key."
+                % user_name))
+
+    def main(self):
+        check_preconditions(self.options.force)
+
+        ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
+        clean_up(ubuntu, self.logger)
+
+        # Use Hoary as the root, as Breezy and Grumpy are broken.
+        populate(ubuntu, 'hoary', 'ubuntu-team', self.options, self.logger)
+
+        admin = get_person_set().getByName('name16')
+        person = create_ppa_user(user_name, self.options, admin, self.logger)
+
+        create_ppa(ubuntu, person, 'test-ppa')
+
+        transaction.commit()
+        self.logger.info("Done.")
+
+        print dedent("""
+            Now start your local Launchpad with "make run_codehosting" and log
+            into https://launchpad.dev/ as "%(email)s" with "test" as the
+            password.
+            Your user name will be %(user_name)s."""
+            % {
+                'email': self.options.email,
+                'user_name': user_name,
+                })
 
 
 if __name__ == "__main__":
-    main(sys.argv)
+    SoyuzSampledataSetup('soyuz-sampledata-setup').lock_and_run()