Merge trunk.

This commit is contained in:
Evan Broder 2010-12-17 01:32:30 -08:00
commit 8627e0a626
2 changed files with 155 additions and 77 deletions

View File

@ -21,16 +21,15 @@ import shutil
import subprocess
import sys
import tempfile
import urllib
from debian.deb822 import Dsc
import launchpadlib.launchpad
import lsb_release
from ubuntutools.builder import getBuilder
from ubuntutools.logger import Logger
devnull = open('/dev/null', 'r+')
lp = None
def error(msg):
Logger.error(msg)
sys.exit(1)
@ -42,15 +41,15 @@ def check_call(cmd, *args, **kwargs):
error('%s returned %d' % (cmd, ret))
def parse(args):
usage = 'Usage: %prog [options] <source package>'
usage = 'Usage: %prog [options] <source package name or .dsc URL/file>'
p = optparse.OptionParser(usage)
p.add_option('-t', '--to',
p.add_option('-d', '--destination',
dest='dest_releases',
default=[],
action='append',
help='Backport to DEST release (required)',
help='Backport to DEST release (default: current release)',
metavar='DEST')
p.add_option('-f', '--from',
p.add_option('-s', '--source',
dest='source_release',
default=None,
help='Backport from SOURCE release (default: devel release)',
@ -79,6 +78,11 @@ def parse(args):
default=None,
help='Package version to backport (or verify)',
metavar='VERSION')
p.add_option('-w', '--workdir',
dest='workdir',
default=None,
help='Specify a working directory (default: temporary dir)',
metavar='WORKDIR')
p.add_option('-l', '--launchpad',
dest='launchpad',
default='production',
@ -87,16 +91,16 @@ def parse(args):
opts, args = p.parse_args(args)
if len(args) != 1:
p.error('You must specify a source package')
if not opts.dest_releases:
p.error('You must specify at least one destination release')
p.error('You must specify a single source package or a .dsc URL/path')
if not opts.upload and not opts.build:
p.error('Nothing to do')
return opts, args
def find_release_package(package, opts):
def find_release_package(lp, package, version, source_release):
ubuntu = lp.distributions['ubuntu']
archive = ubuntu.main_archive
series = ubuntu.getSeries(name_or_version=opts.source_release)
series = ubuntu.getSeries(name_or_version=source_release)
status = 'Published'
for pocket in ('Updates', 'Security', 'Release'):
try:
@ -110,50 +114,67 @@ def find_release_package(package, opts):
continue
else:
error('Unable to find package %s in release %s' %
(package, opts.source_release))
(package, source_release))
if opts.version and opts.version != srcpkg.source_package_version:
if version and version != srcpkg.source_package_version:
error('Requested backport of version %s but %s is at version %s' %
(opts.version, package, srcpkg.source_package_version))
(version, package, srcpkg.source_package_version))
return srcpkg
def find_version_package(package, opts):
def find_version_package(lp, package, version):
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=package,
version=opts.version)[0]
version=version)[0]
except IndexError:
error('Package %s was never published with version %s in Ubuntu' %
(package, opts.version))
error('Version %s of package %s was never published in Ubuntu' %
(version, package))
def fetch_package(workdir, package, opts):
# Returns the path to the .dsc file that was fetched
if not opts.source_release and not opts.version:
opts.source_release = lp.distributions['ubuntu'].current_series.name
def dscurl_from_package(lp, workdir, package, version, source_release):
if not source_release and not version:
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(package, opts)
if source_release:
srcpkg = find_release_package(lp, package, version, source_release)
else:
srcpkg = find_version_package(package, opts)
srcpkg = find_version_package(lp, package, version)
for f in srcpkg.sourceFileUrls():
if f.endswith('.dsc'):
check_call(['dget',
'--download-only',
'--allow-unauthenticated',
f],
cwd=workdir)
return os.path.join(workdir, os.path.basename(f))
return urllib.unquote(f)
else:
error('Package %s contains no .dsc file' % package)
def dscurl_from_dsc(package):
path = os.path.abspath(os.path.expanduser(package))
if os.path.exists(path):
return 'file://%s' % path
else:
# Can't resolve it as a local path? Let's just hope it's good
# as-is
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)
check_call(['dget',
'--download-only',
'--allow-unauthenticated',
dsc],
cwd=workdir)
return os.path.join(workdir, os.path.basename(dsc))
def get_backport_version(version, upload, release):
v = version + ('~%s1' % release)
if upload and upload.startswith('ppa:'):
@ -166,21 +187,21 @@ def get_backport_dist(upload, release):
else:
return release
def do_build(workdir, package, release, bp_version, opts):
builder = getBuilder(opts.builder)
def do_build(workdir, package, release, bp_version, builder):
builder = getBuilder(builder)
if not builder:
return
if opts.update:
builder.update(release)
builder.build(os.path.join(workdir,
'%s_%s.dsc' % (package, bp_version)),
release,
workdir)
return builder.build(os.path.join(workdir,
'%s_%s.dsc' % (package, bp_version)),
release,
workdir)
def do_upload(workdir, package, bp_version, opts):
prompt = 'Do you want to upload this to %s? [Y/n]' % opts.upload
def do_upload(workdir, package, bp_version, upload):
prompt = 'Do you want to upload this to %s? [Y/n]' % upload
while True:
answer = raw_input(prompt).strip().lower()
if answer in ('', 'y', 'yes'):
@ -189,16 +210,13 @@ def do_upload(workdir, package, bp_version, opts):
return
check_call(['dput',
opts.upload,
upload,
'%s_%s_source.changes' %
(package, bp_version)],
cwd=workdir)
def do_backport(workdir, package, dscfile, release, opts):
dsc = Dsc(open(os.path.join(workdir, dscfile)))
v = dsc['Version']
def do_backport(workdir, package, dscfile, version, release, build, builder, upload):
check_call(['dpkg-source',
'-x',
dscfile,
@ -206,8 +224,8 @@ def do_backport(workdir, package, dscfile, release, opts):
cwd=workdir)
srcdir = os.path.join(workdir, package)
bp_version = get_backport_version(v, opts.upload, release)
bp_dist = get_backport_dist(opts.upload, release)
bp_version = get_backport_version(version, upload, release)
bp_dist = get_backport_dist(upload, release)
check_call(['dch',
'--force-bad-version',
@ -223,32 +241,61 @@ def do_backport(workdir, package, dscfile, release, opts):
bp_version = bp_version[bp_version.find(':')+1:]
print 'Please check the package in file://%s carefully' % workdir
if opts.build:
do_build(workdir, package, release, bp_version, opts)
if opts.upload:
do_upload(workdir, package, bp_version, opts)
if build:
if 0 != do_build(workdir, package, release, bp_version, builder):
error('Package failed to build; aborting')
if upload:
do_upload(workdir, package, bp_version, upload)
shutil.rmtree(srcdir)
def main(args):
global lp
os.environ['DEB_VENDOR'] = 'Ubuntu'
opts, (package,) = parse(args[1:])
opts, (package_or_dsc,) = parse(args[1:])
script_name = os.path.basename(sys.argv[0])
lp = launchpadlib.launchpad.Launchpad.login_anonymously(script_name,
opts.launchpad)
workdir = tempfile.mkdtemp(prefix='backportpackage-')
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')
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:
dscfile = fetch_package(workdir, package, opts)
dscfile = fetch_package(lp,
workdir,
package_or_dsc,
opts.version,
opts.source_release)
dsc = Dsc(open(os.path.join(workdir, dscfile)))
package = dsc['Source']
version = dsc['Version']
for release in opts.dest_releases:
do_backport(workdir, package, dscfile, release, opts)
do_backport(workdir,
package,
dscfile,
version,
release,
opts.build,
opts.builder,
opts.upload)
finally:
shutil.rmtree(workdir)
if not opts.workdir:
shutil.rmtree(workdir)
if __name__ == '__main__':
sys.exit(main(sys.argv))

View File

@ -4,21 +4,19 @@ backportpackage \- helper to test package backports
.SH SYNOPSIS
.TP
.B backportpackage \fR[\fIadditional options\fR]
\-\-to <\fIdest release\fR>
.br
\-\-upload <\fIupload target\fR>
.br
<\fIsource package\fR>
<\fIsource package name or .dsc URL/file\fR>
.PP
.B backportpackage \-h
.SH OPTIONS
.TP
.B \-t \fIDEST\fR, \-\-to=\fIDEST\fR
.B \-d \fIDEST\fR, \-\-destination=\fIDEST\fR
\fBRequired\fR. Backport the package to the specified Ubuntu
release. This option may be specified multiple times, but must be
specified at least once.
release. If this option is unspecified, then \fBbackportpackage\fR
defaults to the release on which it is currently running.
.TP
.B \-f \fISOURCE\fR, \-\-from=\fISOURCE\fR
.B \-s \fISOURCE\fR, \-\-source=\fISOURCE\fR
Backport the package from the specified Ubuntu release. If neither
this option nor \fB\-\-version\fR are specified, then
\fBbackportpackage\fR defaults to the current Ubuntu development
@ -42,24 +40,32 @@ Update the builder environment before attempting to build.
Upload to \fIUPLOAD\fR with \fBdput\fR(1) (after confirmation).
.TP
.B \-v \fIVERSION\fR, \-\-version=\fIVERSION\fR
If the \fB\-\-from\fR option is specified, then \fBbackportpackage\fR
verifies that the current version of \fIsource package\fR in
\fISOURCE\fR is the same as \fIVERSION\fR. Otherwise,
If the \fB\-\-source\fR option is specified, then
\fBbackportpackage\fR verifies that the current version of \fIsource
package\fR in \fISOURCE\fR is the same as \fIVERSION\fR. Otherwise,
\fBbackportpackage\fR finds version \fIVERSION\fR of \fIsource
package\fR, regardless of the release in which it was published (or if
that version is still current).
that version is still current). This option is ignored if a .dsc URL
or path is passed in instead of a source package name.
.TP
.B \-w \fIWORKDIR\fR, \-\-workdir=\fIWORKDIR\fR
If \fIWORKDIR\fR is specified, then all files are downloaded,
unpacked, built into, and otherwise manipulated in
\fIWORKDIR\fR. Otherwise, a temporary directory is created, which is
deleted before \fIbackportpackage\fR exits.
.TP
.B \-l \fIINSTANCE\fR, \-\-launchpad=\fIINSTANCE\fR
Use the specified instance of Launchpad (e.g. "staging"), instead of
the default of "production".
.SH DESCRIPTION
\fBbackportpackage\fR fetches a package from one Ubuntu release and
creates a no-change backport of that package to a previous release,
optionally doing a test build of the package and/or uploading the
resulting backport for testing.
\fBbackportpackage\fR fetches a package from one Ubuntu release or
from a specified .dsc path or URL and creates a no-change backport of
that package to a previous release, optionally doing a test build of
the package and/or uploading the resulting backport for testing.
.PP
The backported package is fetched and built in a temporary directory
in \fB/tmp\fR, which is removed once the script finishes running.
Unless a working directory is specified, the backported package is
fetched and built in a temporary directory in \fB/tmp\fR, which is
removed once the script finishes running.
.PP
\fBbackportpackage\fR is only recommended for testing backports in a
PPA, not uploading backports to the Ubuntu archive.
@ -67,9 +73,34 @@ PPA, not uploading backports to the Ubuntu archive.
.TP
.B UBUNTUTOOLS_BUILDER
The default builder for Ubuntu development tools that support it
(including \fBbackportpackage\fR. Supported are \fBpbuilder\fR(8) and
(including \fBbackportpackage\fR). Supported are \fBpbuilder\fR(8) and
\fBsbuild\fR(1). If unset and not provided on the command line,
\fBpbuilder\fR(8) is used.
.SH EXAMPLES
Test-build in your PPA a backport of znc from the current development
release to your workstation's release, deleting the build products
afterwards:
.IP
.nf
.B backportpackage -u ppa:\fIuser\fR/\fIppa\fB znc
.fi
.PP
Backport squashfs-tools from Maverick to both Karmic and Lucid and
test-build both locally, leaving all build products in the current
working directory:
.IP
.nf
.B backportpackage -b -s maverick -d karmic -d lucid -w . \\\\
.B " "squashfs-tools
.fi
.PP
Fetch a package from a PPA, backport it to Hardy, then upload it back
to the same PPA:
.IP
.nf
.B backportpackage -d hardy -u ppa:\fIuser\fR/\fIppa\fR \\\\
.B " "https://launchpad.net/\fIsome/file.dsc\fR
.fi
.SH AUTHOR
\fBbackportpackage\fR and this manpage were written by Evan Broder
<evan@ebroder.net>