From cb41838b48298366a27699793be22ad9e72a3149 Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Sat, 11 Dec 2010 14:12:02 -0800 Subject: [PATCH 01/32] backportpackage: new script for testing backport requests in a PPA. --- backportpackage | 108 ++++++++++++++++++++++++++++++++++++++++++ debian/changelog | 6 ++- doc/backportpackage.1 | 18 +++++++ 3 files changed, 131 insertions(+), 1 deletion(-) create mode 100755 backportpackage create mode 100644 doc/backportpackage.1 diff --git a/backportpackage b/backportpackage new file mode 100755 index 0000000..e8f4b52 --- /dev/null +++ b/backportpackage @@ -0,0 +1,108 @@ +#!/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 + +from debian.changelog import Changelog + +devnull = open('/dev/null', 'r+') + +def error(msg, *args, **kwargs): + logging.error(msg, *args, **kwargs) + sys.exit(1) + +def parse(args): + usage = 'Usage: %prog ' + p = optparse.OptionParser(usage) + + opts, args = p.parse_args(args) + if len(args) != 4: + p.error('Invalid arguments') + + return opts, args + +def main(args): + logging.basicConfig(level=logging.INFO) + + _, (package, source_release, dest_release, upload) = parse(args[1:]) + + tmpdir = tempfile.mkdtemp() + try: + for pocket in ('-updates', '-security', ''): + try: + subprocess.check_call(['pull-lp-source', package, source_release + pocket], + cwd=tmpdir, + stdout=devnull, stderr=devnull) + logging.info('Found package %s in pocket %s' % (package, source_release + pocket)) + break + except subprocess.CalledProcessError: + continue + else: + error('Unable to find package %s in release %s' % (package, source_release)) + + for srcdir in os.listdir(tmpdir): + srcdir = os.path.join(tmpdir, srcdir) + if os.path.isdir(srcdir): + break + else: + error('Something went wrong unpacking package %s' % package) + + cl = Changelog(open(os.path.join(srcdir, 'debian/changelog'))) + v = cl.get_version() + + bp_version = str(v) + ('~%s1' % dest_release) + bp_dist = dest_release + if upload.startswith('ppa:'): + bp_version += '~ppa1' + elif upload == 'ubuntu': + bp_dist += '-backports' + subprocess.check_call(['dch', + '--force-bad-version', + '--preserve', + '--newversion', bp_version, + '--distribution', dest_release, + 'No-change backport to %s' % dest_release], + cwd=srcdir) + subprocess.check_call(['debuild', '-S'], + cwd=srcdir) + + if ':' in bp_version: + bp_version = bp_version[bp_version.find(':')+1:] + + print 'Please check the package in %s carefully' % tmpdir + while True: + answer = raw_input('Do you still want to upload this to %s? [Y/n] ' % upload).strip().lower() + if answer in ('', 'y', 'yes'): + break + elif answer in ('n', 'no'): + return 2 + + subprocess.check_call(['dput', + upload, + '%s_%s_source.changes' % (package, bp_version)], + cwd=tmpdir) + finally: + shutil.rmtree(tmpdir) + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/debian/changelog b/debian/changelog index ea397be..3cc0184 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,10 +1,14 @@ ubuntu-dev-tools (0.108) UNRELEASED; urgency=low + [ Stefano Rivera ] * lp-shell, import-bug-from-debian: Use the 'production' LP instance instead of 'edge' (which is going away). * pbuilder-dist: Fix typo in local archive support, introduced in 0.107. - -- Stefano Rivera Sat, 11 Dec 2010 12:33:37 +0200 + [ Evan Broder ] + * backportpackage: new script for testing backport requests in a PPA. + + -- Evan Broder Sat, 11 Dec 2010 14:11:54 -0800 ubuntu-dev-tools (0.107) experimental; urgency=low diff --git a/doc/backportpackage.1 b/doc/backportpackage.1 new file mode 100644 index 0000000..b74ce24 --- /dev/null +++ b/doc/backportpackage.1 @@ -0,0 +1,18 @@ +.TH BACKPORTPACKAGE "1" "December 2010" "ubuntu-dev-tools" +.SH NAME +backportpackage \- helper to test package backports +.SH SYNOPSIS +.B backportpackage +\fI \fR +.SH DESCRIPTION +\fIbackportpackage\fR fetches a package from one Ubuntu release and +creates a no-change backport of that package to a previous release, +uploading the resulting backport for testing. +.PP +\fIbackportpackage\fR is only recommended for testing backports in a +PPA, not uploading backports to the Ubuntu archive. +.SH AUTHOR +\fIbackportpackage\fR and this manpage were written by Evan Broder + +.PP +Both are released under GNU General Public License, version 2. From 07029e3dfb624c1b0feb3957ba177275660573bd Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Sat, 11 Dec 2010 14:43:31 -0800 Subject: [PATCH 02/32] Set DEB_VENDOR in backportpackage, since it will only be used for Ubuntu development --- backportpackage | 1 + 1 file changed, 1 insertion(+) diff --git a/backportpackage b/backportpackage index e8f4b52..9150579 100755 --- a/backportpackage +++ b/backportpackage @@ -43,6 +43,7 @@ def parse(args): def main(args): logging.basicConfig(level=logging.INFO) + os.environ['DEB_VENDOR'] = 'Ubuntu' _, (package, source_release, dest_release, upload) = parse(args[1:]) From 006e8482fc39c8ee7b73813683f07ed02de1198a Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Sat, 11 Dec 2010 15:24:05 -0800 Subject: [PATCH 03/32] Actually install the backportpackage script. --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index bb91ffe..4bc1e40 100755 --- a/setup.py +++ b/setup.py @@ -16,6 +16,7 @@ if os.path.exists(changelog): setup(name='ubuntu-dev-tools', version=version, scripts=['404main', + 'backportpackage', 'check-symbols', 'dch-repeat', 'dgetlp', From 2befb9ec5a1817425b83bac555dff243a753d185 Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Sat, 11 Dec 2010 15:49:18 -0800 Subject: [PATCH 04/32] backportpackage: Use "backportpackage-" as the prefix for temporary directory, to make it easy to find --- backportpackage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backportpackage b/backportpackage index 9150579..451f65a 100755 --- a/backportpackage +++ b/backportpackage @@ -47,7 +47,7 @@ def main(args): _, (package, source_release, dest_release, upload) = parse(args[1:]) - tmpdir = tempfile.mkdtemp() + tmpdir = tempfile.mkdtemp(prefix='backportpackage-') try: for pocket in ('-updates', '-security', ''): try: From 158c4f369392c61c889f4387ce2c7c7455d9173e Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Sat, 11 Dec 2010 15:49:49 -0800 Subject: [PATCH 05/32] backportpackage.1: Document that backportpackage uses a temporary directory, as this is different from sponsor-patch and syncpackage --- doc/backportpackage.1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/backportpackage.1 b/doc/backportpackage.1 index b74ce24..346fa06 100644 --- a/doc/backportpackage.1 +++ b/doc/backportpackage.1 @@ -9,6 +9,9 @@ backportpackage \- helper to test package backports creates a no-change backport of that package to a previous release, 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. +.PP \fIbackportpackage\fR is only recommended for testing backports in a PPA, not uploading backports to the Ubuntu archive. .SH AUTHOR From 0f01b0752c4ff616671e2b91db7fcac982a29e04 Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Sat, 11 Dec 2010 16:54:19 -0800 Subject: [PATCH 06/32] backportpackage: Print real error messages instead of relying on subprocess.check_call backtraces --- backportpackage | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/backportpackage b/backportpackage index 451f65a..f669ece 100755 --- a/backportpackage +++ b/backportpackage @@ -77,15 +77,17 @@ def main(args): bp_version += '~ppa1' elif upload == 'ubuntu': bp_dist += '-backports' - subprocess.check_call(['dch', - '--force-bad-version', - '--preserve', - '--newversion', bp_version, - '--distribution', dest_release, - 'No-change backport to %s' % dest_release], - cwd=srcdir) - subprocess.check_call(['debuild', '-S'], - cwd=srcdir) + if not subprocess.call(['dch', + '--force-bad-version', + '--preserve', + '--newversion', bp_version, + '--distribution', dest_release, + 'No-change backport to %s' % dest_release], + cwd=srcdir): + error('Something went wrong updating the package changelog') + if not subprocess.check_call(['debuild', '-S'], + cwd=srcdir): + error('Something went wrong while building the source package') if ':' in bp_version: bp_version = bp_version[bp_version.find(':')+1:] @@ -98,10 +100,11 @@ def main(args): elif answer in ('n', 'no'): return 2 - subprocess.check_call(['dput', - upload, - '%s_%s_source.changes' % (package, bp_version)], - cwd=tmpdir) + if not subprocess.check_call(['dput', + upload, + '%s_%s_source.changes' % (package, bp_version)], + cwd=tmpdir): + error('Something went wrong uploading the package %s to %s' % package, upload) finally: shutil.rmtree(tmpdir) From fccec5fab9d5ada5b3c1244f6cd23b938f3b1867 Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Sat, 11 Dec 2010 16:54:37 -0800 Subject: [PATCH 07/32] backportpackage: Pass -sa to debuild to make sure the orig source tarball is included --- backportpackage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backportpackage b/backportpackage index f669ece..fffadf3 100755 --- a/backportpackage +++ b/backportpackage @@ -85,7 +85,7 @@ def main(args): 'No-change backport to %s' % dest_release], cwd=srcdir): error('Something went wrong updating the package changelog') - if not subprocess.check_call(['debuild', '-S'], + if not subprocess.check_call(['debuild', '-S', '-sa'], cwd=srcdir): error('Something went wrong while building the source package') From 4669ee62afe7618e335cd629c601a98db81221d1 Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Sat, 11 Dec 2010 16:57:40 -0800 Subject: [PATCH 08/32] backportpackage: Use a file:/// URL when giving paths to make them clickable in gnome-terminal --- backportpackage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backportpackage b/backportpackage index fffadf3..61aeb18 100755 --- a/backportpackage +++ b/backportpackage @@ -92,7 +92,7 @@ def main(args): if ':' in bp_version: bp_version = bp_version[bp_version.find(':')+1:] - print 'Please check the package in %s carefully' % tmpdir + print 'Please check the package in file://%s carefully' % tmpdir while True: answer = raw_input('Do you still want to upload this to %s? [Y/n] ' % upload).strip().lower() if answer in ('', 'y', 'yes'): From 8ec07732012b5ba9df00591b2ae2cb2bdc1256e2 Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Sun, 12 Dec 2010 12:15:38 -0800 Subject: [PATCH 09/32] backportpackage: Correct uses of subprocess.call --- backportpackage | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/backportpackage b/backportpackage index 61aeb18..c3965a6 100755 --- a/backportpackage +++ b/backportpackage @@ -50,14 +50,11 @@ def main(args): tmpdir = tempfile.mkdtemp(prefix='backportpackage-') try: for pocket in ('-updates', '-security', ''): - try: - subprocess.check_call(['pull-lp-source', package, source_release + pocket], - cwd=tmpdir, - stdout=devnull, stderr=devnull) + if 0 == subprocess.call(['pull-lp-source', package, source_release + pocket], + cwd=tmpdir, + stdout=devnull, stderr=devnull): logging.info('Found package %s in pocket %s' % (package, source_release + pocket)) break - except subprocess.CalledProcessError: - continue else: error('Unable to find package %s in release %s' % (package, source_release)) @@ -77,16 +74,16 @@ def main(args): bp_version += '~ppa1' elif upload == 'ubuntu': bp_dist += '-backports' - if not subprocess.call(['dch', - '--force-bad-version', - '--preserve', - '--newversion', bp_version, - '--distribution', dest_release, - 'No-change backport to %s' % dest_release], - cwd=srcdir): + if 0 != subprocess.call(['dch', + '--force-bad-version', + '--preserve', + '--newversion', bp_version, + '--distribution', dest_release, + 'No-change backport to %s' % dest_release], + cwd=srcdir): error('Something went wrong updating the package changelog') - if not subprocess.check_call(['debuild', '-S', '-sa'], - cwd=srcdir): + if 0 != subprocess.call(['debuild', '-S', '-sa'], + cwd=srcdir): error('Something went wrong while building the source package') if ':' in bp_version: @@ -100,10 +97,10 @@ def main(args): elif answer in ('n', 'no'): return 2 - if not subprocess.check_call(['dput', - upload, - '%s_%s_source.changes' % (package, bp_version)], - cwd=tmpdir): + if 0 != subprocess.call(['dput', + upload, + '%s_%s_source.changes' % (package, bp_version)], + cwd=tmpdir): error('Something went wrong uploading the package %s to %s' % package, upload) finally: shutil.rmtree(tmpdir) From dc76d195bfde4f03ff085ea8f0804ed59b6af2bb Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Sun, 12 Dec 2010 16:36:51 -0800 Subject: [PATCH 10/32] backportpackage: Support backporting to multiple destination releases by using (required) options instead of arguments --- backportpackage | 126 +++++++++++++++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 43 deletions(-) diff --git a/backportpackage b/backportpackage index c3965a6..89865b5 100755 --- a/backportpackage +++ b/backportpackage @@ -23,7 +23,7 @@ import subprocess import sys import tempfile -from debian.changelog import Changelog +from debian.deb822 import Dsc devnull = open('/dev/null', 'r+') @@ -32,12 +32,39 @@ def error(msg, *args, **kwargs): sys.exit(1) def parse(args): - usage = 'Usage: %prog ' + usage = 'Usage: %prog [options]' p = optparse.OptionParser(usage) + 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, + help='Backport from SOURCE release (required)', + metavar='SOURCE') + 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') opts, args = p.parse_args(args) - if len(args) != 4: + if len(args): p.error('Invalid arguments') + 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.source_release: + p.error('You must specify the source release') + if not opts.upload: + p.error('You must specify an upload destination') return opts, args @@ -45,7 +72,11 @@ def main(args): logging.basicConfig(level=logging.INFO) os.environ['DEB_VENDOR'] = 'Ubuntu' - _, (package, source_release, dest_release, upload) = parse(args[1:]) + opts, _ = parse(args[1:]) + package = opts.package + source_release = opts.source_release + dest_releases = opts.dest_releases + upload = opts.upload tmpdir = tempfile.mkdtemp(prefix='backportpackage-') try: @@ -56,52 +87,61 @@ def main(args): logging.info('Found package %s in pocket %s' % (package, source_release + pocket)) break else: - error('Unable to find package %s in release %s' % (package, source_release)) + error('Unable to find source package %s in release %s' % (package, source_release)) - for srcdir in os.listdir(tmpdir): - srcdir = os.path.join(tmpdir, srcdir) - if os.path.isdir(srcdir): + for dscfile in os.listdir(tmpdir): + if dscfile.endswith('.dsc'): break else: - error('Something went wrong unpacking package %s' % package) + error('Unable to find a .dsc file for package %s' % package) - cl = Changelog(open(os.path.join(srcdir, 'debian/changelog'))) - v = cl.get_version() + dsc = Dsc(open(os.path.join(tmpdir, dscfile))) + v = dsc['Version'] - bp_version = str(v) + ('~%s1' % dest_release) - bp_dist = dest_release - if upload.startswith('ppa:'): - bp_version += '~ppa1' - elif upload == 'ubuntu': - bp_dist += '-backports' - if 0 != subprocess.call(['dch', - '--force-bad-version', - '--preserve', - '--newversion', bp_version, - '--distribution', dest_release, - 'No-change backport to %s' % dest_release], - cwd=srcdir): - error('Something went wrong updating the package changelog') - if 0 != subprocess.call(['debuild', '-S', '-sa'], - cwd=srcdir): - error('Something went wrong while building the source package') + for dest_release in dest_releases: + srcdir = os.path.join(tmpdir, package) + if 0 != subprocess.call(['dpkg-source', + '-x', + dscfile, + package], + cwd=tmpdir): + error('Something went wrong unpacking package %s' % package) - if ':' in bp_version: - bp_version = bp_version[bp_version.find(':')+1:] + bp_version = v + ('~%s1' % dest_release) + bp_dist = dest_release + if upload.startswith('ppa:'): + bp_version += '~ppa1' + elif upload == 'ubuntu': + bp_dist += '-backports' + if 0 != subprocess.call(['dch', + '--force-bad-version', + '--preserve', + '--newversion', bp_version, + '--distribution', dest_release, + 'No-change backport to %s' % dest_release], + cwd=srcdir): + error('Something went wrong updating the package changelog') + if 0 != subprocess.call(['debuild', '-S', '-sa'], + cwd=srcdir): + error('Something went wrong while building the source package') - print 'Please check the package in file://%s carefully' % tmpdir - while True: - answer = raw_input('Do you still want to upload this to %s? [Y/n] ' % upload).strip().lower() - if answer in ('', 'y', 'yes'): - break - elif answer in ('n', 'no'): - return 2 - - if 0 != subprocess.call(['dput', - upload, - '%s_%s_source.changes' % (package, bp_version)], - cwd=tmpdir): - error('Something went wrong uploading the package %s to %s' % package, upload) + if ':' in bp_version: + bp_version = bp_version[bp_version.find(':')+1:] + + print 'Please check the package in file://%s carefully' % tmpdir + while True: + answer = raw_input('Do you still want to upload this to %s? [Y/n] ' % upload).strip().lower() + if answer in ('', 'y', 'yes'): + if 0 != subprocess.call(['dput', + upload, + '%s_%s_source.changes' % (package, bp_version)], + cwd=tmpdir): + error('Something went wrong uploading the package %s to %s' % package, upload) + + break + elif answer in ('n', 'no'): + break + shutil.rmtree(srcdir) finally: shutil.rmtree(tmpdir) From 462cd8f4ca2c473c6d0c6ca5da291b0ab1981ea3 Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Sun, 12 Dec 2010 18:12:28 -0800 Subject: [PATCH 11/32] backportpackage: Add a --version argument --version lets you specify either the version of the source package to fetch (in the absence of a source release option) or the version to verify against the specified source release. --- backportpackage | 100 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 17 deletions(-) diff --git a/backportpackage b/backportpackage index 89865b5..aedf162 100755 --- a/backportpackage +++ b/backportpackage @@ -24,8 +24,10 @@ import sys import tempfile from debian.deb822 import Dsc +import launchpadlib.launchpad devnull = open('/dev/null', 'r+') +lp = None def error(msg, *args, **kwargs): logging.error(msg, *args, **kwargs) @@ -43,8 +45,13 @@ def parse(args): p.add_option('-f', '--from', dest='source_release', default=None, - help='Backport from SOURCE release (required)', + help='Backport from SOURCE release (defaults to devel release)', metavar='SOURCE') + p.add_option('-v', '--version', + dest='version', + default=None, + help='Package version to backport (verified if source release also specified)', + metavar='VERSION') p.add_option('-s', '--source', dest='package', help='Backport SOURCE package (required)', @@ -53,6 +60,11 @@ def parse(args): dest='upload', help='Specify an upload destination (required)', metavar='UPLOAD') + p.add_option('-l', '--launchpad', + dest='launchpad', + default='production', + help='Launchpad instance to connect to (default %default)', + metavar='INSTANCE') opts, args = p.parse_args(args) if len(args): @@ -61,14 +73,77 @@ def parse(args): 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.source_release: - p.error('You must specify the source release') if not opts.upload: p.error('You must specify an upload destination') return opts, args +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: + error('Unable to find package %s in release %s' % (package, opts.source_release)) + + if opts.version and opts.version != srcpkg.source_package_version: + error('Requested backport of version %s but %s in %s is at version %s' % + (opts.version, opts.package, opts.source_release, srcpkg.source_package_version)) + + 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'): + if 0 != subprocess.call(['dget', + '--download-only', + '--allow-unauthenticated', + f], + cwd=workdir): + error('Error went wrong fetching the source package') + + return os.path.join(workdir, os.path.basename(f)) + else: + error('Package %s contains no .dsc file' % opts.package) + def main(args): + global lp + logging.basicConfig(level=logging.INFO) os.environ['DEB_VENDOR'] = 'Ubuntu' @@ -78,22 +153,13 @@ def main(args): dest_releases = opts.dest_releases upload = opts.upload + script_name = os.path.basename(sys.argv[0]) + lp = launchpadlib.launchpad.Launchpad.login_anonymously(script_name, + opts.launchpad) + tmpdir = tempfile.mkdtemp(prefix='backportpackage-') try: - for pocket in ('-updates', '-security', ''): - if 0 == subprocess.call(['pull-lp-source', package, source_release + pocket], - cwd=tmpdir, - stdout=devnull, stderr=devnull): - logging.info('Found package %s in pocket %s' % (package, source_release + pocket)) - break - else: - error('Unable to find source package %s in release %s' % (package, source_release)) - - for dscfile in os.listdir(tmpdir): - if dscfile.endswith('.dsc'): - break - else: - error('Unable to find a .dsc file for package %s' % package) + dscfile = fetch_package(tmpdir, opts) dsc = Dsc(open(os.path.join(tmpdir, dscfile))) v = dsc['Version'] From 1a2677b8ef8b1aaebc1f87fbee3f05575ade682b Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Sun, 12 Dec 2010 18:24:28 -0800 Subject: [PATCH 12/32] backportpackage: Factor out subprocess.call error handling into a helper --- backportpackage | 57 ++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/backportpackage b/backportpackage index aedf162..405222f 100755 --- a/backportpackage +++ b/backportpackage @@ -33,6 +33,11 @@ def error(msg, *args, **kwargs): logging.error(msg, *args, **kwargs) sys.exit(1) +def check_call(cmd, *args, **kwargs): + ret = subprocess.call(cmd, *args, **kwargs) + if ret != 0: + error('%s returned %d' % (cmd, ret)) + def parse(args): usage = 'Usage: %prog [options]' p = optparse.OptionParser(usage) @@ -130,13 +135,11 @@ def fetch_package(workdir, opts): for f in srcpkg.sourceFileUrls(): if f.endswith('.dsc'): - if 0 != subprocess.call(['dget', - '--download-only', - '--allow-unauthenticated', - f], - cwd=workdir): - error('Error went wrong fetching the source package') - + check_call(['dget', + '--download-only', + '--allow-unauthenticated', + f], + cwd=workdir) return os.path.join(workdir, os.path.basename(f)) else: error('Package %s contains no .dsc file' % opts.package) @@ -166,12 +169,11 @@ def main(args): for dest_release in dest_releases: srcdir = os.path.join(tmpdir, package) - if 0 != subprocess.call(['dpkg-source', - '-x', - dscfile, - package], - cwd=tmpdir): - error('Something went wrong unpacking package %s' % package) + check_call(['dpkg-source', + '-x', + dscfile, + package], + cwd=tmpdir) bp_version = v + ('~%s1' % dest_release) bp_dist = dest_release @@ -179,17 +181,15 @@ def main(args): bp_version += '~ppa1' elif upload == 'ubuntu': bp_dist += '-backports' - if 0 != subprocess.call(['dch', - '--force-bad-version', - '--preserve', - '--newversion', bp_version, - '--distribution', dest_release, - 'No-change backport to %s' % dest_release], - cwd=srcdir): - error('Something went wrong updating the package changelog') - if 0 != subprocess.call(['debuild', '-S', '-sa'], - cwd=srcdir): - error('Something went wrong while building the source package') + 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) if ':' in bp_version: bp_version = bp_version[bp_version.find(':')+1:] @@ -198,11 +198,10 @@ def main(args): while True: answer = raw_input('Do you still want to upload this to %s? [Y/n] ' % upload).strip().lower() if answer in ('', 'y', 'yes'): - if 0 != subprocess.call(['dput', - upload, - '%s_%s_source.changes' % (package, bp_version)], - cwd=tmpdir): - error('Something went wrong uploading the package %s to %s' % package, upload) + check_call(['dput', + upload, + '%s_%s_source.changes' % (package, bp_version)], + cwd=tmpdir) break elif answer in ('n', 'no'): From 3ab323805e6149ce80394b6065d203609c05206a Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Sun, 12 Dec 2010 18:25:59 -0800 Subject: [PATCH 13/32] backportpackage: Wrap source to 80-column lines --- backportpackage | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/backportpackage b/backportpackage index 405222f..d13444f 100755 --- a/backportpackage +++ b/backportpackage @@ -50,12 +50,12 @@ def parse(args): p.add_option('-f', '--from', dest='source_release', default=None, - help='Backport from SOURCE release (defaults to devel release)', + help='Backport from SOURCE release (default: devel release)', metavar='SOURCE') p.add_option('-v', '--version', dest='version', default=None, - help='Package version to backport (verified if source release also specified)', + help='Package version to backport (or verify)', metavar='VERSION') p.add_option('-s', '--source', dest='package', @@ -68,7 +68,7 @@ def parse(args): p.add_option('-l', '--launchpad', dest='launchpad', default='production', - help='Launchpad instance to connect to (default %default)', + help='Launchpad instance to connect to (default: %default)', metavar='INSTANCE') opts, args = p.parse_args(args) @@ -99,11 +99,12 @@ def find_release_package(workdir, opts): except IndexError: continue else: - error('Unable to find package %s in release %s' % (package, opts.source_release)) + error('Unable to find package %s in release %s' % + (package, opts.source_release)) if opts.version and opts.version != srcpkg.source_package_version: - error('Requested backport of version %s but %s in %s is at version %s' % - (opts.version, opts.package, opts.source_release, srcpkg.source_package_version)) + error('Requested backport of version %s but %s is at version %s' % + (opts.version, opts.package, srcpkg.source_package_version)) return srcpkg @@ -195,12 +196,14 @@ def main(args): bp_version = bp_version[bp_version.find(':')+1:] print 'Please check the package in file://%s carefully' % tmpdir + prompt = 'Do you still want to upload this to %s? [Y/n]' % upload while True: - answer = raw_input('Do you still want to upload this to %s? [Y/n] ' % upload).strip().lower() + answer = raw_input(prompt).strip().lower() if answer in ('', 'y', 'yes'): check_call(['dput', upload, - '%s_%s_source.changes' % (package, bp_version)], + '%s_%s_source.changes' % + (package, bp_version)], cwd=tmpdir) break From 7450a5e8bbe6443e096a135c5bb8c546f14d1c5f Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Sun, 12 Dec 2010 18:26:25 -0800 Subject: [PATCH 14/32] backportpackage: Fix slightly awkward text --- backportpackage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backportpackage b/backportpackage index d13444f..a3a1104 100755 --- a/backportpackage +++ b/backportpackage @@ -196,7 +196,7 @@ def main(args): bp_version = bp_version[bp_version.find(':')+1:] print 'Please check the package in file://%s carefully' % tmpdir - prompt = 'Do you still want to upload this to %s? [Y/n]' % 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'): From 71102a4589dbb80950602199f35d8b46ba56e506 Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Sun, 12 Dec 2010 18:48:50 -0800 Subject: [PATCH 15/32] backportpackage: Refactor code some so main structure is clearer --- backportpackage | 112 ++++++++++++++++++++++++++---------------------- 1 file changed, 60 insertions(+), 52 deletions(-) diff --git a/backportpackage b/backportpackage index a3a1104..5674bf6 100755 --- a/backportpackage +++ b/backportpackage @@ -145,6 +145,61 @@ def fetch_package(workdir, opts): else: error('Package %s contains no .dsc file' % opts.package) +def get_backport_version(version, upload, release): + v = version + ('~%s1' % release) + if upload.startswith('ppa:'): + v += '~ppa1' + return v + +def get_backport_dist(upload, release): + if upload == 'ubuntu': + return '%s-backports' % release + else: + return release + +def do_backport(workdir, dscfile, release, opts): + dsc = Dsc(open(os.path.join(workdir, dscfile))) + v = dsc['Version'] + + check_call(['dpkg-source', + '-x', + dscfile, + opts.package], + cwd=workdir) + srcdir = os.path.join(workdir, opts.package) + + bp_version = get_backport_version(v, opts.upload, release) + bp_dist = get_backport_dist(opts.upload, release) + + check_call(['dch', + '--force-bad-version', + '--preserve', + '--newversion', bp_version, + '--distribution', release, + 'No-change backport to %s' % release], + cwd=srcdir) + check_call(['debuild', '-S', '-sa'], + cwd=srcdir) + + if ':' in bp_version: + bp_version = bp_version[bp_version.find(':')+1:] + + print 'Please check the package in file://%s carefully' % workdir + prompt = 'Do you want to upload this to %s? [Y/n]' % opts.upload + while True: + answer = raw_input(prompt).strip().lower() + if answer in ('', 'y', 'yes'): + check_call(['dput', + opts.upload, + '%s_%s_source.changes' % + (opts.package, bp_version)], + cwd=workdir) + + break + elif answer in ('n', 'no'): + break + shutil.rmtree(srcdir) + def main(args): global lp @@ -152,66 +207,19 @@ def main(args): os.environ['DEB_VENDOR'] = 'Ubuntu' opts, _ = parse(args[1:]) - package = opts.package - source_release = opts.source_release - dest_releases = opts.dest_releases - upload = opts.upload script_name = os.path.basename(sys.argv[0]) lp = launchpadlib.launchpad.Launchpad.login_anonymously(script_name, opts.launchpad) - tmpdir = tempfile.mkdtemp(prefix='backportpackage-') + workdir = tempfile.mkdtemp(prefix='backportpackage-') try: - dscfile = fetch_package(tmpdir, opts) + dscfile = fetch_package(workdir, opts) - dsc = Dsc(open(os.path.join(tmpdir, dscfile))) - v = dsc['Version'] - - for dest_release in dest_releases: - srcdir = os.path.join(tmpdir, package) - check_call(['dpkg-source', - '-x', - dscfile, - package], - cwd=tmpdir) - - bp_version = v + ('~%s1' % dest_release) - bp_dist = dest_release - if upload.startswith('ppa:'): - bp_version += '~ppa1' - elif upload == 'ubuntu': - bp_dist += '-backports' - 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) - - if ':' in bp_version: - bp_version = bp_version[bp_version.find(':')+1:] - - print 'Please check the package in file://%s carefully' % tmpdir - 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'): - check_call(['dput', - upload, - '%s_%s_source.changes' % - (package, bp_version)], - cwd=tmpdir) - - break - elif answer in ('n', 'no'): - break - shutil.rmtree(srcdir) + for release in opts.dest_releases: + do_backport(workdir, dscfile, release, opts) finally: - shutil.rmtree(tmpdir) + shutil.rmtree(workdir) if __name__ == '__main__': sys.exit(main(sys.argv)) From 7dd3ae05d70b15afdff639c386c1b354b663e1c5 Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Sun, 12 Dec 2010 18:54:04 -0800 Subject: [PATCH 16/32] backportpackage: Make the source package an argument instead of an option --- backportpackage | 46 ++++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/backportpackage b/backportpackage index 5674bf6..7085362 100755 --- a/backportpackage +++ b/backportpackage @@ -39,7 +39,7 @@ def check_call(cmd, *args, **kwargs): error('%s returned %d' % (cmd, ret)) def parse(args): - usage = 'Usage: %prog [options]' + usage = 'Usage: %prog [options] ' p = optparse.OptionParser(usage) p.add_option('-t', '--to', dest='dest_releases', @@ -57,10 +57,6 @@ def parse(args): default=None, help='Package version to backport (or verify)', metavar='VERSION') - 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)', @@ -72,10 +68,8 @@ def parse(args): metavar='INSTANCE') opts, args = p.parse_args(args) - if len(args): - p.error('Invalid arguments') - if not opts.package: - p.error('You must specify a package to backport') + 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') if not opts.upload: @@ -83,14 +77,14 @@ def parse(args): return opts, args -def find_release_package(workdir, opts): +def find_release_package(workdir, package, 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, + srcpkg = archive.getPublishedSources(source_name=package, distro_series=series, pocket=pocket, status=status, @@ -104,23 +98,23 @@ def find_release_package(workdir, opts): if opts.version and opts.version != srcpkg.source_package_version: error('Requested backport of version %s but %s is at version %s' % - (opts.version, opts.package, srcpkg.source_package_version)) + (opts.version, package, srcpkg.source_package_version)) return srcpkg -def find_version_package(workdir, opts): +def find_version_package(workdir, package, 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, + return archive.getPublishedSources(source_name=package, version=opts.version)[0] except IndexError: error('Package %s was never published with version %s in Ubuntu' % - (opts.package, opts.version)) + (package, opts.version)) -def fetch_package(workdir, opts): +def fetch_package(workdir, package, opts): # Returns the path to the .dsc file that was fetched ubuntu = lp.distributions['ubuntu'] @@ -130,9 +124,9 @@ def fetch_package(workdir, opts): # If source_release is specified, then version is just for # verification if opts.source_release: - srcpkg = find_release_package(workdir, opts) + srcpkg = find_release_package(workdir, package, opts) else: - srcpkg = find_version_package(workdir, opts) + srcpkg = find_version_package(workdir, package, opts) for f in srcpkg.sourceFileUrls(): if f.endswith('.dsc'): @@ -143,7 +137,7 @@ def fetch_package(workdir, opts): cwd=workdir) return os.path.join(workdir, os.path.basename(f)) else: - error('Package %s contains no .dsc file' % opts.package) + error('Package %s contains no .dsc file' % package) def get_backport_version(version, upload, release): v = version + ('~%s1' % release) @@ -157,16 +151,16 @@ def get_backport_dist(upload, release): else: return release -def do_backport(workdir, dscfile, release, opts): +def do_backport(workdir, package, dscfile, release, opts): dsc = Dsc(open(os.path.join(workdir, dscfile))) v = dsc['Version'] check_call(['dpkg-source', '-x', dscfile, - opts.package], + package], cwd=workdir) - srcdir = os.path.join(workdir, opts.package) + srcdir = os.path.join(workdir, package) bp_version = get_backport_version(v, opts.upload, release) bp_dist = get_backport_dist(opts.upload, release) @@ -192,7 +186,7 @@ def do_backport(workdir, dscfile, release, opts): check_call(['dput', opts.upload, '%s_%s_source.changes' % - (opts.package, bp_version)], + (package, bp_version)], cwd=workdir) break @@ -206,7 +200,7 @@ def main(args): logging.basicConfig(level=logging.INFO) os.environ['DEB_VENDOR'] = 'Ubuntu' - opts, _ = parse(args[1:]) + opts, (package,) = parse(args[1:]) script_name = os.path.basename(sys.argv[0]) lp = launchpadlib.launchpad.Launchpad.login_anonymously(script_name, @@ -214,10 +208,10 @@ def main(args): workdir = tempfile.mkdtemp(prefix='backportpackage-') try: - dscfile = fetch_package(workdir, opts) + dscfile = fetch_package(workdir, package, opts) for release in opts.dest_releases: - do_backport(workdir, dscfile, release, opts) + do_backport(workdir, package, dscfile, release, opts) finally: shutil.rmtree(workdir) From aff1d83e0263f46d2301b3eed912eb5958a43d33 Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Sun, 12 Dec 2010 19:16:59 -0800 Subject: [PATCH 17/32] backportpackage: Update the manpage for the new UI --- backportpackage | 8 ++++---- doc/backportpackage.1 | 45 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/backportpackage b/backportpackage index 7085362..e87af9f 100755 --- a/backportpackage +++ b/backportpackage @@ -41,6 +41,10 @@ def check_call(cmd, *args, **kwargs): def parse(args): usage = 'Usage: %prog [options] ' p = optparse.OptionParser(usage) + p.add_option('-u', '--upload', + dest='upload', + help='Specify an upload destination (required)', + metavar='UPLOAD') p.add_option('-t', '--to', dest='dest_releases', default=[], @@ -57,10 +61,6 @@ def parse(args): default=None, help='Package version to backport (or verify)', metavar='VERSION') - p.add_option('-u', '--upload', - dest='upload', - help='Specify an upload destination (required)', - metavar='UPLOAD') p.add_option('-l', '--launchpad', dest='launchpad', default='production', diff --git a/doc/backportpackage.1 b/doc/backportpackage.1 index 346fa06..0234a91 100644 --- a/doc/backportpackage.1 +++ b/doc/backportpackage.1 @@ -2,20 +2,55 @@ .SH NAME backportpackage \- helper to test package backports .SH SYNOPSIS -.B backportpackage -\fI \fR +.TP +.B backportpackage \fR[\fIadditional options\fR] +\-\-to <\fIdest release\fR> +.br +\-\-upload <\fIupload target\fR> +.br +<\fIsource package\fR> +.PP +.B backportpackage \-h +.SH OPTIONS +.TP +.BR \-u \fIUPLOAD\fR, \-\-upload=\fIUPLOAD\fR +\fBRequired\fR. Upload to \fIUPLOAD\fR with \fBdput\fR(1) (after +confirmation). +.TP +.BR \-t \fIDEST\fR, \-\-to=\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. +.TP +.BR \-f \fISOURCE\fR, \-\-from=\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 +release. +.TP +.BR \-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, +\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). +.TP +.BR \-l \fIINSTANCE\fR, \-\-launchpad=\fIINSTANCE\fR +Use the specified instance of Launchpad (e.g. "staging"), instead of +the default of "production". .SH DESCRIPTION -\fIbackportpackage\fR fetches a package from one Ubuntu release and +\fBbackportpackage\fR fetches a package from one Ubuntu release and creates a no-change backport of that package to a previous release, 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. .PP -\fIbackportpackage\fR is only recommended for testing backports in a +\fBbackportpackage\fR is only recommended for testing backports in a PPA, not uploading backports to the Ubuntu archive. .SH AUTHOR -\fIbackportpackage\fR and this manpage were written by Evan Broder +\fBbackportpackage\fR and this manpage were written by Evan Broder .PP Both are released under GNU General Public License, version 2. From fc7faa978096b9db0640c6d0dac08cc56aabcf85 Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Sun, 12 Dec 2010 19:27:08 -0800 Subject: [PATCH 18/32] sponsor-patch, backportpackage: Factor out logging code from sponsor-patch and share it with backportpackage --- backportpackage | 9 +-- sponsor-patch | 158 +++++++++++++++++------------------------- ubuntutools/logger.py | 55 +++++++++++++++ 3 files changed, 122 insertions(+), 100 deletions(-) create mode 100644 ubuntutools/logger.py diff --git a/backportpackage b/backportpackage index e87af9f..3ec373a 100755 --- a/backportpackage +++ b/backportpackage @@ -15,7 +15,6 @@ # # ################################################################## -import logging import optparse import os import shutil @@ -26,14 +25,17 @@ import tempfile from debian.deb822 import Dsc import launchpadlib.launchpad +from ubuntutools.logger import Logger + devnull = open('/dev/null', 'r+') lp = None -def error(msg, *args, **kwargs): - logging.error(msg, *args, **kwargs) +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, ret)) @@ -197,7 +199,6 @@ def do_backport(workdir, package, dscfile, release, opts): def main(args): global lp - logging.basicConfig(level=logging.INFO) os.environ['DEB_VENDOR'] = 'Ubuntu' opts, (package,) = parse(args[1:]) diff --git a/sponsor-patch b/sponsor-patch index 0f6ca7c..bdda4b4 100755 --- a/sponsor-patch +++ b/sponsor-patch @@ -29,6 +29,7 @@ import debian.debian_support import launchpadlib.launchpad import ubuntutools.update_maintainer +from ubuntutools.logger import Logger USER_ABORT = 2 @@ -57,7 +58,7 @@ class BugTask(object): dsc_file = None for url in source_files: filename = urllib.unquote(os.path.basename(url)) - Print.info("Downloading %s..." % (filename)) + Logger.info("Downloading %s..." % (filename)) urllib.urlretrieve(url, filename) if url.endswith(".dsc"): dsc_file = filename @@ -171,7 +172,7 @@ class Pbuilder(Builder): cmd = ["sudo", "-E", "DIST=" + dist, "pbuilder", "--build", "--distribution", dist, "--architecture", self.architecture, "--buildresult", result_directory, dsc_file] - Print.command(cmd) + Logger.command(cmd) return subprocess.call(cmd) @@ -181,13 +182,13 @@ class Sbuild(Builder): def build(self, dsc_file, dist, result_directory): workdir = os.getcwd() - Print.command(["cd", result_directory]) + Logger.command(["cd", result_directory]) os.chdir(result_directory) cmd = ["sbuild", "--arch-all", "--dist=" + dist, "--arch=" + self.architecture, dsc_file] - Print.command(cmd) + Logger.command(cmd) result = subprocess.call(cmd) - Print.command(["cd", workdir]) + Logger.command(["cd", workdir]) os.chdir(workdir) return result @@ -220,41 +221,6 @@ class Patch(object): self.changed_files)) > 0 -class Print(object): - script_name = os.path.basename(sys.argv[0]) - verbose = False - - @classmethod - def command(cls, cmd): - if cls.verbose: - for i in xrange(len(cmd)): - if cmd[i].find(" ") >= 0: - cmd[i] = '"' + cmd[i] + '"' - print "%s: I: %s" % (script_name, " ".join(cmd)) - - @classmethod - def debug(cls, message): - if cls.verbose: - print "%s: D: %s" % (script_name, message) - - @classmethod - def error(cls, message): - print >> sys.stderr, "%s: Error: %s" % (script_name, message) - - @classmethod - def info(cls, message): - if cls.verbose: - print "%s: I: %s" % (script_name, message) - - @classmethod - def normal(cls, message): - print "%s: %s" % (script_name, message) - - @classmethod - def set_verbosity(cls, verbose): - cls.verbose = verbose - - def get_source_package_name(bug_task): package = None if bug_task.bug_target_name != "ubuntu": @@ -334,7 +300,7 @@ def yes_edit_no_question(question, default): def edit_source(): # Spawn shell to allow modifications cmd = [get_user_shell()] - Print.command(cmd) + Logger.command(cmd) print """An interactive shell was launched in file://%s Edit your files. When you are done, exit the shell. If you wish to abort the @@ -342,7 +308,7 @@ process, exit the shell such that it returns an exit code other than zero. """ % (os.getcwd()), returncode = subprocess.call(cmd) if returncode != 0: - Print.error("Shell exited with exit value %i." % (returncode)) + Logger.error("Shell exited with exit value %i." % (returncode)) sys.exit(1) def get_fixed_lauchpad_bugs(changes_file): @@ -380,10 +346,10 @@ def get_patch_or_branch(bug): linked_branches = map(lambda b: b.branch, bug.linked_branches) if len(attached_patches) == 0 and len(linked_branches) == 0: if len(bug.attachments) == 0: - Print.error("No attachment and no linked branch found on bug #%i." \ + Logger.error("No attachment and no linked branch found on bug #%i." \ % (bug.id)) else: - Print.error(("No attached patch and no linked branch found. Go " \ + Logger.error(("No attached patch and no linked branch found. Go " \ "to https://launchpad.net/bugs/%i and mark an " \ "attachment as patch.") % (bug.id)) sys.exit(1) @@ -393,13 +359,13 @@ def get_patch_or_branch(bug): branch = linked_branches[0].bzr_identity else: if len(attached_patches) == 0: - Print.normal("https://launchpad.net/bugs/%i has %i branches " \ + Logger.normal("https://launchpad.net/bugs/%i has %i branches " \ "linked:" % (bug.id, len(linked_branches))) elif len(linked_branches) == 0: - Print.normal("https://launchpad.net/bugs/%i has %i patches" \ + Logger.normal("https://launchpad.net/bugs/%i has %i patches" \ " attached:" % (bug.id, len(attached_patches))) else: - Print.normal("https://launchpad.net/bugs/%i has %i branch(es)" \ + Logger.normal("https://launchpad.net/bugs/%i has %i branch(es)" \ " linked and %i patch(es) attached:" % \ (bug.id, len(linked_branches), len(attached_patches))) i = 0 @@ -421,11 +387,11 @@ def download_patch(patch): patch_filename = re.sub(" ", "_", patch.title) if not reduce(lambda r, x: r or patch.title.endswith(x), (".debdiff", ".diff", ".patch"), False): - Print.info("Patch %s does not have a proper file extension." % \ + Logger.info("Patch %s does not have a proper file extension." % \ (patch.title)) patch_filename += ".patch" - Print.info("Downloading %s." % (patch_filename)) + Logger.info("Downloading %s." % (patch_filename)) patch_file = open(patch_filename, "w") patch_file.write(patch.data.open().read()) patch_file.close() @@ -436,18 +402,18 @@ def download_branch(branch): if os.path.isdir(dir_name): shutil.rmtree(dir_name) cmd = ["bzr", "branch", branch] - Print.command(cmd) + Logger.command(cmd) if subprocess.call(cmd) != 0: - Print.error("Failed to download branch %s." % (branch)) + Logger.error("Failed to download branch %s." % (branch)) sys.exit(1) return dir_name def merge_branch(branch): edit = False cmd = ["bzr", "merge", branch] - Print.command(cmd) + Logger.command(cmd) if subprocess.call(cmd) != 0: - Print.error("Failed to merge branch %s." % (branch)) + Logger.error("Failed to merge branch %s." % (branch)) ask_for_manual_fixing() edit = True return edit @@ -456,9 +422,9 @@ def extract_source(dsc_file, verbose=False): cmd = ["dpkg-source", "--no-preparation", "-x", dsc_file] if not verbose: cmd.insert(1, "-q") - Print.command(cmd) + Logger.command(cmd) if subprocess.call(cmd) != 0: - Print.error("Extraction of %s failed." % (os.path.basename(dsc_file))) + Logger.error("Extraction of %s failed." % (os.path.basename(dsc_file))) sys.exit(1) def apply_patch(task, patch): @@ -466,9 +432,9 @@ def apply_patch(task, patch): if patch.is_debdiff(): cmd = ["patch", "--merge", "--force", "-p", str(patch.get_strip_level()), "-i", patch.full_path] - Print.command(cmd) + Logger.command(cmd) if subprocess.call(cmd) != 0: - Print.error("Failed to apply debdiff %s to %s %s." % \ + Logger.error("Failed to apply debdiff %s to %s %s." % \ (patch.get_name(), task.package, task.get_version())) if not edit: ask_for_manual_fixing() @@ -477,9 +443,9 @@ def apply_patch(task, patch): # FIXME: edit-patch needs a non-interactive mode # https://launchpad.net/bugs/612566 cmd = ["edit-patch", patch.full_path] - Print.command(cmd) + Logger.command(cmd) if subprocess.call(cmd) != 0: - Print.error("Failed to apply diff %s to %s %s." % \ + Logger.error("Failed to apply diff %s to %s %s." % \ (patch.get_name(), task.package, task.get_version())) if not edit: ask_for_manual_fixing() @@ -493,11 +459,11 @@ def main(script_name, bug_number, build, edit, keyid, upload, workdir, builder, try: os.makedirs(workdir) except os.error, error: - Print.error("Failed to create the working directory %s [Errno " \ + Logger.error("Failed to create the working directory %s [Errno " \ "%i]: %s." % (workdir, error.errno, error.strerror)) sys.exit(1) if workdir != os.getcwd(): - Print.command(["cd", workdir]) + Logger.command(["cd", workdir]) os.chdir(workdir) script_name = os.path.basename(sys.argv[0]) @@ -510,13 +476,13 @@ def main(script_name, bug_number, build, edit, keyid, upload, workdir, builder, bug_tasks = map(lambda x: BugTask(x, launchpad), bug.bug_tasks) ubuntu_tasks = filter(lambda x: x.is_ubuntu_task(), bug_tasks) if len(ubuntu_tasks) == 0: - Print.error("No Ubuntu bug task found on bug #%i." % (bug_number)) + Logger.error("No Ubuntu bug task found on bug #%i." % (bug_number)) sys.exit(1) elif len(ubuntu_tasks) == 1: task = ubuntu_tasks[0] if len(ubuntu_tasks) > 1: if verbose: - Print.info("%i Ubuntu tasks exist for bug #%i." % \ + Logger.info("%i Ubuntu tasks exist for bug #%i." % \ (len(ubuntu_tasks), bug_number)) for task in ubuntu_tasks: print task.get_short_info() @@ -524,7 +490,7 @@ def main(script_name, bug_number, build, edit, keyid, upload, workdir, builder, if len(open_ubuntu_tasks) == 1: task = open_ubuntu_tasks[0] else: - Print.normal("https://launchpad.net/bugs/%i has %i Ubuntu tasks:" \ + Logger.normal("https://launchpad.net/bugs/%i has %i Ubuntu tasks:" \ % (bug_number, len(ubuntu_tasks))) for i in xrange(len(ubuntu_tasks)): print "%i) %s" % (i + 1, @@ -532,7 +498,7 @@ def main(script_name, bug_number, build, edit, keyid, upload, workdir, builder, selected = input_number("To which Ubuntu tasks do the patch belong", 1, len(ubuntu_tasks)) task = ubuntu_tasks[selected - 1] - Print.info("Selected Ubuntu task: %s" % (task.get_short_info())) + Logger.info("Selected Ubuntu task: %s" % (task.get_short_info())) dsc_file = task.download_source() assert os.path.isfile(dsc_file), "%s does not exist." % (dsc_file) @@ -540,15 +506,15 @@ def main(script_name, bug_number, build, edit, keyid, upload, workdir, builder, if patch: patch = download_patch(patch) - Print.info("Ubuntu package: %s" % (task.package)) + Logger.info("Ubuntu package: %s" % (task.package)) if task.is_merge(): - Print.info("The task is a merge request.") + Logger.info("The task is a merge request.") extract_source(dsc_file, verbose) # change directory directory = task.package + '-' + task.get_version().upstream_version - Print.command(["cd", directory]) + Logger.command(["cd", directory]) os.chdir(directory) edit |= apply_patch(task, patch) @@ -556,7 +522,7 @@ def main(script_name, bug_number, build, edit, keyid, upload, workdir, builder, branch_dir = download_branch(task.get_branch_link()) # change directory - Print.command(["cd", branch_dir]) + Logger.command(["cd", branch_dir]) os.chdir(branch_dir) edit |= merge_branch(branch) @@ -568,9 +534,9 @@ def main(script_name, bug_number, build, edit, keyid, upload, workdir, builder, edit = True # update the Maintainer field - Print.command(["update-maintainer"]) + Logger.command(["update-maintainer"]) if ubuntutools.update_maintainer.update_maintainer(verbose) != 0: - Print.error("update-maintainer script failed.") + Logger.error("update-maintainer script failed.") sys.exit(1) # Get new version of package @@ -578,7 +544,7 @@ def main(script_name, bug_number, build, edit, keyid, upload, workdir, builder, try: new_version = changelog.get_version() except IndexError: - Print.error("Debian package version could not be determined. " \ + Logger.error("Debian package version could not be determined. " \ "debian/changelog is probably malformed.") ask_for_manual_fixing() continue @@ -586,15 +552,15 @@ def main(script_name, bug_number, build, edit, keyid, upload, workdir, builder, # Check if version of the new package is greater than the version in # the archive. if new_version <= task.get_version(): - Print.error("The version %s is not greater than the already " \ + Logger.error("The version %s is not greater than the already " \ "available %s." % (new_version, task.get_version())) ask_for_manual_fixing() continue cmd = ["dch", "--maintmaint", "--edit", ""] - Print.command(cmd) + Logger.command(cmd) if subprocess.call(cmd) != 0: - Print.info("Failed to update timestamp in debian/changelog.") + Logger.info("Failed to update timestamp in debian/changelog.") # Build source package if patch: @@ -615,9 +581,9 @@ def main(script_name, bug_number, build, edit, keyid, upload, workdir, builder, env = os.environ if upload == 'ubuntu': env['DEB_VENDOR'] = 'Ubuntu' - Print.command(cmd) + Logger.command(cmd) if subprocess.call(cmd, env=env) != 0: - Print.error("Failed to build source tarball.") + Logger.error("Failed to build source tarball.") # TODO: Add a "retry" option ask_for_manual_fixing() continue @@ -634,7 +600,7 @@ def main(script_name, bug_number, build, edit, keyid, upload, workdir, builder, debdiff_filename = os.path.join(workdir, debdiff_name) if not verbose: cmd.insert(1, "-q") - Print.command(cmd + [">", debdiff_filename]) + Logger.command(cmd + [">", debdiff_filename]) process = subprocess.Popen(cmd, stdout=subprocess.PIPE) debdiff = process.communicate()[0] @@ -646,7 +612,7 @@ def main(script_name, bug_number, build, edit, keyid, upload, workdir, builder, # Make sure that the Launchpad bug will be closed changes_file = new_dsc_file[:-4] + "_source.changes" if bug_number not in get_fixed_lauchpad_bugs(changes_file): - Print.error("Launchpad bug #%i is not closed by new version." % \ + Logger.error("Launchpad bug #%i is not closed by new version." % \ (bug_number)) ask_for_manual_fixing() continue @@ -660,7 +626,7 @@ def main(script_name, bug_number, build, edit, keyid, upload, workdir, builder, allowed = map(lambda s: s + "-proposed", supported_series) + \ [devel_series] if changelog.distributions not in allowed: - Print.error("%s is not an allowed series. It needs to be one " \ + Logger.error("%s is not an allowed series. It needs to be one " \ "of %s." % (changelog.distributions, ", ".join(allowed))) ask_for_manual_fixing() @@ -668,7 +634,7 @@ def main(script_name, bug_number, build, edit, keyid, upload, workdir, builder, elif upload and upload.startwith("ppa/"): allowed = supported_series + [devel_series] if changelog.distributions not in allowed: - Print.error("%s is not an allowed series. It needs to be one " \ + Logger.error("%s is not an allowed series. It needs to be one " \ "of %s." % (changelog.distributions, ", ".join(allowed))) ask_for_manual_fixing() @@ -683,7 +649,7 @@ def main(script_name, bug_number, build, edit, keyid, upload, workdir, builder, dist = re.sub("-.*$", "", changelog.distributions) result = builder.build(new_dsc_file, dist, buildresult) if result != 0: - Print.error("Failed to build %s from source with %s." % \ + Logger.error("Failed to build %s from source with %s." % \ (os.path.basename(new_dsc_file), builder.get_name())) # TODO: Add "retry" and "update" option @@ -699,7 +665,7 @@ def main(script_name, bug_number, build, edit, keyid, upload, workdir, builder, cmd = ["lintian", "-IE", "--pedantic", "-q", build_changes] lintian_filename = os.path.join(workdir, task.package + "_" + strip_epoch(new_version) + ".lintian") - Print.command(cmd + [">", lintian_filename]) + Logger.command(cmd + [">", lintian_filename]) process = subprocess.Popen(cmd, stdout=subprocess.PIPE) report = process.communicate()[0] @@ -727,26 +693,26 @@ def main(script_name, bug_number, build, edit, keyid, upload, workdir, builder, print "Abort." sys.exit(USER_ABORT) cmd = ["dput", "--force", upload, changes_file] - Print.command(cmd) + Logger.command(cmd) if subprocess.call(cmd) != 0: - Print.error("Upload of %s to %s failed." % \ + Logger.error("Upload of %s to %s failed." % \ (os.path.basename(changes_file), upload)) sys.exit(1) if branch: cmd = ['debcommit'] - Print.command(cmd) + Logger.command(cmd) if subprocess.call(cmd) != 0: - Print.error('Bzr commit failed.') + Logger.error('Bzr commit failed.') sys.exit(1) cmd = ['bzr', 'mark-uploaded'] - Print.command(cmd) + Logger.command(cmd) if subprocess.call(cmd) != 0: - Print.error('Bzr tagging failed.') + Logger.error('Bzr tagging failed.') sys.exit(1) cmd = ['bzr', 'push', ':parent'] - Print.command(cmd) + Logger.command(cmd) if subprocess.call(cmd) != 0: - Print.error('Bzr push failed.') + Logger.error('Bzr push failed.') sys.exit(1) # Leave while loop if everything worked @@ -790,20 +756,20 @@ if __name__ == "__main__": help="Specify a working directory.") (options, args) = parser.parse_args() - Print.set_verbosity(options.verbose) + Logger.set_verbosity(options.verbose) if len(args) == 0: - Print.error("No bug number specified.") + Logger.error("No bug number specified.") sys.exit(1) elif len(args) > 1: - Print.error("Multiple bug numbers specified: %s" % (", ".join(args))) + Logger.error("Multiple bug numbers specified: %s" % (", ".join(args))) sys.exit(1) bug_number = args[0] if bug_number.isdigit(): bug_number = int(bug_number) else: - Print.error("Invalid bug number specified: %s" % (bug_number)) + Logger.error("Invalid bug number specified: %s" % (bug_number)) sys.exit(1) if options.builder == "pbuilder": @@ -811,7 +777,7 @@ if __name__ == "__main__": elif options.builder == "sbuild": builder = Sbuild() else: - Print.error("Unsupported builder specified: %s. Only pbuilder and " + Logger.error("Unsupported builder specified: %s. Only pbuilder and " "sbuild are supported." % (options.builder)) sys.exit(1) diff --git a/ubuntutools/logger.py b/ubuntutools/logger.py new file mode 100644 index 0000000..db6a016 --- /dev/null +++ b/ubuntutools/logger.py @@ -0,0 +1,55 @@ +# +# logger.py - A simple logging helper class +# +# Copyright (C) 2010, Benjamin Drung +# +# 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 os +import sys + +class Logger(object): + script_name = os.path.basename(sys.argv[0]) + verbose = False + + @classmethod + def command(cls, cmd): + if cls.verbose: + for i in xrange(len(cmd)): + if cmd[i].find(" ") >= 0: + cmd[i] = '"' + cmd[i] + '"' + print "%s: I: %s" % (cls.script_name, " ".join(cmd)) + + @classmethod + def debug(cls, message): + if cls.verbose: + print "%s: D: %s" % (cls.script_name, message) + + @classmethod + def error(cls, message): + print >> sys.stderr, "%s: Error: %s" % (cls.script_name, message) + + @classmethod + def info(cls, message): + if cls.verbose: + print "%s: I: %s" % (cls.script_name, message) + + @classmethod + def normal(cls, message): + print "%s: %s" % (cls.script_name, message) + + @classmethod + def set_verbosity(cls, verbose): + cls.verbose = verbose From b56b02683b0ce330c3c2d3a1308f2b0e150a23d1 Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Sun, 12 Dec 2010 19:45:52 -0800 Subject: [PATCH 19/32] sponsor-patch: Separate multi-builder support into a Python module. Rename SPONSOR_PATCH_BUILDER environment variable to UBUNTUTOOLS_BUILDER (but still support SPONSOR_PATCH_BUILDER) --- doc/sponsor-patch.1 | 11 ++++-- sponsor-patch | 56 +++-------------------------- ubuntutools/builder.py | 81 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 55 deletions(-) create mode 100644 ubuntutools/builder.py diff --git a/doc/sponsor-patch.1 b/doc/sponsor-patch.1 index 5a59f26..c0e5b6b 100644 --- a/doc/sponsor-patch.1 +++ b/doc/sponsor-patch.1 @@ -61,7 +61,7 @@ by \fBpbuilderrc\fR(5) to select the correct base image. .B \-B \fIBUILDER\fR, \fB\-\-builder\fR=\fIBUILDER Use the specify builder to build the package. Supported are \fBpbuilder\fR(8) and \fBsbuild\fR(1). -This overrides \fBSPONSOR_PATCH_BUILDER\fR. +This overrides \fBUBUNTUTOOLS_BUILDER\fR and \fBSPONSOR_PATCH_BUILDER\fR. The default is \fBpbuilder\fR(8). .TP .BR \-e ", " \-\-edit @@ -90,11 +90,16 @@ Display a help message and exit. .SH ENVIRONMENT .TP -.B SPONSOR_PATCH_BUILDER -The default builder for \fBsponsor\-patch\fR. +.B UBUNTUTOOLS_BUILDER +The default builder for Ubuntu development tools that support it (including \fBsponsor\-patch\fR. Supported are \fBpbuilder\fR(8) and \fBsbuild\fR(1). If unset and not provided on the command line, \fBpbuilder\fR(8) is used. +.TP +.B SPONSOR_PATCH_BUILDER +The default builder for \fBsponsor\-patch\fR. +If specified, this overrides \fBUBUNTUTOOLS_BUILDER\fR. + .TP .B SPONSOR_PATCH_WORKDIR The default working directory for \fBsponsor\-patch\fR. If unset and not diff --git a/sponsor-patch b/sponsor-patch index bdda4b4..0d8cde6 100755 --- a/sponsor-patch +++ b/sponsor-patch @@ -28,6 +28,7 @@ import debian.deb822 import debian.debian_support import launchpadlib.launchpad +from ubuntutools.builder import getBuilder import ubuntutools.update_maintainer from ubuntutools.logger import Logger @@ -149,50 +150,6 @@ class BugTask(object): return self.project == "ubuntu" -class Builder(object): - def __init__(self, name): - self.name = name - cmd = ["dpkg-architecture", "-qDEB_BUILD_ARCH_CPU"] - process = subprocess.Popen(cmd, stdout=subprocess.PIPE) - self.architecture = process.communicate()[0].strip() - - def get_architecture(self): - return self.architecture - - def get_name(self): - return self.name - - -class Pbuilder(Builder): - def __init__(self): - Builder.__init__(self, "pbuilder") - - def build(self, dsc_file, dist, result_directory): - # TODO: Do not rely on a specific pbuilder configuration. - cmd = ["sudo", "-E", "DIST=" + dist, "pbuilder", "--build", - "--distribution", dist, "--architecture", self.architecture, - "--buildresult", result_directory, dsc_file] - Logger.command(cmd) - return subprocess.call(cmd) - - -class Sbuild(Builder): - def __init__(self): - Builder.__init__(self, "sbuild") - - def build(self, dsc_file, dist, result_directory): - workdir = os.getcwd() - Logger.command(["cd", result_directory]) - os.chdir(result_directory) - cmd = ["sbuild", "--arch-all", "--dist=" + dist, - "--arch=" + self.architecture, dsc_file] - Logger.command(cmd) - result = subprocess.call(cmd) - Logger.command(["cd", workdir]) - os.chdir(workdir) - return result - - class Patch(object): def __init__(self, patch_file): self.patch_file = patch_file @@ -732,7 +689,7 @@ if __name__ == "__main__": if "SPONSOR_PATCH_BUILDER" in os.environ: default_builder = os.environ["SPONSOR_PATCH_BUILDER"] else: - default_builder = "pbuilder" + default_builder = None parser.add_option("-b", "--build", dest="build", help="Build the package with the specified builder.", @@ -772,13 +729,8 @@ if __name__ == "__main__": Logger.error("Invalid bug number specified: %s" % (bug_number)) sys.exit(1) - if options.builder == "pbuilder": - builder = Pbuilder() - elif options.builder == "sbuild": - builder = Sbuild() - else: - Logger.error("Unsupported builder specified: %s. Only pbuilder and " - "sbuild are supported." % (options.builder)) + builder = getBuilder(options.builder) + if not builder: sys.exit(1) if options.sponsoring: diff --git a/ubuntutools/builder.py b/ubuntutools/builder.py new file mode 100644 index 0000000..9b66a66 --- /dev/null +++ b/ubuntutools/builder.py @@ -0,0 +1,81 @@ +# +# builder.py - Helper classes for building packages +# +# Copyright (C) 2010, Benjamin Drung +# Copyright (C) 2010, Evan Broder +# +# 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 os +import subprocess + +from ubuntutools.logger import Logger + +class Builder(object): + def __init__(self, name): + self.name = name + cmd = ["dpkg-architecture", "-qDEB_BUILD_ARCH_CPU"] + process = subprocess.Popen(cmd, stdout=subprocess.PIPE) + self.architecture = process.communicate()[0].strip() + + def get_architecture(self): + return self.architecture + + def get_name(self): + return self.name + + +class Pbuilder(Builder): + def __init__(self): + Builder.__init__(self, "pbuilder") + + def build(self, dsc_file, dist, result_directory): + # TODO: Do not rely on a specific pbuilder configuration. + cmd = ["sudo", "-E", "DIST=" + dist, "pbuilder", "--build", + "--distribution", dist, "--architecture", self.architecture, + "--buildresult", result_directory, dsc_file] + Logger.command(cmd) + return subprocess.call(cmd) + + +class Sbuild(Builder): + def __init__(self): + Builder.__init__(self, "sbuild") + + def build(self, dsc_file, dist, result_directory): + workdir = os.getcwd() + Logger.command(["cd", result_directory]) + os.chdir(result_directory) + cmd = ["sbuild", "--arch-all", "--dist=" + dist, + "--arch=" + self.architecture, dsc_file] + Logger.command(cmd) + result = subprocess.call(cmd) + Logger.command(["cd", workdir]) + os.chdir(workdir) + return result + + +def getBuilder(builder=None): + if not builder: + builder = os.environ.get('UBUNTUTOOLS_BUILDER', 'pbuilder') + + if builder == 'pbuilder': + return Pbuilder() + elif builder == 'sbuild': + return Sbuild() + + Logger.error("Unsupported builder specified: %s. Only pbuilder and " + "sbuild are supported." % (options.builder)) From ccb14b68315adcdcc45e343632453a17e801053c Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Sun, 12 Dec 2010 20:11:49 -0800 Subject: [PATCH 20/32] backportpackage: Add a --build option. (And a --builder option) --- backportpackage | 69 ++++++++++++++++++++++++++++++------------- doc/backportpackage.1 | 36 ++++++++++++++++------ 2 files changed, 75 insertions(+), 30 deletions(-) diff --git a/backportpackage b/backportpackage index 3ec373a..1703c6e 100755 --- a/backportpackage +++ b/backportpackage @@ -25,6 +25,7 @@ import tempfile from debian.deb822 import Dsc import launchpadlib.launchpad +from ubuntutools.builder import getBuilder from ubuntutools.logger import Logger devnull = open('/dev/null', 'r+') @@ -43,10 +44,6 @@ def check_call(cmd, *args, **kwargs): def parse(args): usage = 'Usage: %prog [options] ' p = optparse.OptionParser(usage) - p.add_option('-u', '--upload', - dest='upload', - help='Specify an upload destination (required)', - metavar='UPLOAD') p.add_option('-t', '--to', dest='dest_releases', default=[], @@ -58,6 +55,20 @@ def parse(args): default=None, help='Backport from SOURCE release (default: devel release)', metavar='SOURCE') + 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') + p.add_option('-u', '--upload', + dest='upload', + help='Specify an upload destination', + metavar='UPLOAD') p.add_option('-v', '--version', dest='version', default=None, @@ -74,8 +85,6 @@ def parse(args): p.error('You must specify a source package') 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') return opts, args @@ -143,16 +152,42 @@ def fetch_package(workdir, package, opts): def get_backport_version(version, upload, release): v = version + ('~%s1' % release) - if upload.startswith('ppa:'): + if upload and upload.startswith('ppa:'): v += '~ppa1' return v def get_backport_dist(upload, release): - if upload == 'ubuntu': + if not upload or upload == 'ubuntu': return '%s-backports' % release else: return release +def do_build(workdir, package, release, bp_version, opts): + builder = getBuilder(opts.builder) + if not builder: + 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 + while True: + answer = raw_input(prompt).strip().lower() + if answer in ('', 'y', 'yes'): + break + elif answer in ('n', 'no'): + return + + check_call(['dput', + opts.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'] @@ -181,20 +216,12 @@ 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 - prompt = 'Do you want to upload this to %s? [Y/n]' % opts.upload - while True: - answer = raw_input(prompt).strip().lower() - if answer in ('', 'y', 'yes'): - check_call(['dput', - opts.upload, - '%s_%s_source.changes' % - (package, bp_version)], - cwd=workdir) + if opts.build: + do_build(workdir, package, release, bp_version, opts) + if opts.upload: + do_upload(workdir, package, bp_version, opts) - break - elif answer in ('n', 'no'): - break - shutil.rmtree(srcdir) + shutil.rmtree(srcdir) def main(args): global lp diff --git a/doc/backportpackage.1 b/doc/backportpackage.1 index 0234a91..341beaf 100644 --- a/doc/backportpackage.1 +++ b/doc/backportpackage.1 @@ -13,22 +13,32 @@ backportpackage \- helper to test package backports .B backportpackage \-h .SH OPTIONS .TP -.BR \-u \fIUPLOAD\fR, \-\-upload=\fIUPLOAD\fR -\fBRequired\fR. Upload to \fIUPLOAD\fR with \fBdput\fR(1) (after -confirmation). -.TP -.BR \-t \fIDEST\fR, \-\-to=\fIDEST\fR +.B \-t \fIDEST\fR, \-\-to=\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. .TP -.BR \-f \fISOURCE\fR, \-\-from=\fISOURCE\fR +.B \-f \fISOURCE\fR, \-\-from=\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 release. .TP -.BR \-v \fIVERSION\fR, \-\-version=\fIVERSION\fR +.B \-b, \-\-build +Build the package with the specified builder before uploading. Note +for \fBpbuilder\fR(8) users: This assumes the common configuration, +where the \fBDIST\fR environment is read by \fBpbuilderrc\fR(5) to +select the correct base image. +.TP +.B \-B \fIBUILDER\fR, \fB\-\-builder\fR=\fIBUILDER +Use the specified builder to build the package. Supported are +\fBpbuilder\fR(8) and \fBsbuild\fR(1). This overrides +\fBUBUNTUTOOLS_BUILDER\fR. The default is \fBpbuilder\fR(8). +.TP +.B \-u \fIUPLOAD\fR, \-\-upload=\fIUPLOAD\fR +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, @@ -36,19 +46,27 @@ verifies that the current version of \fIsource package\fR in package\fR, regardless of the release in which it was published (or if that version is still current). .TP -.BR \-l \fIINSTANCE\fR, \-\-launchpad=\fIINSTANCE\fR +.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, -uploading the resulting backport for testing. +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. .PP \fBbackportpackage\fR is only recommended for testing backports in a PPA, not uploading backports to the Ubuntu archive. +.SH ENVIRONMENT +.TP +.B UBUNTUTOOLS_BUILDER +The default builder for Ubuntu development tools that support it +(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 AUTHOR \fBbackportpackage\fR and this manpage were written by Evan Broder From 3ae9fea935efcfe5b8304391673927dc481075ec Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Sun, 12 Dec 2010 20:19:37 -0800 Subject: [PATCH 21/32] backportpackage: Minor cleanup identified by pylint --- backportpackage | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/backportpackage b/backportpackage index 1703c6e..27eebed 100755 --- a/backportpackage +++ b/backportpackage @@ -88,7 +88,7 @@ def parse(args): return opts, args -def find_release_package(workdir, package, opts): +def find_release_package(package, opts): ubuntu = lp.distributions['ubuntu'] archive = ubuntu.main_archive series = ubuntu.getSeries(name_or_version=opts.source_release) @@ -113,7 +113,7 @@ def find_release_package(workdir, package, opts): return srcpkg -def find_version_package(workdir, package, opts): +def find_version_package(package, opts): ubuntu = lp.distributions['ubuntu'] archive = ubuntu.main_archive try: @@ -127,7 +127,6 @@ def find_version_package(workdir, package, opts): def fetch_package(workdir, package, 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 @@ -135,9 +134,9 @@ def fetch_package(workdir, package, opts): # If source_release is specified, then version is just for # verification if opts.source_release: - srcpkg = find_release_package(workdir, package, opts) + srcpkg = find_release_package(package, opts) else: - srcpkg = find_version_package(workdir, package, opts) + srcpkg = find_version_package(package, opts) for f in srcpkg.sourceFileUrls(): if f.endswith('.dsc'): @@ -206,7 +205,7 @@ def do_backport(workdir, package, dscfile, release, opts): '--force-bad-version', '--preserve', '--newversion', bp_version, - '--distribution', release, + '--distribution', bp_dist, 'No-change backport to %s' % release], cwd=srcdir) check_call(['debuild', '-S', '-sa'], From ee157d1cd95e4791d81aafa2f97bc7b8983a1581 Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Sun, 12 Dec 2010 20:20:40 -0800 Subject: [PATCH 22/32] ubuntutools.builder: Small fixup spotted by pylint --- ubuntutools/builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubuntutools/builder.py b/ubuntutools/builder.py index 9b66a66..e15a534 100644 --- a/ubuntutools/builder.py +++ b/ubuntutools/builder.py @@ -78,4 +78,4 @@ def getBuilder(builder=None): return Sbuild() Logger.error("Unsupported builder specified: %s. Only pbuilder and " - "sbuild are supported." % (options.builder)) + "sbuild are supported." % builder) From 899139ba9907288eb4550ca850e8211ed0315bb5 Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Wed, 15 Dec 2010 11:00:02 -0800 Subject: [PATCH 23/32] backportpackage: Unquote URLs we get back from LP --- backportpackage | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backportpackage b/backportpackage index 27eebed..6732f93 100755 --- a/backportpackage +++ b/backportpackage @@ -21,6 +21,7 @@ import shutil import subprocess import sys import tempfile +import urllib from debian.deb822 import Dsc import launchpadlib.launchpad @@ -143,7 +144,7 @@ def fetch_package(workdir, package, opts): check_call(['dget', '--download-only', '--allow-unauthenticated', - f], + urllib.unquote(f)], cwd=workdir) return os.path.join(workdir, os.path.basename(f)) else: From a8cea3d3392c12251a3994c0ca0cef5158f2333c Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Wed, 15 Dec 2010 11:02:07 -0800 Subject: [PATCH 24/32] backportpackage: Abort if build fails --- backportpackage | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/backportpackage b/backportpackage index 6732f93..ed2ed58 100755 --- a/backportpackage +++ b/backportpackage @@ -167,10 +167,10 @@ def do_build(workdir, package, release, bp_version, opts): if not builder: return - 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 @@ -217,7 +217,8 @@ def do_backport(workdir, package, dscfile, release, opts): print 'Please check the package in file://%s carefully' % workdir if opts.build: - do_build(workdir, package, release, bp_version, opts) + if 0 != do_build(workdir, package, release, bp_version, opts): + error('Package failed to build; aborting') if opts.upload: do_upload(workdir, package, bp_version, opts) From d116ced2cafbbcb0b15fa89f56c875aee88feab4 Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Wed, 15 Dec 2010 20:16:47 -0800 Subject: [PATCH 25/32] backportpackage: Error if neither -b nor -u is specified --- backportpackage | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backportpackage b/backportpackage index ed2ed58..f53eacf 100755 --- a/backportpackage +++ b/backportpackage @@ -86,6 +86,8 @@ def parse(args): p.error('You must specify a source package') if not opts.dest_releases: p.error('You must specify at least one destination release') + if not opts.upload or opts.build: + p.error('Nothing to do') return opts, args From 2b37f9bc620133ca5a8e6ed304ae4c0176445ec8 Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Wed, 15 Dec 2010 20:19:28 -0800 Subject: [PATCH 26/32] backportpackage: --source/--destination instead of --from/--to --- backportpackage | 4 ++-- doc/backportpackage.1 | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/backportpackage b/backportpackage index f53eacf..2c981b9 100755 --- a/backportpackage +++ b/backportpackage @@ -45,13 +45,13 @@ def check_call(cmd, *args, **kwargs): def parse(args): usage = 'Usage: %prog [options] ' 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)', 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)', diff --git a/doc/backportpackage.1 b/doc/backportpackage.1 index 341beaf..996dc7f 100644 --- a/doc/backportpackage.1 +++ b/doc/backportpackage.1 @@ -4,7 +4,7 @@ backportpackage \- helper to test package backports .SH SYNOPSIS .TP .B backportpackage \fR[\fIadditional options\fR] -\-\-to <\fIdest release\fR> +\-\-destination <\fIdest release\fR> .br \-\-upload <\fIupload target\fR> .br @@ -13,12 +13,12 @@ backportpackage \- helper to test package backports .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. .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 @@ -39,9 +39,9 @@ Use the specified builder to build the package. Supported are 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). From fb0712859c347c5e62a8db8b219ce1d97011f1ab Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Wed, 15 Dec 2010 20:34:58 -0800 Subject: [PATCH 27/32] backportpackage: Eliminate global variables --- backportpackage | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/backportpackage b/backportpackage index 2c981b9..15140a2 100755 --- a/backportpackage +++ b/backportpackage @@ -29,9 +29,6 @@ import launchpadlib.launchpad 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) @@ -86,12 +83,12 @@ def parse(args): p.error('You must specify a source package') if not opts.dest_releases: p.error('You must specify at least one destination release') - if not opts.upload or opts.build: + 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, opts): ubuntu = lp.distributions['ubuntu'] archive = ubuntu.main_archive series = ubuntu.getSeries(name_or_version=opts.source_release) @@ -116,7 +113,7 @@ def find_release_package(package, opts): return srcpkg -def find_version_package(package, opts): +def find_version_package(lp, package, opts): ubuntu = lp.distributions['ubuntu'] archive = ubuntu.main_archive try: @@ -128,7 +125,7 @@ def find_version_package(package, opts): error('Package %s was never published with version %s in Ubuntu' % (package, opts.version)) -def fetch_package(workdir, package, opts): +def fetch_package(lp, workdir, package, opts): # Returns the path to the .dsc file that was fetched if not opts.source_release and not opts.version: @@ -137,9 +134,9 @@ def fetch_package(workdir, package, opts): # If source_release is specified, then version is just for # verification if opts.source_release: - srcpkg = find_release_package(package, opts) + srcpkg = find_release_package(lp, package, opts) else: - srcpkg = find_version_package(package, opts) + srcpkg = find_version_package(lp, package, opts) for f in srcpkg.sourceFileUrls(): if f.endswith('.dsc'): @@ -227,8 +224,6 @@ def do_backport(workdir, package, dscfile, release, opts): shutil.rmtree(srcdir) def main(args): - global lp - os.environ['DEB_VENDOR'] = 'Ubuntu' opts, (package,) = parse(args[1:]) @@ -239,7 +234,7 @@ def main(args): workdir = tempfile.mkdtemp(prefix='backportpackage-') try: - dscfile = fetch_package(workdir, package, opts) + dscfile = fetch_package(lp, workdir, package, opts) for release in opts.dest_releases: do_backport(workdir, package, dscfile, release, opts) From 533b2bf7bd8dbe72cb696e623eae1c0d2809199c Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Wed, 15 Dec 2010 20:39:50 -0800 Subject: [PATCH 28/32] backportpackage: Pass around options instead of options container --- backportpackage | 68 ++++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/backportpackage b/backportpackage index 15140a2..83108f3 100755 --- a/backportpackage +++ b/backportpackage @@ -88,10 +88,10 @@ def parse(args): return opts, args -def find_release_package(lp, 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: @@ -105,38 +105,38 @@ def find_release_package(lp, 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(lp, 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(lp, workdir, package, opts): +def fetch_package(lp, workdir, package, version, source_release): # 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 + 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(lp, package, opts) + if source_release: + srcpkg = find_release_package(lp, package, version, source_release) else: - srcpkg = find_version_package(lp, package, opts) + srcpkg = find_version_package(lp, package, version) for f in srcpkg.sourceFileUrls(): if f.endswith('.dsc'): @@ -161,8 +161,8 @@ 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 @@ -171,8 +171,8 @@ def do_build(workdir, package, release, bp_version, opts): 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'): @@ -181,13 +181,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): +def do_backport(workdir, package, dscfile, release, build, builder, upload): dsc = Dsc(open(os.path.join(workdir, dscfile))) v = dsc['Version'] @@ -198,8 +198,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(v, upload, release) + bp_dist = get_backport_dist(upload, release) check_call(['dch', '--force-bad-version', @@ -215,11 +215,11 @@ 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: - if 0 != do_build(workdir, package, release, bp_version, opts): + if build: + if 0 != do_build(workdir, package, release, bp_version, builder): error('Package failed to build; aborting') - if opts.upload: - do_upload(workdir, package, bp_version, opts) + if upload: + do_upload(workdir, package, bp_version, upload) shutil.rmtree(srcdir) @@ -234,10 +234,20 @@ def main(args): workdir = tempfile.mkdtemp(prefix='backportpackage-') try: - dscfile = fetch_package(lp, workdir, package, opts) + dscfile = fetch_package(lp, + workdir, + package, + opts.version, + opts.source_release) for release in opts.dest_releases: - do_backport(workdir, package, dscfile, release, opts) + do_backport(workdir, + package, + dscfile, + release, + opts.build, + opts.builder, + opts.upload) finally: shutil.rmtree(workdir) From 82373b169d7fe6551d2941fadc918a8410034009 Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Wed, 15 Dec 2010 20:50:30 -0800 Subject: [PATCH 29/32] backportpackage: If no dest release is specified, default to the current release --- backportpackage | 12 +++++++++--- doc/backportpackage.1 | 6 ++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/backportpackage b/backportpackage index 83108f3..85e5b82 100755 --- a/backportpackage +++ b/backportpackage @@ -25,6 +25,7 @@ import urllib from debian.deb822 import Dsc import launchpadlib.launchpad +import lsb_release from ubuntutools.builder import getBuilder from ubuntutools.logger import Logger @@ -46,7 +47,7 @@ def parse(args): 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('-s', '--source', dest='source_release', @@ -81,8 +82,6 @@ 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') if not opts.upload and not opts.build: p.error('Nothing to do') @@ -232,6 +231,13 @@ def main(args): lp = launchpadlib.launchpad.Launchpad.login_anonymously(script_name, opts.launchpad) + 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') + workdir = tempfile.mkdtemp(prefix='backportpackage-') try: dscfile = fetch_package(lp, diff --git a/doc/backportpackage.1 b/doc/backportpackage.1 index 996dc7f..6400af1 100644 --- a/doc/backportpackage.1 +++ b/doc/backportpackage.1 @@ -4,8 +4,6 @@ backportpackage \- helper to test package backports .SH SYNOPSIS .TP .B backportpackage \fR[\fIadditional options\fR] -\-\-destination <\fIdest release\fR> -.br \-\-upload <\fIupload target\fR> .br <\fIsource package\fR> @@ -15,8 +13,8 @@ backportpackage \- helper to test package backports .TP .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 \-s \fISOURCE\fR, \-\-source=\fISOURCE\fR Backport the package from the specified Ubuntu release. If neither From 44f2b31a98023c37cf67be20b322efd8592762b8 Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Wed, 15 Dec 2010 21:18:48 -0800 Subject: [PATCH 30/32] doc/backportpackage.1: Add an EXAMPLES section --- doc/backportpackage.1 | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/backportpackage.1 b/doc/backportpackage.1 index 6400af1..c81c338 100644 --- a/doc/backportpackage.1 +++ b/doc/backportpackage.1 @@ -65,6 +65,20 @@ The default builder for Ubuntu development tools that support it (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: +.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: +.IP +.nf +.B backportpackage -b -s maverick -d karmic -d lucid squashfs-tools +.fi .SH AUTHOR \fBbackportpackage\fR and this manpage were written by Evan Broder From 2585452cbbc7ed7694d445d3699d0f4bb541717c Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Thu, 16 Dec 2010 00:06:57 -0800 Subject: [PATCH 31/32] backportpackage: Allow specifying a working directory --- backportpackage | 17 +++++++++++++++-- doc/backportpackage.1 | 14 +++++++++++--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/backportpackage b/backportpackage index 85e5b82..042481f 100755 --- a/backportpackage +++ b/backportpackage @@ -73,6 +73,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', @@ -238,7 +243,14 @@ def main(args): except: error('No destination release specified and unable to guess yours') - workdir = tempfile.mkdtemp(prefix='backportpackage-') + 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(lp, workdir, @@ -255,7 +267,8 @@ def main(args): opts.builder, opts.upload) finally: - shutil.rmtree(workdir) + if not opts.workdir: + shutil.rmtree(workdir) if __name__ == '__main__': sys.exit(main(sys.argv)) diff --git a/doc/backportpackage.1 b/doc/backportpackage.1 index c81c338..bae8c13 100644 --- a/doc/backportpackage.1 +++ b/doc/backportpackage.1 @@ -44,6 +44,12 @@ package\fR in \fISOURCE\fR is the same as \fIVERSION\fR. Otherwise, package\fR, regardless of the release in which it was published (or if that version is still current). .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". @@ -67,17 +73,19 @@ The default builder for Ubuntu development tools that support it \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: +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: +test-build both locally, leaving all build products in the current +working directory: .IP .nf -.B backportpackage -b -s maverick -d karmic -d lucid squashfs-tools +.B backportpackage -b -s maverick -d karmic -d lucid -w . squashfs-tools .fi .SH AUTHOR \fBbackportpackage\fR and this manpage were written by Evan Broder From b50fbb7750487b2d3c54a197c87e4419f0a66f00 Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Thu, 16 Dec 2010 01:05:29 -0800 Subject: [PATCH 32/32] backportpackage: Accept a URL or path to a .dsc file as an alternative to a source package name --- backportpackage | 55 +++++++++++++++++++++++++++++-------------- doc/backportpackage.1 | 31 ++++++++++++++++-------- 2 files changed, 58 insertions(+), 28 deletions(-) diff --git a/backportpackage b/backportpackage index 042481f..e31fb67 100755 --- a/backportpackage +++ b/backportpackage @@ -41,7 +41,7 @@ def check_call(cmd, *args, **kwargs): error('%s returned %d' % (cmd, ret)) def parse(args): - usage = 'Usage: %prog [options] ' + usage = 'Usage: %prog [options] ' p = optparse.OptionParser(usage) p.add_option('-d', '--destination', dest='dest_releases', @@ -86,7 +86,7 @@ def parse(args): opts, args = p.parse_args(args) if len(args) != 1: - p.error('You must specify a source package') + 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') @@ -129,9 +129,7 @@ def find_version_package(lp, package, version): error('Version %s of package %s was never published in Ubuntu' % (version, package)) -def fetch_package(lp, workdir, package, version, source_release): - # Returns the path to the .dsc file that was fetched - +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 @@ -144,15 +142,34 @@ def fetch_package(lp, workdir, package, version, source_release): for f in srcpkg.sourceFileUrls(): if f.endswith('.dsc'): - check_call(['dget', - '--download-only', - '--allow-unauthenticated', - urllib.unquote(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:'): @@ -191,10 +208,7 @@ def do_upload(workdir, package, bp_version, upload): cwd=workdir) -def do_backport(workdir, package, dscfile, release, build, builder, upload): - 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, @@ -202,7 +216,7 @@ def do_backport(workdir, package, dscfile, release, build, builder, upload): cwd=workdir) srcdir = os.path.join(workdir, package) - bp_version = get_backport_version(v, upload, release) + bp_version = get_backport_version(version, upload, release) bp_dist = get_backport_dist(upload, release) check_call(['dch', @@ -230,7 +244,7 @@ def do_backport(workdir, package, dscfile, release, build, builder, upload): def main(args): 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, @@ -254,14 +268,19 @@ def main(args): try: dscfile = fetch_package(lp, workdir, - package, + 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, + version, release, opts.build, opts.builder, diff --git a/doc/backportpackage.1 b/doc/backportpackage.1 index bae8c13..8d30e88 100644 --- a/doc/backportpackage.1 +++ b/doc/backportpackage.1 @@ -6,7 +6,7 @@ backportpackage \- helper to test package backports .B backportpackage \fR[\fIadditional options\fR] \-\-upload <\fIupload target\fR> .br -<\fIsource package\fR> +<\fIsource package name or .dsc URL/file\fR> .PP .B backportpackage \-h .SH OPTIONS @@ -42,7 +42,8 @@ If the \fB\-\-source\fR option is specified, then 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, @@ -54,13 +55,14 @@ deleted before \fIbackportpackage\fR exits. 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. @@ -68,7 +70,7 @@ 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 @@ -85,7 +87,16 @@ 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 . squashfs-tools +.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