mirror of
https://git.launchpad.net/ubuntu-dev-tools
synced 2025-03-13 16:11:15 +00:00
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
199 lines
7.0 KiB
Python
Executable File
199 lines
7.0 KiB
Python
Executable File
#!/usr/bin/python
|
|
#
|
|
# buildd - command line interface for Launchpad buildd operations.
|
|
#
|
|
# Copyright (C) 2007 Canonical Ltd.
|
|
# Authors:
|
|
# - Martin Pitt <martin.pitt@canonical.com>
|
|
# - Jonathan Davies <jpds@ubuntu.com>
|
|
#
|
|
# 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
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
# Our modules to import.
|
|
import os
|
|
import re
|
|
import sys
|
|
import urllib2
|
|
from optparse import OptionGroup
|
|
from optparse import OptionParser
|
|
from urllib import urlencode
|
|
|
|
# ubuntu-dev-tools modules.
|
|
import ubuntutools.lp.cookie as lp_cookie
|
|
import ubuntutools.lp.functions as lp_functions
|
|
import ubuntutools.lp.urlopener as lp_urlopener
|
|
from ubuntutools import packages
|
|
# https_proxy fix
|
|
import ubuntutools.common
|
|
|
|
# Usage.
|
|
usage = "%prog <srcpackage> <release> <operation>\n\n"
|
|
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"]
|
|
|
|
# Prepare our option parser.
|
|
optParser = OptionParser(usage)
|
|
|
|
# Retry options
|
|
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",
|
|
help = "Rebuild or rescore a specific architecture. " \
|
|
"Valid architectures include: " \
|
|
"%s." % ", ".join(validArchs))
|
|
|
|
# Add the retry options to the main group.
|
|
optParser.add_option_group(retryRescoreOptions)
|
|
|
|
# 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
|
|
|
|
# Prepare Launchpad cookie.
|
|
launchpadCookie = lp_cookie.prepareLaunchpadCookie()
|
|
urlopener = lp_urlopener.setupLaunchpadUrlOpener(launchpadCookie)
|
|
|
|
# Check the release exists.
|
|
packages.checkReleaseExists(release)
|
|
|
|
# Find out the version in given release.
|
|
(page, version) = packages.checkSourceExists(package, release)
|
|
|
|
# Get the component the package is in.
|
|
component = lp_functions.packageComponent(package, release)
|
|
|
|
# Output details.
|
|
print "The source version for '%s' in %s (%s) is at %s." % (package,
|
|
release.capitalize(), component, version)
|
|
|
|
# Parse out build URLs, states, and arches.
|
|
buildstats = {}
|
|
page = urlopener.open('https://launchpad.net/ubuntu/+source/%s/%s' % (package, version))
|
|
url = page.geturl()
|
|
page = page.read()
|
|
release = release.split('-')[0] # strip off pocket
|
|
|
|
print "Current build status for this package:"
|
|
|
|
for m in re.finditer('"/ubuntu/\+source/%s/%s(/\+build/\d+)"[^\n]+\n\s*(\w+).*?<span>(\w+)</span>.*?</a>\s*([^\n]+)\n' %
|
|
(package.replace('+', '\+'), version.replace('+', '\+')), page, re.S):
|
|
if m.group(2) == release:
|
|
print '%s: %s.' % (m.group(3), m.group(4))
|
|
buildstats[url + m.group(1)] = [m.group(3).strip(), m.group(4).strip()]
|
|
|
|
# Check that there actually are builds for that release.
|
|
if len(buildstats) == 0:
|
|
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)
|
|
|
|
# Operations.
|
|
if op == 'status':
|
|
sys.exit(0)
|
|
|
|
# Check if the package in question is architecture independent, and if so; that
|
|
# i386 has been set as the architecture.
|
|
if len(buildstats) == 1 and options.architecture != "i386":
|
|
print "Overriding architecture setting to i386 for architecture " \
|
|
"independent package..."
|
|
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": necessaryPrivs = lp_functions.isLPTeamMember('launchpad-buildd-admins')
|
|
if op == "retry":
|
|
necessaryPrivs = lp_functions.canUploadPackage(package, release)
|
|
|
|
if not necessaryPrivs:
|
|
print >> sys.stderr, "You cannot perform the %s operation on a %s package " \
|
|
"as you are not member of the '%s' team on Launchpad." % (op, component,
|
|
teamNeeded)
|
|
print "Should this be incorrect, please log in to Launchpad using Firefox, " \
|
|
"delete the ~/.lpcookie.txt file and rerun this script."
|
|
sys.exit(1)
|
|
|
|
for build, (arch, status) in buildstats.iteritems():
|
|
if oneArch and not options.architecture == arch:
|
|
# Skip this architecture.
|
|
continue
|
|
|
|
if op in ('rescore'):
|
|
if status in ('Needs building'):
|
|
print 'Rescoring', build, '(%s).' % arch
|
|
|
|
try:
|
|
urlopener.open(build + '/+rescore', urlencode(
|
|
{'field.priority': '5000', 'field.actions.rescore': '1'}))
|
|
except:
|
|
print >> sys.stderr, "Unable to request rescore on %s." % arch
|
|
else:
|
|
print "Not rescoring on %s; status is: %s." % (arch, status.lower())
|
|
|
|
if op in ('retry'): # Retry requested.
|
|
if status in ('Failed to build', 'Chroot problem', 'Failed to upload'):
|
|
print 'Retrying:', build, '(%s).' % arch
|
|
|
|
try:
|
|
urlopener.open(build + '/+retry', urlencode(
|
|
{'RETRY': '1'}))
|
|
except: # Error encountered while submitting request.
|
|
print >> sys.stderr, "Unable to request retry on %s." % arch
|
|
else: # The package does not require rebuilding.
|
|
print "Not retrying on %s; status is %s." % (arch, status.lower())
|