2010-12-11 14:12:02 -08:00
|
|
|
#!/usr/bin/python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# ##################################################################
|
|
|
|
#
|
|
|
|
# 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; version 2.
|
|
|
|
#
|
|
|
|
# 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.
|
|
|
|
#
|
|
|
|
# See file /usr/share/common-licenses/GPL-2 for more details.
|
|
|
|
#
|
|
|
|
# ##################################################################
|
|
|
|
|
|
|
|
import logging
|
|
|
|
import optparse
|
|
|
|
import os
|
|
|
|
import shutil
|
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
import tempfile
|
|
|
|
|
2010-12-12 16:36:51 -08:00
|
|
|
from debian.deb822 import Dsc
|
2010-12-12 18:12:28 -08:00
|
|
|
import launchpadlib.launchpad
|
2010-12-11 14:12:02 -08:00
|
|
|
|
|
|
|
devnull = open('/dev/null', 'r+')
|
2010-12-12 18:12:28 -08:00
|
|
|
lp = None
|
2010-12-11 14:12:02 -08:00
|
|
|
|
|
|
|
def error(msg, *args, **kwargs):
|
|
|
|
logging.error(msg, *args, **kwargs)
|
|
|
|
sys.exit(1)
|
|
|
|
|
2010-12-12 18:24:28 -08:00
|
|
|
def check_call(cmd, *args, **kwargs):
|
|
|
|
ret = subprocess.call(cmd, *args, **kwargs)
|
|
|
|
if ret != 0:
|
|
|
|
error('%s returned %d' % (cmd, ret))
|
|
|
|
|
2010-12-11 14:12:02 -08:00
|
|
|
def parse(args):
|
2010-12-12 16:36:51 -08:00
|
|
|
usage = 'Usage: %prog [options]'
|
2010-12-11 14:12:02 -08:00
|
|
|
p = optparse.OptionParser(usage)
|
2010-12-12 16:36:51 -08:00
|
|
|
p.add_option('-t', '--to',
|
|
|
|
dest='dest_releases',
|
|
|
|
default=[],
|
|
|
|
action='append',
|
|
|
|
help='Backport to DEST release (required)',
|
|
|
|
metavar='DEST')
|
|
|
|
p.add_option('-f', '--from',
|
|
|
|
dest='source_release',
|
|
|
|
default=None,
|
2010-12-12 18:25:59 -08:00
|
|
|
help='Backport from SOURCE release (default: devel release)',
|
2010-12-12 16:36:51 -08:00
|
|
|
metavar='SOURCE')
|
2010-12-12 18:12:28 -08:00
|
|
|
p.add_option('-v', '--version',
|
|
|
|
dest='version',
|
|
|
|
default=None,
|
2010-12-12 18:25:59 -08:00
|
|
|
help='Package version to backport (or verify)',
|
2010-12-12 18:12:28 -08:00
|
|
|
metavar='VERSION')
|
2010-12-12 16:36:51 -08:00
|
|
|
p.add_option('-s', '--source',
|
|
|
|
dest='package',
|
|
|
|
help='Backport SOURCE package (required)',
|
|
|
|
metavar='SOURCE')
|
|
|
|
p.add_option('-u', '--upload',
|
|
|
|
dest='upload',
|
|
|
|
help='Specify an upload destination (required)',
|
|
|
|
metavar='UPLOAD')
|
2010-12-12 18:12:28 -08:00
|
|
|
p.add_option('-l', '--launchpad',
|
|
|
|
dest='launchpad',
|
|
|
|
default='production',
|
2010-12-12 18:25:59 -08:00
|
|
|
help='Launchpad instance to connect to (default: %default)',
|
2010-12-12 18:12:28 -08:00
|
|
|
metavar='INSTANCE')
|
2010-12-11 14:12:02 -08:00
|
|
|
|
|
|
|
opts, args = p.parse_args(args)
|
2010-12-12 16:36:51 -08:00
|
|
|
if len(args):
|
2010-12-11 14:12:02 -08:00
|
|
|
p.error('Invalid arguments')
|
2010-12-12 16:36:51 -08:00
|
|
|
if not opts.package:
|
|
|
|
p.error('You must specify a package to backport')
|
|
|
|
if not opts.dest_releases:
|
|
|
|
p.error('You must specify at least one destination release')
|
|
|
|
if not opts.upload:
|
|
|
|
p.error('You must specify an upload destination')
|
2010-12-11 14:12:02 -08:00
|
|
|
|
|
|
|
return opts, args
|
|
|
|
|
2010-12-12 18:12:28 -08:00
|
|
|
def find_release_package(workdir, opts):
|
|
|
|
ubuntu = lp.distributions['ubuntu']
|
|
|
|
archive = ubuntu.main_archive
|
|
|
|
series = ubuntu.getSeries(name_or_version=opts.source_release)
|
|
|
|
status = 'Published'
|
|
|
|
for pocket in ('Updates', 'Security', 'Release'):
|
|
|
|
try:
|
|
|
|
srcpkg = archive.getPublishedSources(source_name=opts.package,
|
|
|
|
distro_series=series,
|
|
|
|
pocket=pocket,
|
|
|
|
status=status,
|
|
|
|
exact_match=True)[0]
|
|
|
|
break
|
|
|
|
except IndexError:
|
|
|
|
continue
|
|
|
|
else:
|
2010-12-12 18:25:59 -08:00
|
|
|
error('Unable to find package %s in release %s' %
|
|
|
|
(package, opts.source_release))
|
2010-12-12 18:12:28 -08:00
|
|
|
|
|
|
|
if opts.version and opts.version != srcpkg.source_package_version:
|
2010-12-12 18:25:59 -08:00
|
|
|
error('Requested backport of version %s but %s is at version %s' %
|
|
|
|
(opts.version, opts.package, srcpkg.source_package_version))
|
2010-12-12 18:12:28 -08:00
|
|
|
|
|
|
|
return srcpkg
|
|
|
|
|
|
|
|
def find_version_package(workdir, opts):
|
|
|
|
ubuntu = lp.distributions['ubuntu']
|
|
|
|
archive = ubuntu.main_archive
|
|
|
|
try:
|
|
|
|
# Might get more than one (i.e. same version in multiple
|
|
|
|
# releases), but they should all be identical
|
|
|
|
return archive.getPublishedSources(source_name=opts.package,
|
|
|
|
version=opts.version)[0]
|
|
|
|
except IndexError:
|
|
|
|
error('Package %s was never published with version %s in Ubuntu' %
|
|
|
|
(opts.package, opts.version))
|
|
|
|
|
|
|
|
def fetch_package(workdir, opts):
|
|
|
|
# Returns the path to the .dsc file that was fetched
|
|
|
|
ubuntu = lp.distributions['ubuntu']
|
|
|
|
|
|
|
|
if not opts.source_release and not opts.version:
|
|
|
|
opts.source_release = lp.distributions['ubuntu'].current_series.name
|
|
|
|
|
|
|
|
# If source_release is specified, then version is just for
|
|
|
|
# verification
|
|
|
|
if opts.source_release:
|
|
|
|
srcpkg = find_release_package(workdir, opts)
|
|
|
|
else:
|
|
|
|
srcpkg = find_version_package(workdir, opts)
|
|
|
|
|
|
|
|
for f in srcpkg.sourceFileUrls():
|
|
|
|
if f.endswith('.dsc'):
|
2010-12-12 18:24:28 -08:00
|
|
|
check_call(['dget',
|
|
|
|
'--download-only',
|
|
|
|
'--allow-unauthenticated',
|
|
|
|
f],
|
|
|
|
cwd=workdir)
|
2010-12-12 18:12:28 -08:00
|
|
|
return os.path.join(workdir, os.path.basename(f))
|
|
|
|
else:
|
|
|
|
error('Package %s contains no .dsc file' % opts.package)
|
|
|
|
|
2010-12-11 14:12:02 -08:00
|
|
|
def main(args):
|
2010-12-12 18:12:28 -08:00
|
|
|
global lp
|
|
|
|
|
2010-12-11 14:12:02 -08:00
|
|
|
logging.basicConfig(level=logging.INFO)
|
2010-12-11 14:43:31 -08:00
|
|
|
os.environ['DEB_VENDOR'] = 'Ubuntu'
|
2010-12-11 14:12:02 -08:00
|
|
|
|
2010-12-12 16:36:51 -08:00
|
|
|
opts, _ = parse(args[1:])
|
|
|
|
package = opts.package
|
|
|
|
source_release = opts.source_release
|
|
|
|
dest_releases = opts.dest_releases
|
|
|
|
upload = opts.upload
|
2010-12-11 14:12:02 -08:00
|
|
|
|
2010-12-12 18:12:28 -08:00
|
|
|
script_name = os.path.basename(sys.argv[0])
|
|
|
|
lp = launchpadlib.launchpad.Launchpad.login_anonymously(script_name,
|
|
|
|
opts.launchpad)
|
|
|
|
|
2010-12-11 15:49:18 -08:00
|
|
|
tmpdir = tempfile.mkdtemp(prefix='backportpackage-')
|
2010-12-11 14:12:02 -08:00
|
|
|
try:
|
2010-12-12 18:12:28 -08:00
|
|
|
dscfile = fetch_package(tmpdir, opts)
|
2010-12-12 16:36:51 -08:00
|
|
|
|
|
|
|
dsc = Dsc(open(os.path.join(tmpdir, dscfile)))
|
|
|
|
v = dsc['Version']
|
|
|
|
|
|
|
|
for dest_release in dest_releases:
|
|
|
|
srcdir = os.path.join(tmpdir, package)
|
2010-12-12 18:24:28 -08:00
|
|
|
check_call(['dpkg-source',
|
|
|
|
'-x',
|
|
|
|
dscfile,
|
|
|
|
package],
|
|
|
|
cwd=tmpdir)
|
2010-12-12 16:36:51 -08:00
|
|
|
|
|
|
|
bp_version = v + ('~%s1' % dest_release)
|
|
|
|
bp_dist = dest_release
|
|
|
|
if upload.startswith('ppa:'):
|
|
|
|
bp_version += '~ppa1'
|
|
|
|
elif upload == 'ubuntu':
|
|
|
|
bp_dist += '-backports'
|
2010-12-12 18:24:28 -08:00
|
|
|
check_call(['dch',
|
|
|
|
'--force-bad-version',
|
|
|
|
'--preserve',
|
|
|
|
'--newversion', bp_version,
|
|
|
|
'--distribution', dest_release,
|
|
|
|
'No-change backport to %s' % dest_release],
|
|
|
|
cwd=srcdir)
|
|
|
|
check_call(['debuild', '-S', '-sa'],
|
|
|
|
cwd=srcdir)
|
2010-12-12 16:36:51 -08:00
|
|
|
|
|
|
|
if ':' in bp_version:
|
|
|
|
bp_version = bp_version[bp_version.find(':')+1:]
|
|
|
|
|
|
|
|
print 'Please check the package in file://%s carefully' % tmpdir
|
2010-12-12 18:26:25 -08:00
|
|
|
prompt = 'Do you want to upload this to %s? [Y/n]' % upload
|
2010-12-12 16:36:51 -08:00
|
|
|
while True:
|
2010-12-12 18:25:59 -08:00
|
|
|
answer = raw_input(prompt).strip().lower()
|
2010-12-12 16:36:51 -08:00
|
|
|
if answer in ('', 'y', 'yes'):
|
2010-12-12 18:24:28 -08:00
|
|
|
check_call(['dput',
|
|
|
|
upload,
|
2010-12-12 18:25:59 -08:00
|
|
|
'%s_%s_source.changes' %
|
|
|
|
(package, bp_version)],
|
2010-12-12 18:24:28 -08:00
|
|
|
cwd=tmpdir)
|
2010-12-12 16:36:51 -08:00
|
|
|
|
|
|
|
break
|
|
|
|
elif answer in ('n', 'no'):
|
|
|
|
break
|
|
|
|
shutil.rmtree(srcdir)
|
2010-12-11 14:12:02 -08:00
|
|
|
finally:
|
|
|
|
shutil.rmtree(tmpdir)
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
sys.exit(main(sys.argv))
|