diff --git a/buildd b/buildd index abaea44..85e04f6 100755 --- a/buildd +++ b/buildd @@ -116,7 +116,7 @@ packages.checkReleaseExists(release) (page, version) = packages.checkSourceExists(package, release) # Get the component the package is in. -component = packages.packageComponent(package, release) +component = lp_functions.packageComponent(package, release) # Output details. print "The source version for '%s' in %s (%s) is at %s." % (package, @@ -156,14 +156,9 @@ if len(buildstats) == 1 and options.architecture != "i386": # 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": teamNeeded = "launchpad-buildd-admins" +if op == "rescore": necessaryPrivs = lp_functions.isLPTeamMember('launchpad-buildd-admins') if op == "retry": - if component in ("main", "restricted"): - teamNeeded = "ubuntu-core-dev" - else: - teamNeeded = "ubuntu-dev" - -necessaryPrivs = lp_functions.isLPTeamMember(teamNeeded) + necessaryPrivs = lp_functions.canUploadPackage(package, release) if not necessaryPrivs: print >> sys.stderr, "You cannot perform the %s operation on a %s package " \ diff --git a/debian/changelog b/debian/changelog index f62a7e5..7635cc0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -13,7 +13,21 @@ ubuntu-dev-tools (0.74) UNRELEASED; urgency=low - Delete the directory just after creating it if the package doesn't exist. - -- Siegfried-Angel Gevatter Pujals Wed, 06 May 2009 23:14:33 +0200 + [ Iain Lane ] + * ubuntutools/lp/lp_functions.py, + ubuntutools/lp/udtexceptions.py: + - Add new public functions that expose features from LP API + - Modify isLPTeamMember to use LP API + * requestsync + - Use new functions to check if user can upload requested package directly + instead of checking team membership + - Default to current development release if no release is specified on + commandline + * buildd + - Check if user has upload privileges instead of checking for team + membership when seeing if operations are permitted + + -- Iain Lane Sat, 09 May 2009 20:09:28 +0100 ubuntu-dev-tools (0.73) karmic; urgency=low diff --git a/doc/requestsync.1 b/doc/requestsync.1 index 705f8ce..b131c70 100644 --- a/doc/requestsync.1 +++ b/doc/requestsync.1 @@ -2,7 +2,7 @@ .SH NAME requestsync \- helper to file sync requests for Ubuntu .SH SYNOPSIS -.B requestsync\fR [\fB\-d distro\fR] [\fB\-nse\fR] [\fB\-k \fIkeyid\fR] <\fBsource package\fR> <\fBtarget release\fR> [\fIbase version\fR] +.B requestsync\fR [\fB\-d distro\fR] [\fB\-nse\fR] [\fB\-k \fIkeyid\fR] <\fBsource package\fR> [\fBtarget release\fR] [\fIbase version\fR] .br .B requestsync \-\-lp\fR [\fB\-nse\fR] <\fBsource package\fR> <\fBtarget release\fR> [\fIbase version\fR] .br diff --git a/requestsync b/requestsync index cee81ba..7e02d1b 100755 --- a/requestsync +++ b/requestsync @@ -40,12 +40,13 @@ import ubuntutools.lp.cookie as lp_cookie import ubuntutools.lp.functions as lp_functions import ubuntutools.lp.libsupport as lp_libsupport import ubuntutools.lp.urlopener as lp_urlopener +import ubuntutools.lp.udtexceptions as udtexceptions # https_proxy fix import ubuntutools.common launchpad_cookiefile = lp_cookie.prepareLaunchpadCookie() -def checkNeedsSponsorship(component): +def checkNeedsSponsorship(srcpkg): """ Check that the user has the appropriate permissions by checking what Launchpad returns while authenticating with their cookie. @@ -59,25 +60,14 @@ def checkNeedsSponsorship(component): The prepareLaunchpadCookie function above shall ensure that a cookie file exists first. """ - # TODO: use launchpadlib here - # Once LP: #313233 has been fixed this can be implemented by either: - # >>> me = launchpad.me - # >>> me.inTeam() #or - # >>> me in - urlopener = lp_urlopener.setupLaunchpadUrlOpener(launchpad_cookiefile) - - # Check where the package is and assign the appropriate variables. if component in ['main', 'restricted']: team = "ubuntu-core-dev" sponsor = "ubuntu-main-sponsors" else: - team = "ubuntu-dev" + team = "motu" sponsor = "ubuntu-universe-sponsors" - # Check if they are a member of the team. - teamMember = lp_functions.isLPTeamMember(team) - - if not teamMember: + if not lp_functions.canUploadPackage(srcpkg): # Check if they have a per-package upload permission. if lp_functions.isPerPackageUploader(args[0]): @@ -89,11 +79,7 @@ def checkNeedsSponsorship(component): "the '%s'\nteam, who shall be subscribed to this bug report." % sponsor print "This must be done before it can be processed by a member of " \ "the Ubuntu Archive team." - print "If the above is correct please press Enter, otherwise please " \ - "press Ctrl-C to stop this script now\nand check the cookie file " \ - "at:", launchpad_cookiefile - print "Logging into Launchpad, deleting the above and rerunning this " \ - "script should be enough to generate the cookie." + print "If the above is correct please press Enter." raw_input_exit_on_ctrlc() # Abort if necessary. return True # Sponsorship required. @@ -150,21 +136,17 @@ def checkExistingReports(package): raw_input_exit_on_ctrlc() def cur_version_component(sourcepkg, release): - '''Determine current package version in ubuntu.''' - madison = subprocess.Popen(['rmadison', '-u', 'ubuntu', '-a', 'source', \ - '-s', release, sourcepkg], stdout=subprocess.PIPE) - out = madison.communicate()[0] - assert (madison.returncode == 0) + + try: + version = lp_functions.packageVersion(sourcepkg, release) + component = lp_functions.packageComponent(sourcepkg, release) - for l in out.splitlines(): - (pkg, version, rel, builds) = l.split('|') - component = 'main' - if rel.find('/') != -1: - component = rel.split('/')[1] - return (version.strip(), component.strip()) - - print "%s doesn't appear to exist in %s, specify -n for a package not in Ubuntu." % (sourcepkg, release) - sys.exit(1) + return (version, component) + + except udtexceptions.PackageNotFoundException: + + print "%s doesn't appear to exist in %s, specify -n for a package not in Ubuntu." % (sourcepkg, release) + sys.exit(1) def cur_deb_version(sourcepkg, distro): '''Return the current debian version of a package in a Debian distro.''' @@ -505,15 +487,17 @@ if __name__ == '__main__': distro = options.dist ffe = options.ffe - if len(args) not in (2, 3): - optParser.error("Source package / target release missing - please " \ - "specify.") + if len(args) not in (2, 3): # no release specified, assume development release + release = lp_functions.ubuntuDevelopmentSeries() + print >> sys.stderr, ("Source package / target release missing - assuming %s " % + release) + else: + release = args[1] if not use_lp_bugs and not get_email_address(): sys.exit(1) srcpkg = args[0] - release = args[1] force_base_ver = None # Base version specified. @@ -534,7 +518,7 @@ if __name__ == '__main__': sys.exit(1) # -s flag not specified - check if we do need sponsorship. - if not sponsorship: sponsorship = checkNeedsSponsorship(component) + if not sponsorship: sponsorship = checkNeedsSponsorship(srcpkg) # Check for existing package reports. if not newsource and use_lp_bugs: checkExistingReports(srcpkg) diff --git a/ubuntutools/lp/functions.py b/ubuntutools/lp/functions.py index f9a46af..7b79a47 100644 --- a/ubuntutools/lp/functions.py +++ b/ubuntutools/lp/functions.py @@ -22,45 +22,148 @@ import cookie import urlopener as lp_urlopener import urllib2 import sys +from udtexceptions import PackageNotFoundException, TeamNotFoundException, SeriesNotFoundException import libsupport as lp_libsupport import launchpadlib from re import findall +# Takes time to initialise - move to top level so we only pay the penalty +# once. Should probably make this a proper class so we can instansiate +# singleton-style (lazily). +launchpad = lp_libsupport.get_launchpad("ubuntu-dev-tools") + +def ubuntuDevelopmentSeries(): + """ Get the string repr of the current Ubuntu development series """ + + ubuntu = launchpad.distributions['ubuntu'] + return ubuntu.current_series.name + +def _ubuntuSeries(name): + """ Get the LP representation of a series + + returns the LP API repr of a series passed by name (e.g. 'karmic') + If the series is not found: raise SeriesNotFoundException + """ + + ubuntu = launchpad.distributions['ubuntu'] + try: + + return ubuntu.getSeries(name_or_version=name) + + except launchpadlib.errors.HTTPError: + + raise SeriesNotFoundException('The series %s was not found' % name) + +def _ubuntuSourcePackage(package, series): + """ Finds an Ubuntu source package on LP + + returns LP API repr of the source package + If the package does not exist: raise PackageNotFoundException + """ + + try: + + lpseries = _ubuntuSeries(series) + + ubuntu = launchpad.distributions['ubuntu'] + u_archive = ubuntu.main_archive + + component = u_archive.getPublishedSources(source_name=package, status="Published", + exact_match=True, distro_series=lpseries)[0] + + return component + + except IndexError: + + raise PackageNotFoundException("The package %s does not exist in the Ubuntu main archive" % + package) + +def packageVersion(package, series=ubuntuDevelopmentSeries()): + """ Retrieves the version of a given source package in the current + development distroseries + + returns unicode string repr of source package version + If the package does not exist: raise PackageNotFoundException + """ + + return _ubuntuSourcePackage(package, series).source_package_version + +def packageComponent(package, series=ubuntuDevelopmentSeries()): + """ Retrieves the component for a given source package + + returns unicode string representation of component + If the package does not exist: raise PackageNotFoundException + """ + + return _ubuntuSourcePackage(package, series).component_name + +def canUploadPackage(package, series=ubuntuDevelopmentSeries()): + """ Checks whether the user can upload package to Ubuntu's main archive + + Uses LP API to do this. + + If the user can upload the package: return True. + If the user cannot upload the package: return False. + If the package does not exist: raise PackageNotFoundException + """ + + ubuntu = launchpad.distributions['ubuntu'] + u_archive = ubuntu.main_archive + + uploaders = u_archive.getUploadersForComponent(component_name=packageComponent(package, series)) + + for permission in uploaders: + current_uploader = permission.person + if _findMember(current_uploader, launchpad.me): + return True + + return False + +def _findMember(haystack, needle): + """ Find a person in a haystack. A haystack can consist of either people or teams. + + If the needle is in the haystack: return True + If the needle is not in the haystack: return False + """ + + if not haystack.is_team: + return (str(haystack) == str(needle)) + else: # is a team + members = haystack.members + for m in members: + if _findMember(m, needle): + return True + + return False + def isLPTeamMember(team): """ Checks if the user is a member of a certain team on Launchpad. - We do this by opening the team page on Launchpad and checking if the - text "You are not a member of this team" is present using the - user's cookie file for authentication. + Uses the LP API. If the user is a member of the team: return True. If the user is not a member of the team: return False. + + If the team is not found: raise a TeamNotFoundException. """ - # TODO: Check if launchpadlib may be a better way of doing this. - - # Prepare cookie. - cookieFile = cookie.prepareLaunchpadCookie() - # Prepare URL opener. - urlopener = lp_urlopener.setupLaunchpadUrlOpener(cookieFile) - - # Try to open the Launchpad team page: try: - lpTeamPage = urlopener.open("https://launchpad.net/~%s" % team).read() - except urllib2.HTTPError, error: - print >> sys.stderr, "Unable to connect to Launchpad. Received a %s." % error.code - sys.exit(1) - # Check if text is present in page. - if ("You are not a member of this team") in lpTeamPage: - return False - - return True + lpteam = launchpad.people[team] + + if not lpteam.is_team: + raise KeyError + + return _findMember(lpteam, launchpad.me) + + except KeyError: + + raise TeamNotFoundException("The team %s does not exist on Launchpad" % + team) def isPerPackageUploader(package): # Checks if the user has upload privileges for a certain package. - launchpad = lp_libsupport.get_launchpad("ubuntu-dev-tools") me = findall('~(\S+)', '%s' % launchpad.me)[0] main_archive = launchpad.distributions["ubuntu"].main_archive try: diff --git a/ubuntutools/lp/udtexceptions.py b/ubuntutools/lp/udtexceptions.py new file mode 100644 index 0000000..79fa9a4 --- /dev/null +++ b/ubuntutools/lp/udtexceptions.py @@ -0,0 +1,11 @@ +class PackageNotFoundException(BaseException): + """ Thrown when a package is not found """ + pass + +class TeamNotFoundException(BaseException): + """ Thrown when a team is not found """ + pass + +class SeriesNotFoundException(BaseException): + """ Thrown when a distroseries is not found """ + pass