ubuntu-dev-tools/backportpackage

355 lines
13 KiB
Plaintext
Raw Normal View History

#!/usr/bin/python
# -*- coding: utf-8 -*-
# ##################################################################
#
# Copyright (C) 2010-2011, Evan Broder <evan@ebroder.net>
# Copyright (C) 2010, Benjamin Drung <bdrung@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; 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 glob
import optparse
import os
import shutil
import sys
import tempfile
import lsb_release
from devscripts.logger import Logger
from httplib2 import Http, HttpLib2Error
2011-06-24 17:50:10 +02:00
from ubuntutools.archive import (SourcePackage, DebianSourcePackage,
2011-12-21 22:13:25 +01:00
UbuntuSourcePackage, DownloadError)
from ubuntutools.config import UDTConfig, ubu_email
from ubuntutools.builder import get_builder
from ubuntutools.lp.lpapicache import (Launchpad, Distribution,
SeriesNotFoundException,
PackageNotFoundException)
2011-06-24 17:50:10 +02:00
from ubuntutools.misc import (system_distribution, vendor_to_distroinfo,
codename_to_distribution)
from ubuntutools.question import YesNoQuestion
from ubuntutools import subprocess
def error(msg):
Logger.error(msg)
sys.exit(1)
def check_call(cmd, *args, **kwargs):
Logger.command(cmd)
ret = subprocess.call(cmd, *args, **kwargs)
if ret != 0:
error('%s returned %d.' % (cmd[0], ret))
def parse(args):
usage = 'Usage: %prog [options] <source package name or .dsc URL/file>'
2010-12-27 20:32:07 +01:00
parser = optparse.OptionParser(usage)
parser.add_option('-d', '--destination',
metavar='DEST',
2010-12-27 20:32:07 +01:00
dest='dest_releases',
default=[],
action='append',
help='Backport to DEST release '
'(default: current release)')
2010-12-27 20:32:07 +01:00
parser.add_option('-s', '--source',
metavar='SOURCE',
2010-12-27 20:32:07 +01:00
dest='source_release',
help='Backport from SOURCE release '
'(default: devel release)')
2010-12-27 20:32:07 +01:00
parser.add_option('-S', '--suffix',
metavar='SUFFIX',
2010-12-27 20:32:07 +01:00
help='Suffix to append to version number '
'(default: ~ppa1 when uploading to a PPA)')
2010-12-27 20:32:07 +01:00
parser.add_option('-b', '--build',
default=False,
action='store_true',
help='Build the package before uploading '
'(default: %default)')
parser.add_option('-B', '--builder',
metavar='BUILDER',
help='Specify the package builder (default: pbuilder)')
2010-12-27 20:32:07 +01:00
parser.add_option('-U', '--update',
default=False,
action='store_true',
help='Update the build environment before '
'attempting to build')
parser.add_option('-u', '--upload',
metavar='UPLOAD',
help='Specify an upload destination')
2010-12-27 20:32:07 +01:00
parser.add_option('-y', '--yes',
dest='prompt',
default=True,
action='store_false',
help='Do not prompt before uploading to a PPA')
parser.add_option('-v', '--version',
metavar='VERSION',
help='Package version to backport (or verify)')
2010-12-27 20:32:07 +01:00
parser.add_option('-w', '--workdir',
metavar='WORKDIR',
2010-12-27 20:32:07 +01:00
help='Specify a working directory '
'(default: temporary dir)')
parser.add_option('-r', '--release-pocket',
default=False,
action='store_true',
help='Target the release pocket in the .changes file. '
'Necessary (and default) for uploads to PPAs')
2010-12-27 20:32:07 +01:00
parser.add_option('-m', '--mirror',
metavar='INSTANCE',
help='Preferred mirror (default: Launchpad)')
2010-12-27 20:32:07 +01:00
parser.add_option('-l', '--lpinstance',
metavar='INSTANCE',
2010-12-27 20:32:07 +01:00
help='Launchpad instance to connect to '
'(default: production)')
2010-12-27 20:32:07 +01:00
parser.add_option('--no-conf',
default=False,
action='store_true',
help="Don't read config files or environment variables")
2010-12-27 20:32:07 +01:00
opts, args = parser.parse_args(args)
if len(args) != 1:
2010-12-27 20:32:07 +01:00
parser.error('You must specify a single source package or a .dsc '
'URL/path.')
config = UDTConfig(opts.no_conf)
if opts.builder is None:
opts.builder = config.get_value('BUILDER')
if not opts.update:
2010-12-21 02:02:38 +02:00
opts.update = config.get_value('UPDATE_BUILDER', boolean=True)
if opts.workdir is None:
opts.workdir = config.get_value('WORKDIR')
2010-12-21 15:14:45 +02:00
if opts.lpinstance is None:
opts.lpinstance = config.get_value('LPINSTANCE')
if opts.upload is None:
opts.upload = config.get_value('UPLOAD')
if not opts.upload and not opts.workdir:
2010-12-27 20:32:07 +01:00
parser.error('Please specify either a working dir or an upload target!')
if opts.upload and opts.upload.startswith('ppa:'):
opts.release_pocket = True
return opts, args, config
def find_release_package(mirror, workdir, package, version, source_release,
config):
srcpkg = None
if source_release:
distribution = codename_to_distribution(source_release)
if not distribution:
error('Unknown release codename %s' % source_release)
info = vendor_to_distroinfo(distribution)()
source_release = info.codename(source_release, default=source_release)
else:
distribution = system_distribution()
mirrors = [mirror] if mirror else []
mirrors.append(config.get_value('%s_MIRROR' % distribution.upper()))
if not version:
archive = Distribution(distribution.lower()).getArchive()
try:
spph = archive.getSourcePackage(package, source_release)
except (SeriesNotFoundException, PackageNotFoundException), e:
error(str(e))
version = spph.getVersion()
if distribution == 'Debian':
srcpkg = DebianSourcePackage(package,
version,
workdir=workdir,
mirrors=mirrors)
elif distribution == 'Ubuntu':
srcpkg = UbuntuSourcePackage(package,
version,
workdir=workdir,
mirrors=mirrors)
return srcpkg
def find_package(mirror, workdir, package, version, source_release, config):
2010-12-30 18:05:55 +02:00
"Returns the SourcePackage"
if package.endswith('.dsc'):
return SourcePackage(version=version, dscfile=package,
workdir=workdir, mirrors=(mirror,))
if not source_release and not version:
info = vendor_to_distroinfo(system_distribution())
source_release = info().devel()
srcpkg = find_release_package(mirror, workdir, package, version,
source_release, config)
if version and srcpkg.version != version:
2011-06-19 14:50:52 -07:00
error('Requested backport of version %s but version of %s in %s is %s'
% (version, package, source_release, srcpkg.version))
return srcpkg
2010-12-18 12:16:02 -08:00
def get_backport_version(version, suffix, upload, release):
2010-12-27 15:20:49 +01:00
backport_version = version + ('~%s1' % release)
2010-12-18 12:33:10 -08:00
if suffix is not None:
2010-12-27 15:20:49 +01:00
backport_version += suffix
2010-12-18 12:16:02 -08:00
elif upload and upload.startswith('ppa:'):
2010-12-27 15:20:49 +01:00
backport_version += '~ppa1'
return backport_version
def get_backport_dist(release, release_pocket):
if release_pocket:
return release
else:
return '%s-backports' % release
2010-12-30 18:05:55 +02:00
def do_build(workdir, dsc, release, builder, update):
builder = get_builder(builder)
if not builder:
return
if update:
if 0 != builder.update(release):
sys.exit(1)
# builder.build is going to chdir to buildresult:
workdir = os.path.realpath(workdir)
2010-12-30 18:05:55 +02:00
return builder.build(os.path.join(workdir, dsc),
2010-12-15 11:02:07 -08:00
release,
os.path.join(workdir, "buildresult"))
2010-12-30 18:05:55 +02:00
def do_upload(workdir, package, bp_version, changes, upload, prompt):
print 'Please check %s %s in file://%s carefully!' % \
(package, bp_version, workdir)
if prompt or upload == 'ubuntu':
question = 'Do you want to upload the package to %s' % upload
answer = YesNoQuestion().ask(question, "yes")
if answer == "no":
return
2010-12-30 18:05:55 +02:00
check_call(['dput', upload, changes], cwd=workdir)
def orig_needed(upload, workdir, pkg):
'''Avoid a -sa if possible'''
if not upload or not upload.startswith('ppa:'):
return True
ppa = upload.split(':', 1)[1]
user, ppa = ppa.split('/', 1)
version = pkg.version.full_version
if pkg.version.epoch:
version = version.split(pkg.version.epoch, 1)[1]
h = Http()
for filename in glob.glob(os.path.join(workdir,
'%s_%s.orig*' % (pkg.source, version))):
url = ('https://launchpad.net/~%s/+archive/%s/+files/%s'
% (user, ppa, filename))
try:
headers, body = h.request(url, 'HEAD')
if headers.status != 200:
return True
except HttpLib2Error, e:
Logger.info(e)
return True
return False
def do_backport(workdir, pkg, suffix, release, release_pocket, build, builder,
update, upload, prompt):
2010-12-30 18:05:55 +02:00
dirname = '%s-%s' % (pkg.source, release)
srcdir = os.path.join(workdir, dirname)
if os.path.exists(srcdir):
question = 'Working directory %s already exists. Delete it?' % srcdir
if YesNoQuestion().ask(question, 'no') == 'no':
sys.exit(1)
shutil.rmtree(srcdir)
pkg.unpack(dirname)
2010-12-30 18:05:55 +02:00
bp_version = get_backport_version(pkg.version.full_version, suffix,
upload, release)
bp_dist = get_backport_dist(release, release_pocket)
check_call(['dch',
'--force-bad-version',
2010-12-30 18:05:55 +02:00
'--force-distribution',
'--preserve',
'--newversion', bp_version,
'--distribution', bp_dist,
'No-change backport to %s' % release],
cwd=srcdir)
cmd = ['debuild', '--no-lintian', '-S', '-nc']
if orig_needed(upload, workdir, pkg):
cmd.append('-sa')
else:
cmd.append('-sd')
check_call(cmd, cwd=srcdir)
2010-12-30 18:05:55 +02:00
fn_base = pkg.source + '_' + bp_version.split(':', 1)[-1]
if build:
2010-12-30 18:05:55 +02:00
if 0 != do_build(workdir, fn_base + '.dsc', release, builder, update):
sys.exit(1)
if upload:
do_upload(workdir, pkg.source, bp_version, fn_base + '_source.changes',
2010-12-30 18:05:55 +02:00
upload, prompt)
shutil.rmtree(srcdir)
def main(args):
ubu_email()
opts, (package_or_dsc,), config = parse(args[1:])
Launchpad.login_anonymously(service=opts.lpinstance)
if not opts.dest_releases:
2010-12-27 15:20:49 +01:00
distinfo = lsb_release.get_distro_information()
try:
opts.dest_releases = [distinfo['CODENAME']]
2010-12-27 15:20:49 +01:00
except KeyError:
error('No destination release specified and unable to guess yours.')
if opts.workdir:
workdir = os.path.expanduser(opts.workdir)
else:
workdir = tempfile.mkdtemp(prefix='backportpackage-')
if not os.path.exists(workdir):
os.makedirs(workdir)
try:
pkg = find_package(opts.mirror,
2010-12-30 18:05:55 +02:00
workdir,
package_or_dsc,
opts.version,
opts.source_release,
config)
2010-12-30 18:05:55 +02:00
pkg.pull()
for release in opts.dest_releases:
do_backport(workdir,
2010-12-30 18:05:55 +02:00
pkg,
2010-12-18 12:21:40 -08:00
opts.suffix,
release,
opts.release_pocket,
opts.build,
opts.builder,
opts.update,
opts.upload,
opts.prompt)
2011-01-01 19:51:03 +02:00
except DownloadError, e:
error(str(e))
finally:
if not opts.workdir:
shutil.rmtree(workdir)
if __name__ == '__main__':
sys.exit(main(sys.argv))