mirror of
https://git.launchpad.net/ubuntu-dev-tools
synced 2025-03-12 23:51:08 +00:00
New Tool: ubuntu-upload-permission: Query upload permissions (LP: #876554)
This commit is contained in:
commit
89e15a2f5d
3
debian/changelog
vendored
3
debian/changelog
vendored
@ -22,10 +22,11 @@ ubuntu-dev-tools (0.137) UNRELEASED; urgency=low
|
||||
- Do the report boiler-plate checking in a script that wraps an editor, so
|
||||
that we only edit the report once, after checking for duplicates.
|
||||
- rm the tmpdir with a little more force (shutil.rmtree) (LP: #899399)
|
||||
* New Tool: ubuntu-upload-permission: Query upload permissions (LP: #876554)
|
||||
|
||||
[ Andreas Moog ]
|
||||
* sponsor-patch: Check permission to unsubscribe sponsors-team (LP: #896884)
|
||||
* grep-merges: We already require a UTF-8 enabled terminal, so encode
|
||||
* grep-merges: We already require a UTF-8 enabled terminal, so encode
|
||||
package and uploader name in UTF-8 (LP: #694388)
|
||||
* requestsync: Give user option to retry in case of temporary error (LP: #850360)
|
||||
|
||||
|
3
debian/control
vendored
3
debian/control
vendored
@ -68,6 +68,7 @@ Description: useful tools for Ubuntu developers
|
||||
.
|
||||
- 404main - used to check what components a package's deps are in, for
|
||||
doing a main inclusion report for example.
|
||||
- backportpackage - helper to test package backports
|
||||
- bitesize - add the 'bitesize' tag to a bug and comment that you are
|
||||
willing to help fix it.
|
||||
- check-mir - check support status of build/binary dependencies
|
||||
@ -105,4 +106,6 @@ Description: useful tools for Ubuntu developers
|
||||
- ubuntu-build - give commands to the Launchpad build daemons from the
|
||||
command line.
|
||||
- ubuntu-iso - output information of an Ubuntu ISO image.
|
||||
- ubuntu-upload-permission - query / list the upload permissions for a
|
||||
package.
|
||||
- update-maintainer - script to update maintainer field in ubuntu packages.
|
||||
|
2
debian/copyright
vendored
2
debian/copyright
vendored
@ -150,6 +150,7 @@ Files: doc/pull-debian-debdiff.1
|
||||
doc/reverse-depends.1
|
||||
doc/sponsor-patch.1
|
||||
doc/ubuntu-dev-tools.5
|
||||
doc/ubuntu-upload-permission.1
|
||||
doc/update-maintainer.1
|
||||
enforced-editing-wrapper
|
||||
pull-debian-debdiff
|
||||
@ -158,6 +159,7 @@ Files: doc/pull-debian-debdiff.1
|
||||
reverse-depends
|
||||
sponsor-patch
|
||||
test-data/*
|
||||
ubuntu-upload-permission
|
||||
ubuntutools/archive.py
|
||||
ubuntutools/builder.py
|
||||
ubuntutools/config.py
|
||||
|
60
doc/ubuntu-upload-permission.1
Normal file
60
doc/ubuntu-upload-permission.1
Normal file
@ -0,0 +1,60 @@
|
||||
.\" 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.
|
||||
.TH ubuntu\-upload\-permission 1 "November 2011" ubuntu\-dev\-tools
|
||||
|
||||
.SH NAME
|
||||
ubuntu\-upload\-permission \- Query upload rights and (optionally) list
|
||||
the people and teams with upload rights for a package
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B ubuntu\-upload\-permission \fR[\fIoptions\fR] \fIpackage
|
||||
|
||||
.SH DESCRIPTION
|
||||
\fBubuntu\-upload\-permission\fR checks if the user has upload
|
||||
permissions for \fIpackage\fR.
|
||||
If the \fB\-\-list\-uploaders\fR option is provided, all the people and
|
||||
teams that do have upload rights for \fIpackage\fR will be listed.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-r\fR \fIRELEASE\fR, \fB\-\-release\fR=\fIRELEASE\fR
|
||||
Query permissions in \fIRELEASE\fR.
|
||||
Default: current development release.
|
||||
.TP
|
||||
\fB\-a\fR, \fB\-\-list\-uploaders\fR
|
||||
List all the people and teams who have upload rights for \fIpackage\fR.
|
||||
.TP
|
||||
\fB\-t\fR, \fB\-\-list\-team\-members\fR
|
||||
List all the members of every team with rights. (Implies
|
||||
\fB\-\-list\-uploaders\fR)
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Display a help message and exit
|
||||
|
||||
.SH EXIT STATUS
|
||||
.TP
|
||||
.B 0
|
||||
You have the necessary upload rights.
|
||||
.TP
|
||||
.B 1
|
||||
You don't have the necessary upload rights.
|
||||
.TP
|
||||
.B 2
|
||||
There was an error.
|
||||
|
||||
.SH AUTHORS
|
||||
\fBubuntu\-upload\-permission\fR and this manpage were written by
|
||||
Stefano Rivera <stefanor@ubuntu.com>.
|
||||
.PP
|
||||
Both are released under the terms of the ISC License.
|
1
setup.py
1
setup.py
@ -41,6 +41,7 @@ scripts = ['404main',
|
||||
'syncpackage',
|
||||
'ubuntu-build',
|
||||
'ubuntu-iso',
|
||||
'ubuntu-upload-permission',
|
||||
'update-maintainer',
|
||||
]
|
||||
|
||||
|
145
ubuntu-upload-permission
Executable file
145
ubuntu-upload-permission
Executable file
@ -0,0 +1,145 @@
|
||||
#!/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
|
||||
import sys
|
||||
|
||||
from devscripts.logger import Logger
|
||||
|
||||
from ubuntutools.lp.lpapicache import (Launchpad, Distribution, PersonTeam,
|
||||
Packageset, PackageNotFoundException,
|
||||
SeriesNotFoundException)
|
||||
from ubuntutools.misc import split_release_pocket
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
'''Parse arguments and return (options, package)'''
|
||||
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('-a', '--list-uploaders',
|
||||
default=False, action='store_true',
|
||||
help='List all the people/teams with upload rights')
|
||||
parser.add_option('-t', '--list-team-members',
|
||||
default=False, action='store_true',
|
||||
help='List all team members of teams with upload rights '
|
||||
'(implies --list-uploaders)')
|
||||
options, args = parser.parse_args()
|
||||
|
||||
if len(args) != 1:
|
||||
parser.error("One (and only one) package must be specified")
|
||||
package = args[0]
|
||||
|
||||
if options.list_team_members:
|
||||
options.list_uploaders = True
|
||||
|
||||
return (options, package)
|
||||
|
||||
|
||||
def main():
|
||||
'''Query upload permissions'''
|
||||
options, package = parse_arguments()
|
||||
# Need to be logged in to see uploaders:
|
||||
Launchpad.login()
|
||||
|
||||
ubuntu = Distribution('ubuntu')
|
||||
archive = ubuntu.getArchive()
|
||||
if options.release is None:
|
||||
options.release = ubuntu.getDevelopmentSeries().name
|
||||
try:
|
||||
release, pocket = split_release_pocket(options.release)
|
||||
series = ubuntu.getSeries(release)
|
||||
except SeriesNotFoundException, e:
|
||||
Logger.error(str(e))
|
||||
sys.exit(2)
|
||||
|
||||
try:
|
||||
spph = archive.getSourcePackage(package)
|
||||
except PackageNotFoundException, e:
|
||||
Logger.error(str(e))
|
||||
sys.exit(2)
|
||||
component = spph.getComponent()
|
||||
if (options.list_uploaders and (pocket != 'Release' or series.status in
|
||||
('Experimental', 'Active Development', 'Pre-release Freeze'))):
|
||||
|
||||
component_uploader = archive.getUploadersForComponent(
|
||||
component_name=component)[0]
|
||||
print "All upload permissions for %s:" % package
|
||||
print
|
||||
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,
|
||||
pocket):
|
||||
print "You can upload %s to %s." % (package, options.release)
|
||||
else:
|
||||
print ("You can not upload %s to %s, yourself."
|
||||
% (package, options.release))
|
||||
if (series.status in ('Current Stable Release', 'Supported', 'Obsolete')
|
||||
and pocket == 'Release'):
|
||||
print ("%s is in the '%s' state. "
|
||||
"You may want to query the %s-proposed pocket."
|
||||
% (release, series.status, release))
|
||||
else:
|
||||
print ("But you can still contribute to it via the sponsorship "
|
||||
"process: https://wiki.ubuntu.com/SponsorshipProcess")
|
||||
if not options.list_uploaders:
|
||||
print ("To see who has the necessary upload rights, "
|
||||
"use the --list-uploaders option.")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def print_uploaders(uploaders, expand_teams=False, prefix=''):
|
||||
"""Given a list of uploaders, pretty-print them all
|
||||
Each line is prefixed with prefix.
|
||||
If expand_teams is set, recurse, adding more spaces to prefix on each
|
||||
recursion.
|
||||
"""
|
||||
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()
|
@ -54,6 +54,7 @@ __all__ = [
|
||||
|
||||
_POCKETS = ('Release', 'Security', 'Updates', 'Proposed', 'Backports')
|
||||
|
||||
|
||||
class _Launchpad(object):
|
||||
'''Singleton for LP API access.'''
|
||||
|
||||
@ -116,7 +117,7 @@ class BaseWrapper(object):
|
||||
A base class from which other wrapper classes are derived.
|
||||
'''
|
||||
__metaclass__ = MetaWrapper
|
||||
resource_type = None # it's a base class after all
|
||||
resource_type = None # it's a base class after all
|
||||
|
||||
def __new__(cls, data):
|
||||
if (isinstance(data, basestring) and
|
||||
@ -278,11 +279,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 +401,45 @@ 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.
|
||||
[Note: the permission records, themselves, aren't exposed]
|
||||
'''
|
||||
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)
|
||||
[Note: the permission records, themselves, aren't exposed]
|
||||
'''
|
||||
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
|
||||
[Note: the permission records, themselves, aren't exposed]
|
||||
'''
|
||||
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):
|
||||
'''
|
||||
@ -585,6 +625,7 @@ class MetaPersonTeam(MetaWrapper):
|
||||
raise
|
||||
return cls._me
|
||||
|
||||
|
||||
class PersonTeam(BaseWrapper):
|
||||
'''
|
||||
Wrapper class around a LP person or team object.
|
||||
@ -687,7 +728,7 @@ class Build(BaseWrapper):
|
||||
|
||||
def rescore(self, score):
|
||||
if self.can_be_rescored:
|
||||
self().rescore(score = score)
|
||||
self().rescore(score=score)
|
||||
return True
|
||||
return False
|
||||
|
||||
@ -703,3 +744,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]
|
||||
|
Loading…
x
Reference in New Issue
Block a user