mirror of
				https://git.launchpad.net/ubuntu-dev-tools
				synced 2025-11-04 07:54:03 +00:00 
			
		
		
		
	backportpackage: new script for testing backport requests in a PPA.
This commit is contained in:
		
						commit
						8eda0c4326
					
				
							
								
								
									
										280
									
								
								backportpackage
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										280
									
								
								backportpackage
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,280 @@
 | 
			
		||||
#!/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 optparse
 | 
			
		||||
import os
 | 
			
		||||
import shutil
 | 
			
		||||
import subprocess
 | 
			
		||||
import sys
 | 
			
		||||
import tempfile
 | 
			
		||||
import urllib
 | 
			
		||||
 | 
			
		||||
from debian.deb822 import Dsc
 | 
			
		||||
import launchpadlib.launchpad
 | 
			
		||||
import lsb_release
 | 
			
		||||
 | 
			
		||||
from ubuntutools.builder import getBuilder
 | 
			
		||||
from ubuntutools.logger import Logger
 | 
			
		||||
 | 
			
		||||
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))
 | 
			
		||||
 | 
			
		||||
def parse(args):
 | 
			
		||||
    usage = 'Usage: %prog [options] <source package name or .dsc URL/file>'
 | 
			
		||||
    p = optparse.OptionParser(usage)
 | 
			
		||||
    p.add_option('-d', '--destination',
 | 
			
		||||
                 dest='dest_releases',
 | 
			
		||||
                 default=[],
 | 
			
		||||
                 action='append',
 | 
			
		||||
                 help='Backport to DEST release (default: current release)',
 | 
			
		||||
                 metavar='DEST')
 | 
			
		||||
    p.add_option('-s', '--source',
 | 
			
		||||
                 dest='source_release',
 | 
			
		||||
                 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,
 | 
			
		||||
                 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',
 | 
			
		||||
                 help='Launchpad instance to connect to (default: %default)',
 | 
			
		||||
                 metavar='INSTANCE')
 | 
			
		||||
 | 
			
		||||
    opts, args = p.parse_args(args)
 | 
			
		||||
    if len(args) != 1:
 | 
			
		||||
        p.error('You must specify a single source package or a .dsc URL/path')
 | 
			
		||||
    if not opts.upload and not opts.build:
 | 
			
		||||
        p.error('Nothing to do')
 | 
			
		||||
 | 
			
		||||
    return opts, args
 | 
			
		||||
 | 
			
		||||
def find_release_package(lp, package, version, source_release):
 | 
			
		||||
    ubuntu = lp.distributions['ubuntu']
 | 
			
		||||
    archive = ubuntu.main_archive
 | 
			
		||||
    series = ubuntu.getSeries(name_or_version=source_release)
 | 
			
		||||
    status = 'Published'
 | 
			
		||||
    for pocket in ('Updates', 'Security', 'Release'):
 | 
			
		||||
        try:
 | 
			
		||||
            srcpkg = archive.getPublishedSources(source_name=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, source_release))
 | 
			
		||||
 | 
			
		||||
    if version and version != srcpkg.source_package_version:
 | 
			
		||||
        error('Requested backport of version %s but %s is at version %s' %
 | 
			
		||||
              (version, package, srcpkg.source_package_version))
 | 
			
		||||
 | 
			
		||||
    return srcpkg
 | 
			
		||||
 | 
			
		||||
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=version)[0]
 | 
			
		||||
    except IndexError:
 | 
			
		||||
        error('Version %s of package %s was never published in Ubuntu' %
 | 
			
		||||
              (version, package))
 | 
			
		||||
 | 
			
		||||
def dscurl_from_package(lp, workdir, package, version, source_release):
 | 
			
		||||
    if not source_release and not version:
 | 
			
		||||
        source_release = lp.distributions['ubuntu'].current_series.name
 | 
			
		||||
 | 
			
		||||
    # If source_release is specified, then version is just for verification
 | 
			
		||||
    if source_release:
 | 
			
		||||
        srcpkg = find_release_package(lp, package, version, source_release)
 | 
			
		||||
    else:
 | 
			
		||||
        srcpkg = find_version_package(lp, package, version)
 | 
			
		||||
 | 
			
		||||
    for f in srcpkg.sourceFileUrls():
 | 
			
		||||
        if f.endswith('.dsc'):
 | 
			
		||||
            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:'):
 | 
			
		||||
        v += '~ppa1'
 | 
			
		||||
    return v
 | 
			
		||||
 | 
			
		||||
def get_backport_dist(upload, release):
 | 
			
		||||
    if not upload or upload == 'ubuntu':
 | 
			
		||||
        return '%s-backports' % release
 | 
			
		||||
    else:
 | 
			
		||||
        return release
 | 
			
		||||
 | 
			
		||||
def do_build(workdir, package, release, bp_version, builder):
 | 
			
		||||
    builder = getBuilder(builder)
 | 
			
		||||
    if not builder:
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    return builder.build(os.path.join(workdir,
 | 
			
		||||
                                      '%s_%s.dsc' % (package, bp_version)),
 | 
			
		||||
                         release,
 | 
			
		||||
                         workdir)
 | 
			
		||||
 | 
			
		||||
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'):
 | 
			
		||||
            break
 | 
			
		||||
        elif answer in ('n', 'no'):
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
    check_call(['dput', upload, '%s_%s_source.changes' % (package, bp_version)],
 | 
			
		||||
               cwd=workdir)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def do_backport(workdir, package, dscfile, version, release, build, builder, upload):
 | 
			
		||||
    check_call(['dpkg-source', '-x', dscfile, package], cwd=workdir)
 | 
			
		||||
    srcdir = os.path.join(workdir, package)
 | 
			
		||||
 | 
			
		||||
    bp_version = get_backport_version(version, upload, release)
 | 
			
		||||
    bp_dist = get_backport_dist(upload, release)
 | 
			
		||||
 | 
			
		||||
    check_call(['dch',
 | 
			
		||||
                '--force-bad-version',
 | 
			
		||||
                '--preserve',
 | 
			
		||||
                '--newversion', bp_version,
 | 
			
		||||
                '--distribution', bp_dist,
 | 
			
		||||
                '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
 | 
			
		||||
    if build:
 | 
			
		||||
        if 0 != do_build(workdir, package, release, bp_version, builder):
 | 
			
		||||
            error('Package failed to build; aborting')
 | 
			
		||||
    if upload:
 | 
			
		||||
        do_upload(workdir, package, bp_version, upload)
 | 
			
		||||
 | 
			
		||||
    shutil.rmtree(srcdir)
 | 
			
		||||
 | 
			
		||||
def main(args):
 | 
			
		||||
    os.environ['DEB_VENDOR'] = 'Ubuntu'
 | 
			
		||||
 | 
			
		||||
    opts, (package_or_dsc,) = parse(args[1:])
 | 
			
		||||
 | 
			
		||||
    script_name = os.path.basename(sys.argv[0])
 | 
			
		||||
    lp = launchpadlib.launchpad.Launchpad.login_anonymously(script_name,
 | 
			
		||||
                                                            opts.launchpad)
 | 
			
		||||
 | 
			
		||||
    if not opts.dest_releases:
 | 
			
		||||
        try:
 | 
			
		||||
            distinfo = lsb_release.get_distro_information()
 | 
			
		||||
            opts.dest_releases = [distinfo['CODENAME']]
 | 
			
		||||
        except:
 | 
			
		||||
            error('No destination release specified and unable to guess yours')
 | 
			
		||||
 | 
			
		||||
    if opts.workdir:
 | 
			
		||||
        workdir = os.path.expanduser(opts.workdir)
 | 
			
		||||
    else:
 | 
			
		||||
        workdir = tempfile.mkdtemp(prefix='backportpackage-')
 | 
			
		||||
 | 
			
		||||
    if not os.path.exists(workdir):
 | 
			
		||||
        os.makedirs(workdir)
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        dscfile = fetch_package(lp,
 | 
			
		||||
                                workdir,
 | 
			
		||||
                                package_or_dsc,
 | 
			
		||||
                                opts.version,
 | 
			
		||||
                                opts.source_release)
 | 
			
		||||
 | 
			
		||||
        dsc = Dsc(open(os.path.join(workdir, dscfile)))
 | 
			
		||||
        package = dsc['Source']
 | 
			
		||||
        version = dsc['Version']
 | 
			
		||||
 | 
			
		||||
        for release in opts.dest_releases:
 | 
			
		||||
            do_backport(workdir,
 | 
			
		||||
                        package,
 | 
			
		||||
                        dscfile,
 | 
			
		||||
                        version,
 | 
			
		||||
                        release,
 | 
			
		||||
                        opts.build,
 | 
			
		||||
                        opts.builder,
 | 
			
		||||
                        opts.upload)
 | 
			
		||||
    finally:
 | 
			
		||||
        if not opts.workdir:
 | 
			
		||||
            shutil.rmtree(workdir)
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    sys.exit(main(sys.argv))
 | 
			
		||||
							
								
								
									
										5
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@ -12,7 +12,10 @@ ubuntu-dev-tools (0.108) UNRELEASED; urgency=low
 | 
			
		||||
  [ Colin Watson ]
 | 
			
		||||
  * grep-merges: New tool.
 | 
			
		||||
 | 
			
		||||
 -- Benjamin Drung <bdrung@ubuntu.com>  Tue, 14 Dec 2010 18:21:37 +0100
 | 
			
		||||
  [ Evan Broder ]
 | 
			
		||||
  * backportpackage: new script for testing backport requests in a PPA.
 | 
			
		||||
 | 
			
		||||
 -- Benjamin Drung <bdrung@ubuntu.com>  Thu, 16 Dec 2010 23:40:14 +0100
 | 
			
		||||
 | 
			
		||||
ubuntu-dev-tools (0.107) experimental; urgency=low
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										105
									
								
								doc/backportpackage.1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								doc/backportpackage.1
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,105 @@
 | 
			
		||||
.TH BACKPORTPACKAGE "1" "December 2010" "ubuntu-dev-tools"
 | 
			
		||||
.SH NAME
 | 
			
		||||
backportpackage \- helper to test package backports
 | 
			
		||||
.SH SYNOPSIS
 | 
			
		||||
.TP
 | 
			
		||||
.B backportpackage \fR[\fIadditional options\fR]
 | 
			
		||||
\-\-upload <\fIupload target\fR>
 | 
			
		||||
.br
 | 
			
		||||
<\fIsource package name or .dsc URL/file\fR>
 | 
			
		||||
.PP
 | 
			
		||||
.B backportpackage \-h
 | 
			
		||||
.SH OPTIONS
 | 
			
		||||
.TP
 | 
			
		||||
.B \-d \fIDEST\fR, \-\-destination=\fIDEST\fR
 | 
			
		||||
\fBRequired\fR. Backport the package to the specified Ubuntu
 | 
			
		||||
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
 | 
			
		||||
this option nor \fB\-\-version\fR are specified, then
 | 
			
		||||
\fBbackportpackage\fR defaults to the current Ubuntu development
 | 
			
		||||
release.
 | 
			
		||||
.TP
 | 
			
		||||
.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\-\-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). This option is ignored if a .dsc URL
 | 
			
		||||
or path is passed in instead of a source package name.
 | 
			
		||||
.TP
 | 
			
		||||
.B \-w \fIWORKDIR\fR, \-\-workdir=\fIWORKDIR\fR
 | 
			
		||||
If \fIWORKDIR\fR is specified, then all files are downloaded,
 | 
			
		||||
unpacked, built into, and otherwise manipulated in
 | 
			
		||||
\fIWORKDIR\fR. Otherwise, a temporary directory is created, which is
 | 
			
		||||
deleted before \fIbackportpackage\fR exits.
 | 
			
		||||
.TP
 | 
			
		||||
.B \-l \fIINSTANCE\fR, \-\-launchpad=\fIINSTANCE\fR
 | 
			
		||||
Use the specified instance of Launchpad (e.g. "staging"), instead of
 | 
			
		||||
the default of "production".
 | 
			
		||||
.SH DESCRIPTION
 | 
			
		||||
\fBbackportpackage\fR fetches a package from one Ubuntu release 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
 | 
			
		||||
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.
 | 
			
		||||
.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 EXAMPLES
 | 
			
		||||
Test-build in your PPA a backport of znc from the current development
 | 
			
		||||
release to your workstation's release, deleting the build products
 | 
			
		||||
afterwards:
 | 
			
		||||
.IP
 | 
			
		||||
.nf
 | 
			
		||||
.B backportpackage -u ppa:\fIuser\fR/\fIppa\fB znc
 | 
			
		||||
.fi
 | 
			
		||||
.PP
 | 
			
		||||
Backport squashfs-tools from Maverick to both Karmic and Lucid and
 | 
			
		||||
test-build both locally, leaving all build products in the current
 | 
			
		||||
working directory:
 | 
			
		||||
.IP
 | 
			
		||||
.nf
 | 
			
		||||
.B backportpackage -b -s maverick -d karmic -d lucid -w . \\\\
 | 
			
		||||
.B "  "squashfs-tools
 | 
			
		||||
.fi
 | 
			
		||||
.PP
 | 
			
		||||
Fetch a package from a PPA, backport it to Hardy, then upload it back
 | 
			
		||||
to the same PPA:
 | 
			
		||||
.IP
 | 
			
		||||
.nf
 | 
			
		||||
.B backportpackage -d hardy -u ppa:\fIuser\fR/\fIppa\fR \\\\
 | 
			
		||||
.B "  "https://launchpad.net/\fIsome/file.dsc\fR
 | 
			
		||||
.fi
 | 
			
		||||
.SH AUTHOR
 | 
			
		||||
\fBbackportpackage\fR and this manpage were written by Evan Broder
 | 
			
		||||
<evan@ebroder.net>
 | 
			
		||||
.PP
 | 
			
		||||
Both are released under GNU General Public License, version 2.
 | 
			
		||||
@ -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,17 @@ 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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								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',
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										204
									
								
								sponsor-patch
									
									
									
									
									
								
							
							
						
						
									
										204
									
								
								sponsor-patch
									
									
									
									
									
								
							@ -28,7 +28,9 @@ import debian.deb822
 | 
			
		||||
import debian.debian_support
 | 
			
		||||
import launchpadlib.launchpad
 | 
			
		||||
 | 
			
		||||
from ubuntutools.builder import getBuilder
 | 
			
		||||
import ubuntutools.update_maintainer
 | 
			
		||||
from ubuntutools.logger import Logger
 | 
			
		||||
 | 
			
		||||
USER_ABORT = 2
 | 
			
		||||
 | 
			
		||||
@ -57,7 +59,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
 | 
			
		||||
@ -148,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]
 | 
			
		||||
        Print.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()
 | 
			
		||||
        Print.command(["cd", result_directory])
 | 
			
		||||
        os.chdir(result_directory)
 | 
			
		||||
        cmd = ["sbuild", "--arch-all", "--dist=" + dist,
 | 
			
		||||
               "--arch=" + self.architecture, dsc_file]
 | 
			
		||||
        Print.command(cmd)
 | 
			
		||||
        result = subprocess.call(cmd)
 | 
			
		||||
        Print.command(["cd", workdir])
 | 
			
		||||
        os.chdir(workdir)
 | 
			
		||||
        return result
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Patch(object):
 | 
			
		||||
    def __init__(self, patch_file):
 | 
			
		||||
        self.patch_file = patch_file
 | 
			
		||||
@ -220,41 +178,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 +257,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 +265,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 +303,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 +316,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 +344,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 +359,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 +379,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 +389,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 +400,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 +416,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 +433,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 +447,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 +455,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 +463,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 +479,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 +491,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 +501,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 +509,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 +538,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 +557,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 +569,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 +583,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 +591,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 +606,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 +622,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 +650,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
 | 
			
		||||
@ -766,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.",
 | 
			
		||||
@ -790,29 +713,24 @@ 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":
 | 
			
		||||
        builder = Pbuilder()
 | 
			
		||||
    elif options.builder == "sbuild":
 | 
			
		||||
        builder = Sbuild()
 | 
			
		||||
    else:
 | 
			
		||||
        Print.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:
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										81
									
								
								ubuntutools/builder.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								ubuntutools/builder.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,81 @@
 | 
			
		||||
#
 | 
			
		||||
#   builder.py - Helper classes for building packages
 | 
			
		||||
#
 | 
			
		||||
#   Copyright (C) 2010, Benjamin Drung <bdrung@ubuntu.com>
 | 
			
		||||
#   Copyright (C) 2010, Evan Broder <evan@ebroder.net>
 | 
			
		||||
#
 | 
			
		||||
#   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." % builder)
 | 
			
		||||
							
								
								
									
										55
									
								
								ubuntutools/logger.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								ubuntutools/logger.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,55 @@
 | 
			
		||||
#
 | 
			
		||||
#   logger.py - A simple logging helper class
 | 
			
		||||
#
 | 
			
		||||
#   Copyright (C) 2010, Benjamin Drung <bdrung@ubuntu.com>
 | 
			
		||||
#
 | 
			
		||||
#   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
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user