From ddab40c44f7d12fb22c6dd278bf36a8d4af252ce Mon Sep 17 00:00:00 2001 From: Michael Bienia Date: Thu, 23 Jul 2009 13:36:05 +0200 Subject: [PATCH 01/12] * lpapiwrapper.py: - Move getUbuntuSourcePackage() from LpApiWrapper to the Archive class and rename it to getSourcePackage(). - Leave LpApiWrapper.getUbuntuSourcePackage() as a shortcut for now. --- ubuntutools/lp/lpapiwrapper.py | 81 ++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 27 deletions(-) diff --git a/ubuntutools/lp/lpapiwrapper.py b/ubuntutools/lp/lpapiwrapper.py index 01073f4..eceef74 100644 --- a/ubuntutools/lp/lpapiwrapper.py +++ b/ubuntutools/lp/lpapiwrapper.py @@ -58,7 +58,6 @@ class LpApiWrapper(object): ubuntu-dev-tools. ''' _me = None - _src_pkg = dict() _upload_comp = dict() _upload_pkg = dict() @@ -86,32 +85,7 @@ class LpApiWrapper(object): Returns a wrapped LP representation of the source package. If the package does not exist: raise PackageNotFoundException ''' - - # Check if pocket has a valid value - if pocket not in ('Release', 'Security', 'Updates', 'Proposed', 'Backports'): - raise PocketDoesNotExist("Pocket '%s' does not exist." % pocket) - - # Check if we have already a LP representation of an Ubuntu series or not - if not isinstance(series, DistroSeries): - series = cls.getUbuntuDistribution().getSeries(series) - - if (name, series, pocket) not in cls._src_pkg: - try: - srcpkg = cls.getUbuntuDistribution().getMainArchive().getPublishedSources( - source_name = name, distro_series = series(), pocket = pocket, - status = 'Published', exact_match = True)[0] - cls._src_pkg[(name, series, pocket)] = SourcePackage(srcpkg) - except IndexError: - if pocket == 'Release': - msg = "The package '%s' does not exist in the Ubuntu main archive in '%s'" % \ - (name, series.name) - else: - msg = "The package '%s' does not exist in the Ubuntu main archive in '%s-%s'" % \ - (name, series.name, pocket.lower()) - - raise PackageNotFoundException(msg) - - return cls._src_pkg[(name, series, pocket)] + return cls.getUbuntuDistribution().getMainArchive().getSourcePackage(name, series, pocket) @classmethod def canUploadPackage(cls, package, series = None): @@ -311,6 +285,59 @@ class Archive(BaseWrapper): ''' resource_type = 'https://api.edge.launchpad.net/beta/#archive' + def __init__(self, *args): + # Don't share _srcpkgs between different Archives + if '_srcpkgs' not in self.__dict__: + self._srcpkgs = dict() + + def getSourcePackage(self, name, series = None, pocket = 'Release'): + ''' + Returns a SourcePackage object for the most recent source package + in the distribution 'dist', series and pocket. + + series defaults to the current development series if not specified. + + If the requested source package doesn't exist a + PackageNotFoundException is raised. + ''' + # Check if pocket has a valid value + if pocket not in ('Release', 'Security', 'Updates', 'Proposed', 'Backports'): + raise PocketDoesNotExist("Pocket '%s' does not exist." % pocket) + + dist = Distribution(self.distribution_link) + # Check if series is already a DistoSeries object or not + if not isinstance(series, DistroSeries): + if series: + series = dist.getSeries(series) + else: + series = dist.getDevelopmentSeries() + + # NOTE: + # For Debian all source publication are in the state 'Pending' so filter on this + # instead of 'Published'. As the result is sorted also by date the first result + # will be the most recent one (i.e. the one we are interested in). + if dist.name in ('debian',): + state = 'Pending' + else: + state = 'Published' + + if (name, series.name, pocket) not in self._srcpkgs: + try: + srcpkg = self.getPublishedSources( + source_name = name, distro_series = series(), pocket = pocket, + status = state, exact_match = True)[0] + self._srcpkgs[(name, series.name, pocket)] = SourcePackage(srcpkg) + except IndexError: + if pocket == 'Release': + msg = "The package '%s' does not exist in the %s %s archive in '%s'" % \ + (name, dist.display_name, self.name, series.name) + else: + msg = "The package '%s' does not exist in the %s %s archive in '%s-%s'" % \ + (name, dist.display_name, self.name, series.name, pocket.lower()) + raise PackageNotFoundException(msg) + + return self._srcpkgs[(name, series.name, pocket)] + class SourcePackage(BaseWrapper): ''' From d062a2f8a61605b030a712a432a92599bc115219 Mon Sep 17 00:00:00 2001 From: Michael Bienia Date: Thu, 23 Jul 2009 14:19:21 +0200 Subject: [PATCH 02/12] * lpapiwrapper.py: - Rename Distribution.getMainArchive() to getArchive() and let it also fetch other archives besides the main one. * udtexceptions.py: - Add ArchiveNotFoundException --- ubuntutools/lp/lpapiwrapper.py | 38 +++++++++++++++++++++++++-------- ubuntutools/lp/udtexceptions.py | 4 ++++ 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/ubuntutools/lp/lpapiwrapper.py b/ubuntutools/lp/lpapiwrapper.py index eceef74..f6e4a78 100644 --- a/ubuntutools/lp/lpapiwrapper.py +++ b/ubuntutools/lp/lpapiwrapper.py @@ -27,7 +27,7 @@ import libsupport from launchpadlib.errors import HTTPError from launchpadlib.resource import Entry -from udtexceptions import PackageNotFoundException, SeriesNotFoundException, PocketDoesNotExist +from udtexceptions import * __all__ = ['LpApiWrapper'] @@ -85,7 +85,7 @@ class LpApiWrapper(object): Returns a wrapped LP representation of the source package. If the package does not exist: raise PackageNotFoundException ''' - return cls.getUbuntuDistribution().getMainArchive().getSourcePackage(name, series, pocket) + return cls.getUbuntuDistribution().getArchive().getSourcePackage(name, series, pocket) @classmethod def canUploadPackage(cls, package, series = None): @@ -115,7 +115,7 @@ class LpApiWrapper(object): if component not in cls._upload_comp and package not in cls._upload_pkg: me = cls.getMe() - archive = cls.getUbuntuDistribution().getMainArchive() + archive = cls.getUbuntuDistribution().getArchive() for perm in archive.getPermissionsForPerson(person = me()): if perm.permission != 'Archive Upload Rights': continue @@ -217,9 +217,11 @@ class Distribution(BaseWrapper): resource_type = 'https://api.edge.launchpad.net/beta/#distribution' def __init__(self, *args): - # Don't share _series between different Distributions + # Don't share _series and _archives between different Distributions if '_series' not in self.__dict__: self._series = dict() + if '_archives' not in self.__dict__: + self._archives = dict() def cache(self): self._cache[self.name] = self @@ -236,13 +238,31 @@ class Distribution(BaseWrapper): cached = Distribution(Launchpad.distributions[dist]) return cached - def getMainArchive(self): + def getArchive(self, archive = None): ''' - Returns the LP representation for the Ubuntu main archive. + Returns an Archive object for the requested archive. + Raises a ArchiveNotFoundException if the archive doesn't exist. + + If 'archive' is None, return the main archive. ''' - if not '_archive' in self.__dict__: - self._archive = Archive(self.main_archive_link) - return self._archive + if archive: + res = self._archives.get(archive) + + if not res: + for a in self.archives: + if a.name == archive: + res = Archive(a) + self._archives[res.name] = res + break + + if res: + return res + else: + raise ArchiveNotFoundException("The Archive '%s' doesn't exist in %s" % (archive, self.display_name)) + else: + if not '_main_archive' in self.__dict__: + self._main_archive = Archive(self.main_archive_link) + return self._main_archive def getSeries(self, name_or_version): ''' diff --git a/ubuntutools/lp/udtexceptions.py b/ubuntutools/lp/udtexceptions.py index 9f3bf05..30b6c25 100644 --- a/ubuntutools/lp/udtexceptions.py +++ b/ubuntutools/lp/udtexceptions.py @@ -9,3 +9,7 @@ class SeriesNotFoundException(BaseException): class PocketDoesNotExist(BaseException): """ Thrown when a invalid pocket is passed """ pass + +class ArchiveNotFoundException(BaseException): + """ Thrown when an archive for a distibution is not found """ + pass From 3bcda69839425ba8f9c6a91615381c9477687f1c Mon Sep 17 00:00:00 2001 From: Michael Bienia Date: Thu, 23 Jul 2009 14:21:42 +0200 Subject: [PATCH 03/12] * udtexceptions.py - Rename PocketDoesNotExist to PocketDoesNotExistException to be in line with the naming of the other exceptions. --- ubuntutools/lp/lpapiwrapper.py | 2 +- ubuntutools/lp/udtexceptions.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ubuntutools/lp/lpapiwrapper.py b/ubuntutools/lp/lpapiwrapper.py index f6e4a78..dfec6bb 100644 --- a/ubuntutools/lp/lpapiwrapper.py +++ b/ubuntutools/lp/lpapiwrapper.py @@ -322,7 +322,7 @@ class Archive(BaseWrapper): ''' # Check if pocket has a valid value if pocket not in ('Release', 'Security', 'Updates', 'Proposed', 'Backports'): - raise PocketDoesNotExist("Pocket '%s' does not exist." % pocket) + raise PocketDoesNotExistException("Pocket '%s' does not exist." % pocket) dist = Distribution(self.distribution_link) # Check if series is already a DistoSeries object or not diff --git a/ubuntutools/lp/udtexceptions.py b/ubuntutools/lp/udtexceptions.py index 30b6c25..5864d09 100644 --- a/ubuntutools/lp/udtexceptions.py +++ b/ubuntutools/lp/udtexceptions.py @@ -6,7 +6,7 @@ class SeriesNotFoundException(BaseException): """ Thrown when a distroseries is not found """ pass -class PocketDoesNotExist(BaseException): +class PocketDoesNotExistException(BaseException): """ Thrown when a invalid pocket is passed """ pass From 3fb1ca0b7c5c3dcd4603e302f5ed5465c59d7702 Mon Sep 17 00:00:00 2001 From: Michael Bienia Date: Fri, 24 Jul 2009 16:52:18 +0200 Subject: [PATCH 04/12] lpapiwrapper.py: - Move LpApiWrapper.canUploadPackage() and LpApiWrapper.isPerPackageUploader() to PersonTeam. Leave stubs for now in LpApiWrapper. --- ubuntutools/lp/lpapiwrapper.py | 114 +++++++++++++++++++++++---------- 1 file changed, 79 insertions(+), 35 deletions(-) diff --git a/ubuntutools/lp/lpapiwrapper.py b/ubuntutools/lp/lpapiwrapper.py index dfec6bb..4a09aa0 100644 --- a/ubuntutools/lp/lpapiwrapper.py +++ b/ubuntutools/lp/lpapiwrapper.py @@ -58,8 +58,6 @@ class LpApiWrapper(object): ubuntu-dev-tools. ''' _me = None - _upload_comp = dict() - _upload_pkg = dict() @classmethod def getMe(cls): @@ -88,48 +86,33 @@ class LpApiWrapper(object): return cls.getUbuntuDistribution().getArchive().getSourcePackage(name, series, pocket) @classmethod - def canUploadPackage(cls, package, series = None): + def canUploadPackage(cls, srcpkg, series = None): ''' Check if the currently authenticated LP user has upload rights for package either through component upload rights or per-package upload rights. - 'package' can either be a wrapped LP representation of a source - package or a string and an Ubuntu series. If 'package' doesn't - exist yet in Ubuntu assume 'universe' for component. + 'package' can either be a SourcePackage object or a string and + an Ubuntu series. If 'package' doesn't exist yet in Ubuntu + assume 'universe' for component. ''' + component = 'universe' + archive = cls.getUbuntuDistribution().getArchive() - if isinstance(package, SourcePackage): - component = package.getComponent() - package = package.getPackageName() + if isinstance(srcpkg, SourcePackage): + package = srcpkg.getPackageName() + component = srcpkg.getComponent() else: if not series: - # Fall-back to current Ubuntu development series series = cls.getUbuntuDistribution().getDevelopmentSeries() - try: - component = cls.getUbuntuSourcePackage(package, series).getComponent() + srcpkg = archive.getSourcePackage(srcpkg, series) + package = srcpkg.getPackageName() + component = srcpkg.getComponent() except PackageNotFoundException: - # Probably a new package, assume "universe" as component - component = 'universe' + package = None - if component not in cls._upload_comp and package not in cls._upload_pkg: - me = cls.getMe() - archive = cls.getUbuntuDistribution().getArchive() - for perm in archive.getPermissionsForPerson(person = me()): - if perm.permission != 'Archive Upload Rights': - continue - if perm.component_name == component: - cls._upload_comp[component] = True - return True - if perm.source_package_name == package: - cls._upload_pkg[package] = True - return True - return False - elif component in cls._upload_comp: - return cls._upload_comp[component] - else: - return cls._upload_pkg[package] + return cls.getMe().canUploadPackage(archive, package, component) # TODO: check if this is still needed after ArchiveReorg (or at all) @classmethod @@ -138,11 +121,11 @@ class LpApiWrapper(object): Check if the user has PerPackageUpload rights for package. ''' if isinstance(package, SourcePackage): - pkg = package.getPackageName() - else: - pkg = package + package = package.getPackageName() - return cls.canUploadPackage(package, series) and pkg in cls._upload_pkg + archive = cls.getUbuntuDistribution().getArchive() + + return cls.getMe().canUploadPackage(archive, package, None) class MetaWrapper(type): @@ -390,6 +373,13 @@ class PersonTeam(BaseWrapper): ''' resource_type = ('https://api.edge.launchpad.net/beta/#person', 'https://api.edge.launchpad.net/beta/#team') + def __init__(self, *args): + # Don't share _upload_{pkg,comp} between different PersonTeams + if '_upload_pkg' not in self.__dict__: + self._upload_pkg = dict() + if '_upload_comp' not in self.__dict__: + self._upload_comp = dict() + def __str__(self): return '%s (%s)' % (self.display_name, self.name) @@ -415,3 +405,57 @@ class PersonTeam(BaseWrapper): Returns True if the user is a member of the team otherwise False. ''' return any(t.name == team for t in self.super_teams) + + def canUploadPackage(self, archive, package, component): + ''' + Check if the person or team has upload rights for the source package + to the specified 'archive' either through component upload + rights or per-package upload rights. + Either a source package name or a component has the specified. + + 'archive' has to be a Archive object. + ''' + if not isinstance(archive, Archive): + raise TypeError("'%r' is not an Archive object." % archive) + if not isinstance(package, (str, None)): + raise TypeError('A source package name expected.') + if not isinstance(component, (str, None)): + raise TypeError('A component name expected.') + if not package and not component: + raise ValueError('Either a source package name or a component has to be specified.') + + upload_comp = self._upload_comp.get((archive, component)) + upload_pkg = self._upload_pkg.get((archive, package)) + + if upload_comp == None and upload_pkg == None: + for perm in archive.getPermissionsForPerson(person = self()): + if perm.permission != 'Archive Upload Rights': + continue + if component and perm.component_name == component: + self._upload_comp[(archive, component)] = True + return True + if package and perm.source_package_name == package: + self._upload_pkg[(archive, package)] = True + return True + # don't have upload rights + if package: + self._upload_pkg[(archive, package)] = False + if component: + self._upload_comp[(archive, component)] = False + return False + else: + return upload_comp or upload_pkg + + # TODO: check if this is still needed after ArchiveReorg (or at all) + def isPerPackageUploader(self, archive, package): + ''' + Check if the user has PerPackageUpload rights for package. + ''' + if isinstance(package, SourcePackage): + pkg = package.getPackageName() + comp = package.getComponent() + else: + pkg = package + compon + + return self.canUploadPackage(archive, pkg, None) From ce84101f73ff5b79518e62425d908ebad65e3c56 Mon Sep 17 00:00:00 2001 From: Michael Bienia Date: Sat, 25 Jul 2009 14:04:25 +0200 Subject: [PATCH 05/12] lpapiwrapper.py: Let PersonTeam.__str__() return a unicode string. --- ubuntutools/lp/lpapiwrapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubuntutools/lp/lpapiwrapper.py b/ubuntutools/lp/lpapiwrapper.py index 4a09aa0..f77bf7f 100644 --- a/ubuntutools/lp/lpapiwrapper.py +++ b/ubuntutools/lp/lpapiwrapper.py @@ -381,7 +381,7 @@ class PersonTeam(BaseWrapper): self._upload_comp = dict() def __str__(self): - return '%s (%s)' % (self.display_name, self.name) + return u'%s (%s)' % (self.display_name, self.name) def cache(self): self._cache[self.name] = self From f3e8c0ee84a2940efb65949289f4ce811533a896 Mon Sep 17 00:00:00 2001 From: Michael Bienia Date: Sat, 25 Jul 2009 16:00:01 +0200 Subject: [PATCH 06/12] lpapiwrapper.py: Added methods needed for buildd. --- ubuntutools/lp/lpapiwrapper.py | 77 ++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/ubuntutools/lp/lpapiwrapper.py b/ubuntutools/lp/lpapiwrapper.py index 4a09aa0..162ef85 100644 --- a/ubuntutools/lp/lpapiwrapper.py +++ b/ubuntutools/lp/lpapiwrapper.py @@ -348,6 +348,11 @@ class SourcePackage(BaseWrapper): ''' resource_type = 'https://api.edge.launchpad.net/beta/#source_package_publishing_history' + def __init__(self, *args): + # Don't share _builds between different SourcePackages + if '_builds' not in self.__dict__: + self._builds = dict() + def getPackageName(self): ''' Returns the source package name. @@ -366,6 +371,57 @@ class SourcePackage(BaseWrapper): ''' return self._lpobject.component_name + def _fetch_builds(self): + '''Populate self._builds with the build records.''' + builds = self.getBuilds() + for build in builds: + self._builds[build.arch_tag] = Build(build) + + def getBuildStates(self, archs): + res = list() + + if not self._builds: + self._fetch_builds() + + for arch in archs: + build = self._builds.get(arch) + if build: + res.append(' %s' % build) + return "Build state(s) for '%s':\n%s" % ( + self.getPackageName(), '\n'.join(res)) + + def rescoreBuilds(self, archs, score): + res = list() + + if not self._builds: + self._fetch_builds() + + for arch in archs: + build = self._builds.get(arch) + if build: + if build.rescore(score): + res.append(' %s: done' % arch) + else: + res.append(' %s: failed' % arch) + return "Rescoring builds of '%s' to %i:\n%s" % ( + self.getPackageName(), score, '\n'.join(res)) + + def retryBuilds(self, archs): + res = list() + + if not self._builds: + self._fetch_builds() + + for arch in archs: + build = self._builds.get(arch) + if build: + if build.retry(): + res.append(' %s: done' % arch) + else: + res.append(' %s: failed' % arch) + return "Retrying builds of '%s':\n%s" % ( + self.PackageName(), '\n'.join(res)) + class PersonTeam(BaseWrapper): ''' @@ -459,3 +515,24 @@ class PersonTeam(BaseWrapper): compon return self.canUploadPackage(archive, pkg, None) + +class Build(BaseWrapper): + ''' + Wrapper class around a build object. + ''' + resource_type = 'https://api.edge.launchpad.net/beta/#build' + + def __str__(self): + return u'%s: %s' % (self.arch_tag, self.buildstate) + + def rescore(self, score): + if self.can_be_rescored: + self.rescore(score = score) + return True + return False + + def retry(self): + if self.can_be_tried: + self.retry() + return True + return False From cdb12929637626a3ee3fa011a68dd2c948de2351 Mon Sep 17 00:00:00 2001 From: Michael Bienia Date: Sat, 25 Jul 2009 17:03:14 +0200 Subject: [PATCH 07/12] - lpapiwrapper.py: fix typos - buildd: add options for the new mode (v2) --- buildd | 18 ++++++++++++++++++ ubuntutools/lp/lpapiwrapper.py | 8 ++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/buildd b/buildd index 2a30d44..6379dab 100755 --- a/buildd +++ b/buildd @@ -49,8 +49,26 @@ retryRescoreOptions.add_option("-a", "--arch", type = "string", "Valid architectures include: " \ "%s." % ", ".join(validArchs)) +# A new calling interface (v2) +v2options = OptionGroup(optParser, "Extended calling convention", + "These options and parameter ordering is only available in the --v2 mode.\n" + "Usage: buildd --v2 [options] --series=SERIES ...") +v2options.add_option('--v2', action = 'store_true', dest = 'v2mode', default = False, + help = 'Enable the new (v2) mode') +v2options.add_option('--series', action = 'store', dest = 'series', type = 'string', + help = 'Selects the Ubuntu series to operate on.') +v2options.add_option('--retry', action = 'store_true', dest = 'retry', default = False, + help = 'Retry builds (give-back).') +v2options.add_option('--rescore', action = 'store', dest = 'priority', type = 'int', + help = 'Rescore builds to .') +v2options.add_option('--arch2', action = 'append', dest = 'architecture', type = 'string', + help = "Affect only 'architecture' (can be used several times). " + "Valid architectures include: %s." % ', '.join(validArchs)) + # Add the retry options to the main group. optParser.add_option_group(retryRescoreOptions) +# Add the new v2 mode to the main group. +optParser.add_option_group(v2options) # Parse our options. (options, args) = optParser.parse_args() diff --git a/ubuntutools/lp/lpapiwrapper.py b/ubuntutools/lp/lpapiwrapper.py index c5ed5b6..7e0489c 100644 --- a/ubuntutools/lp/lpapiwrapper.py +++ b/ubuntutools/lp/lpapiwrapper.py @@ -420,7 +420,7 @@ class SourcePackage(BaseWrapper): else: res.append(' %s: failed' % arch) return "Retrying builds of '%s':\n%s" % ( - self.PackageName(), '\n'.join(res)) + self.getPackageName(), '\n'.join(res)) class PersonTeam(BaseWrapper): @@ -527,12 +527,12 @@ class Build(BaseWrapper): def rescore(self, score): if self.can_be_rescored: - self.rescore(score = score) + self().rescore(score = score) return True return False def retry(self): - if self.can_be_tried: - self.retry() + if self.can_be_retried: + self().retry() return True return False From 4f97f5ba15e718d3442d13f8cedbc8ab3b02788a Mon Sep 17 00:00:00 2001 From: Michael Bienia Date: Wed, 29 Jul 2009 14:49:06 +0200 Subject: [PATCH 08/12] buildd: Create a base to add code for the new v2 mode --- buildd | 204 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 102 insertions(+), 102 deletions(-) diff --git a/buildd b/buildd index 6379dab..37c976e 100755 --- a/buildd +++ b/buildd @@ -6,6 +6,7 @@ # Authors: # - Martin Pitt # - Jonathan Davies +# - Michael Bienia # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -73,115 +74,114 @@ optParser.add_option_group(v2options) # Parse our options. (options, args) = optParser.parse_args() -# 'help' called by itself - show our help. -try: - if args[0].lower() in ("help") and len(args) == 1: - optParser.print_help() - sys.exit(0) -except IndexError: - optParser.print_help() - sys.exit(0) - -# Check we have the correct number of arguments. -if len(args) < 3: - optParser.error("Incorrect number of arguments.") - -try: - package = str(args[0]).lower() - release = str(args[1]).lower() - op = str(args[2]).lower() -except IndexError: - optParser.print_help() - sys.exit(0) - -# Check our operation. -if op not in ("rescore", "retry", "status"): - print >> sys.stderr, "Invalid operation: %s." % op - sys.exit(1) - -# If the user has specified an architecture to build, we only wish to rebuild it -# and nothing else. -if op not in ("retry", 'rescore') and options.architecture: - print >> sys.stderr, "Operation %s does not use the --arch option." % op - sys.exit(1) -elif op in ("retry", "rescore") and options.architecture: - if options.architecture not in validArchs: - print >> sys.stderr, "Invalid architecture specified: %s." % options.architecture - sys.exit(1) - else: - oneArch = True -else: - oneArch = False - -# split release and pocket -if '-' in release: - (release, pocket) = release.split('-') -else: - pocket = 'Release' -pocket = pocket.capitalize() -if pocket not in ('Release', 'Security', 'Updates', 'Proposed', 'Backports'): - print 'Unknown pocket: %s' % pocket +if not len(args): + optParser.print_help() sys.exit(1) -# Get an instance of the LP API wrapper -lpapiwrapper = LpApiWrapper() -# Get list of published sources for package in question. -try: - sources = lpapiwrapper.getUbuntuSourcePackage(package, release, pocket) -except (SeriesNotFoundException, PackageNotFoundException), e: - print e - sys.exit(1) -# Get list of builds for that package. -builds = sources.getBuilds() +if not options.v2mode: + # Check we have the correct number of arguments. + if len(args) < 3: + optParser.error("Incorrect number of arguments.") -# Find out the version and component in given release. -version = sources.getVersion() -component = sources.getComponent() + try: + package = str(args[0]).lower() + release = str(args[1]).lower() + op = str(args[2]).lower() + except IndexError: + optParser.print_help() + sys.exit(1) -# Operations that are remaining may only be done by Ubuntu developers (retry) -# or buildd admins (rescore). Check if the proper permissions are in place. -if op == "rescore": necessaryPrivs = lpapiwrapper.getMe().isLpTeamMember('launchpad-buildd-admins') -if op == "retry": necessaryPrivs = lpapiwrapper.canUploadPackage(package, release) + # Check our operation. + if op not in ("rescore", "retry", "status"): + print >> sys.stderr, "Invalid operation: %s." % op + sys.exit(1) -if op in ('rescore', 'retry') and not necessaryPrivs: - print >> sys.stderr, "You cannot perform the %s operation on a %s package " \ - "as you do not have the permissions to do this action." % (op, component) - sys.exit(1) - -# Output details. -print "The source version for '%s' in %s (%s) is at %s." % (package, - release.capitalize(), component, version) - -print "Current build status for this package:" - -# Output list of arches for package and their status. -done = False -for build in builds: - if oneArch and build.arch_tag != options.architecture: - # Skip this architecture. - continue - - done = True - print "%s: %s." % (build.arch_tag, build.buildstate) - if op == 'rescore': - if build.can_be_rescored: - # FIXME: make priority an option - priority = 5000 - print 'Rescoring build %s to %d...' % (build.arch_tag, priority) - build.rescore(score = priority) - else: - print 'Cannot rescore build on %s.' % build.arch_tag - if op == 'retry': - if build.can_be_retried: - print 'Retrying build on %s...' % build.arch_tag - build.retry() + # If the user has specified an architecture to build, we only wish to rebuild it + # and nothing else. + if op not in ("retry", 'rescore') and options.architecture: + print >> sys.stderr, "Operation %s does not use the --arch option." % op + sys.exit(1) + elif op in ("retry", "rescore") and options.architecture: + if options.architecture not in validArchs: + print >> sys.stderr, "Invalid architecture specified: %s." % options.architecture + sys.exit(1) + else: + oneArch = True else: - print 'Cannot retry build on %s.' % build.arch_tag + oneArch = False + + # split release and pocket + if '-' in release: + (release, pocket) = release.split('-') + else: + pocket = 'Release' + pocket = pocket.capitalize() + if pocket not in ('Release', 'Security', 'Updates', 'Proposed', 'Backports'): + print 'Unknown pocket: %s' % pocket + sys.exit(1) + + # Get an instance of the LP API wrapper + lpapiwrapper = LpApiWrapper() + # Get list of published sources for package in question. + try: + sources = lpapiwrapper.getUbuntuSourcePackage(package, release, pocket) + except (SeriesNotFoundException, PackageNotFoundException), e: + print e + sys.exit(1) + # Get list of builds for that package. + builds = sources.getBuilds() + + # Find out the version and component in given release. + version = sources.getVersion() + component = sources.getComponent() + + # Operations that are remaining may only be done by Ubuntu developers (retry) + # or buildd admins (rescore). Check if the proper permissions are in place. + if op == "rescore": necessaryPrivs = lpapiwrapper.getMe().isLpTeamMember('launchpad-buildd-admins') + if op == "retry": necessaryPrivs = lpapiwrapper.canUploadPackage(package, release) + + if op in ('rescore', 'retry') and not necessaryPrivs: + print >> sys.stderr, "You cannot perform the %s operation on a %s package " \ + "as you do not have the permissions to do this action." % (op, component) + sys.exit(1) + + # Output details. + print "The source version for '%s' in %s (%s) is at %s." % (package, + release.capitalize(), component, version) + + print "Current build status for this package:" + + # Output list of arches for package and their status. + done = False + for build in builds: + if oneArch and build.arch_tag != options.architecture: + # Skip this architecture. + continue + + done = True + print "%s: %s." % (build.arch_tag, build.buildstate) + if op == 'rescore': + if build.can_be_rescored: + # FIXME: make priority an option + priority = 5000 + print 'Rescoring build %s to %d...' % (build.arch_tag, priority) + build.rescore(score = priority) + else: + print 'Cannot rescore build on %s.' % build.arch_tag + if op == 'retry': + if build.can_be_retried: + print 'Retrying build on %s...' % build.arch_tag + build.retry() + else: + print 'Cannot retry build on %s.' % build.arch_tag -# We are done -if done: sys.exit(0) + # We are done + if done: sys.exit(0) -print "No builds for '%s' found in the %s release - it may have been " \ - "built in a former release." % (package, release.capitalize()) + print "No builds for '%s' found in the %s release - it may have been " \ + "built in a former release." % (package, release.capitalize()) + sys.exit(0) + +# V2 mode sys.exit(0) From 6c5a9971feaa571682906dcefef96695e0906da3 Mon Sep 17 00:00:00 2001 From: Michael Bienia Date: Wed, 29 Jul 2009 16:02:58 +0200 Subject: [PATCH 09/12] buildd: Add v2 mode --- buildd | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 7 deletions(-) diff --git a/buildd b/buildd index 37c976e..c4c66ec 100755 --- a/buildd +++ b/buildd @@ -35,8 +35,8 @@ usage += "Where operation may be one of: rescore, retry, or status.\n" usage += "Only Launchpad Buildd Admins may rescore package builds." # Valid architectures. -validArchs = ["armel", "amd64", "hppa", "i386", - "ia64", "lpia", "powerpc", "sparc"] +valid_archs = set(["armel", "amd64", "hppa", "i386", + "ia64", "lpia", "powerpc", "sparc"]) # Prepare our option parser. optParser = OptionParser(usage) @@ -48,7 +48,7 @@ retryRescoreOptions.add_option("-a", "--arch", type = "string", action = "store", dest = "architecture", help = "Rebuild or rescore a specific architecture. " \ "Valid architectures include: " \ - "%s." % ", ".join(validArchs)) + "%s." % ", ".join(valid_archs)) # A new calling interface (v2) v2options = OptionGroup(optParser, "Extended calling convention", @@ -64,7 +64,7 @@ v2options.add_option('--rescore', action = 'store', dest = 'priority', type = 'i help = 'Rescore builds to .') v2options.add_option('--arch2', action = 'append', dest = 'architecture', type = 'string', help = "Affect only 'architecture' (can be used several times). " - "Valid architectures include: %s." % ', '.join(validArchs)) + "Valid architectures include: %s." % ', '.join(valid_archs)) # Add the retry options to the main group. optParser.add_option_group(retryRescoreOptions) @@ -102,7 +102,7 @@ if not options.v2mode: print >> sys.stderr, "Operation %s does not use the --arch option." % op sys.exit(1) elif op in ("retry", "rescore") and options.architecture: - if options.architecture not in validArchs: + if options.architecture not in valid_archs: print >> sys.stderr, "Invalid architecture specified: %s." % options.architecture sys.exit(1) else: @@ -124,7 +124,7 @@ if not options.v2mode: lpapiwrapper = LpApiWrapper() # Get list of published sources for package in question. try: - sources = lpapiwrapper.getUbuntuSourcePackage(package, release, pocket) + sources = lpapiwrapper.getUbuntuDistribution().getArchive().getSourcePackage(package, release, pocket) except (SeriesNotFoundException, PackageNotFoundException), e: print e sys.exit(1) @@ -184,4 +184,57 @@ if not options.v2mode: sys.exit(0) # V2 mode -sys.exit(0) + +if not options.architecture: + # no specific architectures specified, assume all valid ones + archs = valid_archs +else: + archs = set(options.architecture) + +# filter out duplicate and invalid architectures +archs.intersection_update(valid_archs) + +release = options.series # if None it falls back to the current development series +pocket = 'Release' +if release and '-' in release: + # split release and pocket + (release, pocket) = options.series.split('-') + pocket = pocket.capitalize() + + if pocket not in ('Release', 'Security', 'Updates', 'Proposed', 'Backports'): + print 'Unknown pocket: %s' % pocket + sys.exit(1) + +ubuntu_archive = LpApiWrapper.getUbuntuDistribution().getArchive() +me = LpApiWrapper.getMe() + +# Check permisions (part 1): Rescoring can only be done by buildd admins +can_rescore = options.priority and me.isLpTeamMember('launchpad-buildd-admins') or False +if options.priority and not can_rescore: + print >> sys.stderr, "You don't have the permissions to rescore builds. Ignoring your rescore request." + +for pkg in args: + try: + pkg = ubuntu_archive.getSourcePackage(pkg, release, pocket) + except SeriesNotFoundException, e: + print e + sys.exit(1) + except PackageNotFoundException, e: + print e + continue + + # Check permissions (part 2): check upload permissions for the source package + can_retry = options.retry and me.canUploadPackage(ubuntu_archive, pkg.getPackageName(), pkg.getComponent()) + if options.retry and not can_retry: + print >> sys.stderr, "You don't have the permissions to retry the build of '%s'. Ignoring your request." % pkg.getPackageName() + + print "The source version for '%s' in '%s' (%s) is: %s" % ( + pkg.getPackageName(), release, pocket, pkg.getVersion()) + + print pkg.getBuildStates(archs) + if can_retry: + print pkg.retryBuilds(archs) + if options.priority and can_rescore: + print pkg.rescoreBuilds(archs, options.priority) + + print '' From 20157940ae124d4ab765d924e4754983c9098bd4 Mon Sep 17 00:00:00 2001 From: Michael Bienia Date: Wed, 29 Jul 2009 16:13:24 +0200 Subject: [PATCH 10/12] buildd: make the -a option not break in v2 mode --- buildd | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/buildd b/buildd index c4c66ec..c2248d6 100755 --- a/buildd +++ b/buildd @@ -45,7 +45,7 @@ optParser = OptionParser(usage) retryRescoreOptions = OptionGroup(optParser, "Retry and rescore options", "These options may only be used with the 'retry' and 'rescore' operations.") retryRescoreOptions.add_option("-a", "--arch", type = "string", - action = "store", dest = "architecture", + action = "append", dest = "architecture", help = "Rebuild or rescore a specific architecture. " \ "Valid architectures include: " \ "%s." % ", ".join(valid_archs)) @@ -64,7 +64,7 @@ v2options.add_option('--rescore', action = 'store', dest = 'priority', type = 'i help = 'Rescore builds to .') v2options.add_option('--arch2', action = 'append', dest = 'architecture', type = 'string', help = "Affect only 'architecture' (can be used several times). " - "Valid architectures include: %s." % ', '.join(valid_archs)) + "Valid architectures are: %s." % ', '.join(valid_archs)) # Add the retry options to the main group. optParser.add_option_group(retryRescoreOptions) @@ -98,12 +98,9 @@ if not options.v2mode: # If the user has specified an architecture to build, we only wish to rebuild it # and nothing else. - if op not in ("retry", 'rescore') and options.architecture: - print >> sys.stderr, "Operation %s does not use the --arch option." % op - sys.exit(1) - elif op in ("retry", "rescore") and options.architecture: - if options.architecture not in valid_archs: - print >> sys.stderr, "Invalid architecture specified: %s." % options.architecture + if options.architecture: + if options.architecture[0] not in valid_archs: + print >> sys.stderr, "Invalid architecture specified: %s." % options.architecture[0] sys.exit(1) else: oneArch = True @@ -154,7 +151,7 @@ if not options.v2mode: # Output list of arches for package and their status. done = False for build in builds: - if oneArch and build.arch_tag != options.architecture: + if oneArch and build.arch_tag != options.architecture[0]: # Skip this architecture. continue From a30e797e8cfdfa799ec4c46e7c619ebe72c75d36 Mon Sep 17 00:00:00 2001 From: Michael Bienia Date: Wed, 29 Jul 2009 16:20:58 +0200 Subject: [PATCH 11/12] buildd: some cleanup --- buildd | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/buildd b/buildd index c2248d6..a21cf46 100755 --- a/buildd +++ b/buildd @@ -53,11 +53,11 @@ retryRescoreOptions.add_option("-a", "--arch", type = "string", # A new calling interface (v2) v2options = OptionGroup(optParser, "Extended calling convention", "These options and parameter ordering is only available in the --v2 mode.\n" - "Usage: buildd --v2 [options] --series=SERIES ...") + "Usage: buildd --v2 [options] ...") v2options.add_option('--v2', action = 'store_true', dest = 'v2mode', default = False, help = 'Enable the new (v2) mode') v2options.add_option('--series', action = 'store', dest = 'series', type = 'string', - help = 'Selects the Ubuntu series to operate on.') + help = 'Selects the Ubuntu series to operate on (default: current development series)') v2options.add_option('--retry', action = 'store_true', dest = 'retry', default = False, help = 'Retry builds (give-back).') v2options.add_option('--rescore', action = 'store', dest = 'priority', type = 'int', @@ -117,11 +117,11 @@ if not options.v2mode: print 'Unknown pocket: %s' % pocket sys.exit(1) - # Get an instance of the LP API wrapper - lpapiwrapper = LpApiWrapper() + # Get the ubuntu archive + ubuntu_archive = LpApiWrapper.getUbuntuDistribution().getArchive() # Get list of published sources for package in question. try: - sources = lpapiwrapper.getUbuntuDistribution().getArchive().getSourcePackage(package, release, pocket) + sources = ubuntu_archive.getSourcePackage(package, release, pocket) except (SeriesNotFoundException, PackageNotFoundException), e: print e sys.exit(1) @@ -134,8 +134,10 @@ if not options.v2mode: # Operations that are remaining may only be done by Ubuntu developers (retry) # or buildd admins (rescore). Check if the proper permissions are in place. - if op == "rescore": necessaryPrivs = lpapiwrapper.getMe().isLpTeamMember('launchpad-buildd-admins') - if op == "retry": necessaryPrivs = lpapiwrapper.canUploadPackage(package, release) + me = LpApiWrapper.getMe() + if op == "rescore": necessaryPrivs = me.isLpTeamMember('launchpad-buildd-admins') + if op == "retry": necessaryPrivs = me.canUploadPackage( + ubuntu_archive, sources.getPackageName(), sources.getComponent()) if op in ('rescore', 'retry') and not necessaryPrivs: print >> sys.stderr, "You cannot perform the %s operation on a %s package " \ From c1a9323035f8fb151814276c8e2d9d51d3d3bea2 Mon Sep 17 00:00:00 2001 From: Michael Bienia Date: Wed, 29 Jul 2009 22:59:26 +0200 Subject: [PATCH 12/12] buildd: Rename --v2 to --batch --- buildd | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/buildd b/buildd index a21cf46..e19c162 100755 --- a/buildd +++ b/buildd @@ -50,26 +50,32 @@ retryRescoreOptions.add_option("-a", "--arch", type = "string", "Valid architectures include: " \ "%s." % ", ".join(valid_archs)) -# A new calling interface (v2) -v2options = OptionGroup(optParser, "Extended calling convention", - "These options and parameter ordering is only available in the --v2 mode.\n" - "Usage: buildd --v2 [options] ...") -v2options.add_option('--v2', action = 'store_true', dest = 'v2mode', default = False, - help = 'Enable the new (v2) mode') -v2options.add_option('--series', action = 'store', dest = 'series', type = 'string', +# Batch processing options +batch_options = OptionGroup( + optParser, "Batch processing", + "These options and parameter ordering is only available in --batch mode.\n" + "Usage: buildd --batch [options] ...") +batch_options.add_option( + '--batch', action = 'store_true', dest = 'batch', default = False, + help = 'Enable batch mode') +batch_options.add_option( + '--series', action = 'store', dest = 'series', type = 'string', help = 'Selects the Ubuntu series to operate on (default: current development series)') -v2options.add_option('--retry', action = 'store_true', dest = 'retry', default = False, +batch_options.add_option( + '--retry', action = 'store_true', dest = 'retry', default = False, help = 'Retry builds (give-back).') -v2options.add_option('--rescore', action = 'store', dest = 'priority', type = 'int', +batch_options.add_option( + '--rescore', action = 'store', dest = 'priority', type = 'int', help = 'Rescore builds to .') -v2options.add_option('--arch2', action = 'append', dest = 'architecture', type = 'string', +batch_options.add_option( + '--arch2', action = 'append', dest = 'architecture', type = 'string', help = "Affect only 'architecture' (can be used several times). " "Valid architectures are: %s." % ', '.join(valid_archs)) # Add the retry options to the main group. optParser.add_option_group(retryRescoreOptions) -# Add the new v2 mode to the main group. -optParser.add_option_group(v2options) +# Add the batch mode to the main group. +optParser.add_option_group(batch_options) # Parse our options. (options, args) = optParser.parse_args() @@ -78,7 +84,7 @@ if not len(args): optParser.print_help() sys.exit(1) -if not options.v2mode: +if not options.batch: # Check we have the correct number of arguments. if len(args) < 3: optParser.error("Incorrect number of arguments.") @@ -182,7 +188,7 @@ if not options.v2mode: "built in a former release." % (package, release.capitalize()) sys.exit(0) -# V2 mode +# Batch mode if not options.architecture: # no specific architectures specified, assume all valid ones