2010-12-11 14:12:02 -08:00
|
|
|
#!/usr/bin/python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# ##################################################################
|
|
|
|
#
|
2010-12-18 12:09:44 -08:00
|
|
|
# Copyright (C) 2010, Evan Broder <evan@ebroder.net>
|
2010-12-18 12:58:11 -08:00
|
|
|
# Copyright (C) 2010, Benjamin Drung <bdrung@ubuntu.com>
|
2010-12-18 12:09:44 -08:00
|
|
|
#
|
2010-12-11 14:12:02 -08:00
|
|
|
# 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 optparse
|
|
|
|
import os
|
|
|
|
import shutil
|
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
import tempfile
|
2010-12-15 11:00:02 -08:00
|
|
|
import urllib
|
2010-12-11 14:12:02 -08:00
|
|
|
|
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-15 20:50:30 -08:00
|
|
|
import lsb_release
|
2010-12-11 14:12:02 -08:00
|
|
|
|
2010-12-12 20:11:49 -08:00
|
|
|
from ubuntutools.builder import getBuilder
|
2010-12-12 19:27:08 -08:00
|
|
|
from ubuntutools.logger import Logger
|
2010-12-18 19:22:07 +01:00
|
|
|
from ubuntutools.question import YesNoQuestion
|
2010-12-12 19:27:08 -08:00
|
|
|
|
|
|
|
def error(msg):
|
|
|
|
Logger.error(msg)
|
2010-12-11 14:12:02 -08:00
|
|
|
sys.exit(1)
|
|
|
|
|
2010-12-12 18:24:28 -08:00
|
|
|
def check_call(cmd, *args, **kwargs):
|
2010-12-12 19:27:08 -08:00
|
|
|
Logger.command(cmd)
|
2010-12-12 18:24:28 -08:00
|
|
|
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-16 01:05:29 -08:00
|
|
|
usage = 'Usage: %prog [options] <source package name or .dsc URL/file>'
|
2010-12-11 14:12:02 -08:00
|
|
|
p = optparse.OptionParser(usage)
|
2010-12-15 20:19:28 -08:00
|
|
|
p.add_option('-d', '--destination',
|
2010-12-12 16:36:51 -08:00
|
|
|
dest='dest_releases',
|
|
|
|
default=[],
|
|
|
|
action='append',
|
2010-12-15 20:50:30 -08:00
|
|
|
help='Backport to DEST release (default: current release)',
|
2010-12-12 16:36:51 -08:00
|
|
|
metavar='DEST')
|
2010-12-15 20:19:28 -08:00
|
|
|
p.add_option('-s', '--source',
|
2010-12-12 16:36:51 -08:00
|
|
|
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-18 12:16:02 -08:00
|
|
|
p.add_option('-S', '--suffix',
|
|
|
|
dest='suffix',
|
|
|
|
default=None,
|
|
|
|
help='Suffix to append to version number (default: ~ppa1)',
|
|
|
|
metavar='SUFFIX')
|
2010-12-12 20:11:49 -08:00
|
|
|
p.add_option('-b', '--build',
|
|
|
|
dest='build',
|
|
|
|
default=False,
|
|
|
|
action='store_true',
|
|
|
|
help='Build the package before uploading (default: %default)')
|
|
|
|
p.add_option('-B', '--builder',
|
|
|
|
dest='builder',
|
|
|
|
default=None,
|
|
|
|
help='Specify the package builder (default: pbuilder)',
|
|
|
|
metavar='BUILDER')
|
2010-12-13 04:00:14 -08:00
|
|
|
p.add_option('-U', '--update',
|
|
|
|
dest='update',
|
|
|
|
default=False,
|
|
|
|
action='store_true',
|
|
|
|
help='Update the build environment before attempting to build')
|
2010-12-12 20:11:49 -08:00
|
|
|
p.add_option('-u', '--upload',
|
|
|
|
dest='upload',
|
|
|
|
help='Specify an upload destination',
|
|
|
|
metavar='UPLOAD')
|
2010-12-18 12:29:38 -08:00
|
|
|
p.add_option('-y', '--yes',
|
|
|
|
dest='prompt',
|
|
|
|
default=True,
|
|
|
|
action='store_false',
|
|
|
|
help='Do not prompt before uploading to a PPA')
|
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-16 00:06:57 -08:00
|
|
|
p.add_option('-w', '--workdir',
|
|
|
|
dest='workdir',
|
|
|
|
default=None,
|
|
|
|
help='Specify a working directory (default: temporary dir)',
|
|
|
|
metavar='WORKDIR')
|
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 18:54:04 -08:00
|
|
|
if len(args) != 1:
|
2010-12-16 01:05:29 -08:00
|
|
|
p.error('You must specify a single source package or a .dsc URL/path')
|
2010-12-18 12:32:48 -08:00
|
|
|
if not opts.upload and not opts.workdir:
|
|
|
|
p.error('Please specify either a working dir or an upload target')
|
2010-12-11 14:12:02 -08:00
|
|
|
|
|
|
|
return opts, args
|
|
|
|
|
2010-12-15 20:39:50 -08:00
|
|
|
def find_release_package(lp, package, version, source_release):
|
2010-12-12 18:12:28 -08:00
|
|
|
ubuntu = lp.distributions['ubuntu']
|
|
|
|
archive = ubuntu.main_archive
|
2010-12-15 20:39:50 -08:00
|
|
|
series = ubuntu.getSeries(name_or_version=source_release)
|
2010-12-12 18:12:28 -08:00
|
|
|
status = 'Published'
|
|
|
|
for pocket in ('Updates', 'Security', 'Release'):
|
|
|
|
try:
|
2010-12-12 18:54:04 -08:00
|
|
|
srcpkg = archive.getPublishedSources(source_name=package,
|
2010-12-12 18:12:28 -08:00
|
|
|
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' %
|
2010-12-15 20:39:50 -08:00
|
|
|
(package, source_release))
|
2010-12-12 18:12:28 -08:00
|
|
|
|
2010-12-15 20:39:50 -08:00
|
|
|
if version and 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' %
|
2010-12-15 20:39:50 -08:00
|
|
|
(version, package, srcpkg.source_package_version))
|
2010-12-12 18:12:28 -08:00
|
|
|
|
|
|
|
return srcpkg
|
|
|
|
|
2010-12-15 20:39:50 -08:00
|
|
|
def find_version_package(lp, package, version):
|
2010-12-12 18:12:28 -08:00
|
|
|
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
|
2010-12-12 18:54:04 -08:00
|
|
|
return archive.getPublishedSources(source_name=package,
|
2010-12-15 20:39:50 -08:00
|
|
|
version=version)[0]
|
2010-12-12 18:12:28 -08:00
|
|
|
except IndexError:
|
2010-12-15 20:39:50 -08:00
|
|
|
error('Version %s of package %s was never published in Ubuntu' %
|
|
|
|
(version, package))
|
2010-12-12 18:12:28 -08:00
|
|
|
|
2010-12-16 01:05:29 -08:00
|
|
|
def dscurl_from_package(lp, workdir, package, version, source_release):
|
2010-12-15 20:39:50 -08:00
|
|
|
if not source_release and not version:
|
|
|
|
source_release = lp.distributions['ubuntu'].current_series.name
|
2010-12-12 18:12:28 -08:00
|
|
|
|
2010-12-16 23:43:48 +01:00
|
|
|
# If source_release is specified, then version is just for verification
|
2010-12-15 20:39:50 -08:00
|
|
|
if source_release:
|
|
|
|
srcpkg = find_release_package(lp, package, version, source_release)
|
2010-12-12 18:12:28 -08:00
|
|
|
else:
|
2010-12-15 20:39:50 -08:00
|
|
|
srcpkg = find_version_package(lp, package, version)
|
2010-12-12 18:12:28 -08:00
|
|
|
|
|
|
|
for f in srcpkg.sourceFileUrls():
|
|
|
|
if f.endswith('.dsc'):
|
2010-12-16 01:05:29 -08:00
|
|
|
return urllib.unquote(f)
|
2010-12-12 18:12:28 -08:00
|
|
|
else:
|
2010-12-12 18:54:04 -08:00
|
|
|
error('Package %s contains no .dsc file' % package)
|
2010-12-12 18:12:28 -08:00
|
|
|
|
2010-12-16 01:05:29 -08:00
|
|
|
def dscurl_from_dsc(package):
|
|
|
|
path = os.path.abspath(os.path.expanduser(package))
|
|
|
|
if os.path.exists(path):
|
|
|
|
return 'file://%s' % path
|
|
|
|
else:
|
2010-12-16 23:43:48 +01:00
|
|
|
# Can't resolve it as a local path? Let's just hope it's good as-is
|
2010-12-16 01:05:29 -08:00
|
|
|
return package
|
|
|
|
|
|
|
|
def fetch_package(lp, workdir, package, version, source_release):
|
|
|
|
# Returns the path to the .dsc file that was fetched
|
|
|
|
|
|
|
|
if package.endswith('.dsc'):
|
|
|
|
dsc = dscurl_from_dsc(package)
|
|
|
|
else:
|
|
|
|
dsc = dscurl_from_package(lp, workdir, package, version, source_release)
|
|
|
|
|
2010-12-16 23:43:48 +01:00
|
|
|
check_call(['dget', '--download-only', '--allow-unauthenticated', dsc],
|
2010-12-16 01:05:29 -08:00
|
|
|
cwd=workdir)
|
|
|
|
return os.path.join(workdir, os.path.basename(dsc))
|
|
|
|
|
2010-12-18 12:16:02 -08:00
|
|
|
def get_backport_version(version, suffix, upload, release):
|
2010-12-12 18:48:50 -08:00
|
|
|
v = version + ('~%s1' % release)
|
2010-12-18 12:33:10 -08:00
|
|
|
if suffix is not None:
|
2010-12-18 12:16:02 -08:00
|
|
|
v += suffix
|
|
|
|
elif upload and upload.startswith('ppa:'):
|
2010-12-12 18:48:50 -08:00
|
|
|
v += '~ppa1'
|
|
|
|
return v
|
|
|
|
|
|
|
|
def get_backport_dist(upload, release):
|
2010-12-12 20:11:49 -08:00
|
|
|
if not upload or upload == 'ubuntu':
|
2010-12-12 18:48:50 -08:00
|
|
|
return '%s-backports' % release
|
|
|
|
else:
|
|
|
|
return release
|
|
|
|
|
2010-12-18 18:17:29 +01:00
|
|
|
def do_build(workdir, package, release, bp_version, builder, update):
|
2010-12-15 20:39:50 -08:00
|
|
|
builder = getBuilder(builder)
|
2010-12-12 20:11:49 -08:00
|
|
|
if not builder:
|
|
|
|
return
|
|
|
|
|
2010-12-18 18:17:29 +01:00
|
|
|
if update:
|
2010-12-17 01:36:05 -08:00
|
|
|
if 0 != builder.update(release):
|
|
|
|
error('Failed to update %s chroot for %s.' % \
|
2010-12-17 13:48:05 +01:00
|
|
|
(release, builder.get_name()))
|
2010-12-13 04:00:14 -08:00
|
|
|
|
2010-12-15 11:02:07 -08:00
|
|
|
return builder.build(os.path.join(workdir,
|
|
|
|
'%s_%s.dsc' % (package, bp_version)),
|
|
|
|
release,
|
2010-12-18 20:35:20 +01:00
|
|
|
os.path.join(workdir, "buildresult"))
|
2010-12-12 20:11:49 -08:00
|
|
|
|
2010-12-18 12:29:38 -08:00
|
|
|
def do_upload(workdir, package, bp_version, upload, prompt):
|
2010-12-18 19:37:11 +01:00
|
|
|
print 'Please check %s %s in file://%s carefully!' % \
|
|
|
|
(package, bp_version, workdir)
|
2010-12-18 12:29:38 -08:00
|
|
|
if prompt or upload == 'ubuntu':
|
|
|
|
question = 'Do you want to upload the package to %s' % upload
|
|
|
|
answer = YesNoQuestion().ask(question, "yes")
|
2010-12-18 13:11:27 -08:00
|
|
|
if answer == "no":
|
2010-12-18 12:29:38 -08:00
|
|
|
return
|
|
|
|
|
|
|
|
changes_file = '%s_%s_source.changes' % (package, bp_version)
|
|
|
|
check_call(['dput', upload, changes_file], cwd=workdir)
|
2010-12-12 20:11:49 -08:00
|
|
|
|
|
|
|
|
2010-12-18 12:16:02 -08:00
|
|
|
def do_backport(workdir, package, dscfile, version, suffix, release, build,
|
2010-12-18 12:29:38 -08:00
|
|
|
builder, update, upload, prompt):
|
2010-12-16 23:43:48 +01:00
|
|
|
check_call(['dpkg-source', '-x', dscfile, package], cwd=workdir)
|
2010-12-12 18:54:04 -08:00
|
|
|
srcdir = os.path.join(workdir, package)
|
2010-12-12 18:48:50 -08:00
|
|
|
|
2010-12-18 12:16:02 -08:00
|
|
|
bp_version = get_backport_version(version, suffix, upload, release)
|
2010-12-15 20:39:50 -08:00
|
|
|
bp_dist = get_backport_dist(upload, release)
|
2010-12-12 18:48:50 -08:00
|
|
|
|
|
|
|
check_call(['dch',
|
|
|
|
'--force-bad-version',
|
|
|
|
'--preserve',
|
|
|
|
'--newversion', bp_version,
|
2010-12-12 20:19:37 -08:00
|
|
|
'--distribution', bp_dist,
|
2010-12-12 18:48:50 -08:00
|
|
|
'No-change backport to %s' % release],
|
|
|
|
cwd=srcdir)
|
2010-12-18 22:32:19 +01:00
|
|
|
check_call(['debuild', '--no-lintian', '-S', '-sa'], cwd=srcdir)
|
2010-12-12 18:48:50 -08:00
|
|
|
|
|
|
|
if ':' in bp_version:
|
|
|
|
bp_version = bp_version[bp_version.find(':')+1:]
|
|
|
|
|
2010-12-15 20:39:50 -08:00
|
|
|
if build:
|
2010-12-18 18:17:29 +01:00
|
|
|
if 0 != do_build(workdir, package, release, bp_version, builder, update):
|
2010-12-15 11:02:07 -08:00
|
|
|
error('Package failed to build; aborting')
|
2010-12-15 20:39:50 -08:00
|
|
|
if upload:
|
2010-12-18 12:29:38 -08:00
|
|
|
do_upload(workdir, package, bp_version, upload, prompt)
|
2010-12-12 18:48:50 -08:00
|
|
|
|
2010-12-12 20:11:49 -08:00
|
|
|
shutil.rmtree(srcdir)
|
2010-12-12 18:48:50 -08:00
|
|
|
|
2010-12-11 14:12:02 -08:00
|
|
|
def main(args):
|
2010-12-11 14:43:31 -08:00
|
|
|
os.environ['DEB_VENDOR'] = 'Ubuntu'
|
2010-12-11 14:12:02 -08:00
|
|
|
|
2010-12-16 01:05:29 -08:00
|
|
|
opts, (package_or_dsc,) = parse(args[1:])
|
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-15 20:50:30 -08:00
|
|
|
if not opts.dest_releases:
|
|
|
|
try:
|
|
|
|
distinfo = lsb_release.get_distro_information()
|
|
|
|
opts.dest_releases = [distinfo['CODENAME']]
|
|
|
|
except:
|
|
|
|
error('No destination release specified and unable to guess yours')
|
|
|
|
|
2010-12-16 00:06:57 -08:00
|
|
|
if opts.workdir:
|
|
|
|
workdir = os.path.expanduser(opts.workdir)
|
|
|
|
else:
|
|
|
|
workdir = tempfile.mkdtemp(prefix='backportpackage-')
|
|
|
|
|
|
|
|
if not os.path.exists(workdir):
|
|
|
|
os.makedirs(workdir)
|
|
|
|
|
2010-12-11 14:12:02 -08:00
|
|
|
try:
|
2010-12-15 20:39:50 -08:00
|
|
|
dscfile = fetch_package(lp,
|
|
|
|
workdir,
|
2010-12-16 01:05:29 -08:00
|
|
|
package_or_dsc,
|
2010-12-15 20:39:50 -08:00
|
|
|
opts.version,
|
|
|
|
opts.source_release)
|
2010-12-12 18:48:50 -08:00
|
|
|
|
2010-12-16 01:05:29 -08:00
|
|
|
dsc = Dsc(open(os.path.join(workdir, dscfile)))
|
|
|
|
package = dsc['Source']
|
|
|
|
version = dsc['Version']
|
2010-12-12 18:48:50 -08:00
|
|
|
|
|
|
|
for release in opts.dest_releases:
|
2010-12-15 20:39:50 -08:00
|
|
|
do_backport(workdir,
|
|
|
|
package,
|
|
|
|
dscfile,
|
2010-12-16 01:05:29 -08:00
|
|
|
version,
|
2010-12-18 12:21:40 -08:00
|
|
|
opts.suffix,
|
2010-12-15 20:39:50 -08:00
|
|
|
release,
|
|
|
|
opts.build,
|
|
|
|
opts.builder,
|
2010-12-18 18:17:29 +01:00
|
|
|
opts.update,
|
2010-12-18 12:29:38 -08:00
|
|
|
opts.upload,
|
|
|
|
opts.prompt)
|
2010-12-11 14:12:02 -08:00
|
|
|
finally:
|
2010-12-16 00:06:57 -08:00
|
|
|
if not opts.workdir:
|
|
|
|
shutil.rmtree(workdir)
|
2010-12-11 14:12:02 -08:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
sys.exit(main(sys.argv))
|