diff --git a/debian/changelog b/debian/changelog index b0db2d1..0bcc159 100644 --- a/debian/changelog +++ b/debian/changelog @@ -18,6 +18,7 @@ ubuntu-dev-tools (0.137) UNRELEASED; urgency=low * mk-sbuild, pbuilder-dist, ubuntu-build: Add armhf. * pull-debian-source, pull-lp-source: Resolve the source package (via DDE), if a binary package was requested (LP: #617349) + * New Tool: who-can-upload (LP: #876554) [ Andreas Moog ] * sponsor-patch: Check permission to unsubscribe sponsors-team (LP: #896884) diff --git a/ubuntutools/lp/lpapicache.py b/ubuntutools/lp/lpapicache.py index f284d09..dedda24 100644 --- a/ubuntutools/lp/lpapicache.py +++ b/ubuntutools/lp/lpapicache.py @@ -278,11 +278,11 @@ class Archive(BaseWrapper): resource_type = 'archive' def __init__(self, *args): - # Don't share between different Archives - if '_binpkgs' not in self.__dict__: - self._binpkgs = dict() - if '_srcpkgs' not in self.__dict__: - self._srcpkgs = dict() + self._binpkgs = {} + self._srcpkgs = {} + self._pkg_uploaders = {} + self._pkgset_uploaders = {} + self._component_uploaders = {} def getSourcePackage(self, name, series=None, pocket=None): ''' @@ -400,6 +400,40 @@ class Archive(BaseWrapper): include_binaries=include_binaries ) + def getUploadersForComponent(self, component_name): + '''Get the list of PersonTeams who can upload packages in the + specified component. + ''' + if component_name not in self._component_uploaders: + self._component_uploaders[component_name] = sorted(set( + PersonTeam(permission.person_link) + for permission in self._lpobject.getUploadersForComponent( + component_name=component_name + ))) + return self._component_uploaders[component_name] + + def getUploadersForPackage(self, source_package_name): + '''Get the list of PersonTeams who can upload source_package_name)''' + if source_package_name not in self._pkg_uploaders: + self._pkg_uploaders[source_package_name] = sorted(set( + PersonTeam(permission.person_link) + for permission in self._lpobject.getUploadersForPackage( + source_package_name=source_package_name + ))) + return self._pkg_uploaders[source_package_name] + + def getUploadersForPackageset(self, packageset, direct_permissions=False): + '''Get the list of PersonTeams who can upload packages in packageset''' + key = (packageset, direct_permissions) + if key not in self._pkgset_uploaders: + self._pkgset_uploaders[key] = sorted(set( + PersonTeam(permission.person_link) + for permission in self._lpobject.getUploadersForPackageset( + packageset=packageset._lpobject, + direct_permissions=direct_permissions, + ))) + return self._pkgset_uploaders[key] + class SourcePackagePublishingHistory(BaseWrapper): ''' @@ -703,3 +737,36 @@ class DistributionSourcePackage(BaseWrapper): Caching class for distribution_source_package objects. ''' resource_type = 'distribution_source_package' + + +class Packageset(BaseWrapper): + ''' + Caching class for packageset objects. + ''' + resource_type = 'packageset' + _lp_packagesets = None + _source_sets = {} + + @classmethod + def setsIncludingSource(cls, sourcepackagename, distroseries=None, + direct_inclusion=False): + '''Get the package sets including sourcepackagename''' + + if cls._lp_packagesets is None: + cls._lp_packagesets = Launchpad.packagesets + + key = (sourcepackagename, distroseries, direct_inclusion) + if key not in cls._source_sets: + params = { + 'sourcepackagename': sourcepackagename, + 'direct_inclusion': direct_inclusion, + } + if distroseries is not None: + params['distroseries'] = distroseries._lpobject + + cls._source_sets[key] = [ + Packageset(packageset) for packageset + in cls._lp_packagesets.setsIncludingSource(**params) + ] + + return cls._source_sets[key] diff --git a/who-can-upload b/who-can-upload new file mode 100755 index 0000000..04ea9f5 --- /dev/null +++ b/who-can-upload @@ -0,0 +1,97 @@ +#!/usr/bin/python +# +# Copyright (C) 2011, Stefano Rivera <stefanor@ubuntu.com> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import optparse + +from ubuntutools.lp.lpapicache import (Launchpad, Distribution, PersonTeam, + Packageset, + SeriesNotFoundException) + + +def main(): + parser = optparse.OptionParser('%prog [options] package') + parser.add_option('-r', '--release', default=None, metavar='RELEASE', + help='Use RELEASE, rather than the current development ' + 'release') + parser.add_option('-t', '--list-team-members', + default=False, action='store_true', + help='List all team members of teams with upload rights') + options, args = parser.parse_args() + + if len(args) != 1: + parser.error("One (and only one) package must be specified") + package = args[0] + + # Need to be logged in to see uploaders: + Launchpad.login() + + ubuntu = Distribution('ubuntu') + archive = ubuntu.getArchive() + if options.release is None: + series = ubuntu.getDevelopmentSeries() + else: + try: + series = ubuntu.getSeries(options.release) + except SeriesNotFoundException, e: + parser.error(str(e)) + + spph = archive.getSourcePackage(package) + component = spph.getComponent() + component_uploader = archive.getUploadersForComponent( + component_name=component)[0] + print "Component (%s)" % component + print "============" + ("=" * len(component)) + print_uploaders([component_uploader], options.list_team_members) + + packagesets = sorted(Packageset.setsIncludingSource(distroseries=series, + sourcepackagename=package)) + if packagesets: + print + print "Packagesets" + print "===========" + for packageset in packagesets: + print + print "%s:" % packageset.name + print_uploaders(archive.getUploadersForPackageset( + packageset=packageset), options.list_team_members) + + ppu_uploaders = archive.getUploadersForPackage(source_package_name=package) + if ppu_uploaders: + print + print "Per-Package-Uploaders" + print "=====================" + print + print_uploaders(ppu_uploaders, options.list_team_members) + + print + if PersonTeam.me.canUploadPackage(archive, series, package, component): + print "You can upload this package" + else: + print "You can not upload this package, yourself." + print ("But you can still contribute to it via the sponsorship " + "process: https://wiki.ubuntu.com/SponsorshipProcess") + + +def print_uploaders(uploaders, expand_teams=False, prefix=''): + for uploader in sorted(uploaders, key=lambda p: p.display_name): + print ("%s* %s (%s)%s" + % (prefix, uploader.display_name, uploader.name, + ' [team]' if uploader.is_team else '')) + if expand_teams and uploader.is_team: + print_uploaders(uploader.participants, True, prefix=prefix + ' ') + +if __name__ == '__main__': + main()