2011-11-07 23:20:37 +02:00
|
|
|
#!/usr/bin/python
|
|
|
|
#
|
|
|
|
# Copyright (C) 2011, Stefano Rivera <stefanor@debian.org>
|
|
|
|
#
|
|
|
|
# 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 distro_info import UbuntuDistroInfo
|
|
|
|
|
2011-11-07 23:35:14 +02:00
|
|
|
from ubuntutools.lp.lpapicache import Launchpad, Distribution
|
2011-11-07 23:20:37 +02:00
|
|
|
from ubuntutools.lp.udtexceptions import PackageNotFoundException
|
|
|
|
from ubuntutools.config import UDTConfig
|
|
|
|
from ubuntutools.requestsync.common import edit_report
|
|
|
|
from ubuntutools.question import YesNoQuestion
|
|
|
|
|
2011-11-07 23:35:14 +02:00
|
|
|
|
2011-11-07 23:20:37 +02:00
|
|
|
class DestinationException(Exception):
|
|
|
|
pass
|
|
|
|
|
2011-11-07 23:35:14 +02:00
|
|
|
|
2011-11-07 23:20:37 +02:00
|
|
|
def determine_destinations(source, destination):
|
|
|
|
ubuntu_info = UbuntuDistroInfo()
|
|
|
|
if destination is None:
|
|
|
|
destination = ubuntu_info.stable()
|
|
|
|
|
|
|
|
if source not in ubuntu_info.all:
|
|
|
|
raise DestinationException("Source release %s does not exist" % source)
|
|
|
|
if destination not in ubuntu_info.all:
|
|
|
|
raise DestinationException("Destination release %s does not exist"
|
|
|
|
% destination)
|
|
|
|
if destination not in ubuntu_info.supported():
|
|
|
|
raise DestinationException("Destination release %s is not supported"
|
|
|
|
% destination)
|
|
|
|
|
|
|
|
found = False
|
|
|
|
destinations = []
|
|
|
|
support_gap = False
|
|
|
|
for release in ubuntu_info.all:
|
|
|
|
if release == destination:
|
|
|
|
found = True
|
|
|
|
if release == source:
|
|
|
|
break
|
|
|
|
if found:
|
2011-11-08 00:03:45 +02:00
|
|
|
if support_gap:
|
|
|
|
if ubuntu_info.is_lts(release):
|
|
|
|
support_gap = False
|
|
|
|
else:
|
|
|
|
continue
|
2011-11-07 23:20:37 +02:00
|
|
|
if release not in ubuntu_info.supported():
|
|
|
|
support_gap = True
|
|
|
|
continue
|
|
|
|
destinations.append(release)
|
|
|
|
|
|
|
|
assert found
|
|
|
|
assert len(destinations) > 0
|
|
|
|
|
|
|
|
return destinations
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
parser = optparse.OptionParser('%progname [options] package')
|
|
|
|
parser.add_option('-d', '--destination', metavar='DEST',
|
|
|
|
help='Backport to DEST release and necessary '
|
|
|
|
'intermediate releases '
|
|
|
|
'(default: current stable release)')
|
|
|
|
parser.add_option('-s', '--source', metavar='SOURCE',
|
|
|
|
help='Backport from SOURCE release '
|
|
|
|
'(default: current devel release)')
|
|
|
|
parser.add_option('-l', '--lpinstance', metavar='INSTANCE', default=None,
|
|
|
|
help='Launchpad instance to connect to '
|
|
|
|
'(default: production).')
|
|
|
|
parser.add_option('--no-conf', action='store_true',
|
|
|
|
dest='no_conf', default=False,
|
|
|
|
help="Don't read config files or environment variables")
|
|
|
|
options, args = parser.parse_args()
|
|
|
|
|
|
|
|
if len(args) != 1:
|
|
|
|
parser.error("One (and only one) package must be specified")
|
|
|
|
package = args[0]
|
|
|
|
|
|
|
|
config = UDTConfig(options.no_conf)
|
|
|
|
|
|
|
|
if options.lpinstance is None:
|
|
|
|
options.lpinstance = config.get_value('LPINSTANCE')
|
|
|
|
Launchpad.login(options.lpinstance)
|
|
|
|
|
|
|
|
if options.source is None:
|
|
|
|
options.source = Distribution('ubuntu').getDevelopmentSeries().name
|
|
|
|
|
|
|
|
try:
|
|
|
|
destinations = determine_destinations(options.source,
|
|
|
|
options.destination)
|
|
|
|
except DestinationException, e:
|
|
|
|
print str(e)
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
request_backport(package, options.source, destinations)
|
|
|
|
|
|
|
|
|
|
|
|
def request_backport(package, source, destinations):
|
|
|
|
archive = Distribution('ubuntu').getArchive()
|
|
|
|
try:
|
|
|
|
package_spph = archive.getSourcePackage(package, source)
|
|
|
|
except PackageNotFoundException, e:
|
|
|
|
print str(e)
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
subst = {
|
|
|
|
'package': package_spph.getPackageName(),
|
|
|
|
'version': package_spph.getVersion(),
|
|
|
|
'component': package_spph.getComponent(),
|
|
|
|
'source': source,
|
|
|
|
'destinations': ', '.join(destinations),
|
|
|
|
}
|
|
|
|
subject = ("Please backport %(package)s %(version)s (%(component)s) "
|
|
|
|
"from %(source)s" % subst)
|
|
|
|
body = ("Please backport %(package)s %(version)s (%(component)s) "
|
|
|
|
"from %(source)s to %(destinations)s.\n\n"
|
|
|
|
"Reason for the backport:\n"
|
|
|
|
"<<< Enter your reasoning here >>>\n\n"
|
|
|
|
"Testing performed:\n"
|
|
|
|
"<<< Mention any build & install tests you've done >>>\n"
|
|
|
|
"<<< List the reverse dependencies that you've tested >>>\n"
|
|
|
|
% subst)
|
|
|
|
|
|
|
|
subject, body = edit_report(subject, body, changes_required=True)
|
|
|
|
|
|
|
|
print ('The final report is:\nSummary: %s\nDescription:\n%s\n'
|
|
|
|
% (subject, body))
|
|
|
|
if YesNoQuestion().ask("Request this backport", "yes") == "no":
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
targets = [Launchpad.projects['%s-backports' % destination]
|
|
|
|
for destination in destinations]
|
|
|
|
bug = Launchpad.bugs.createBug(title=subject, description=body,
|
|
|
|
target=targets[0])
|
|
|
|
for target in targets[1:]:
|
|
|
|
bug.addTask(target=target)
|
|
|
|
|
|
|
|
print "Backport request filed as %s" % bug.web_link
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|