mirror of
https://git.launchpad.net/ubuntu-dev-tools
synced 2025-05-19 04:41:28 +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 ]
|
[ Colin Watson ]
|
||||||
* grep-merges: New tool.
|
* 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
|
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
|
.B \-B \fIBUILDER\fR, \fB\-\-builder\fR=\fIBUILDER
|
||||||
Use the specify builder to build the package.
|
Use the specify builder to build the package.
|
||||||
Supported are \fBpbuilder\fR(8) and \fBsbuild\fR(1).
|
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).
|
The default is \fBpbuilder\fR(8).
|
||||||
.TP
|
.TP
|
||||||
.BR \-e ", " \-\-edit
|
.BR \-e ", " \-\-edit
|
||||||
@ -90,11 +90,17 @@ Display a help message and exit.
|
|||||||
.SH ENVIRONMENT
|
.SH ENVIRONMENT
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B SPONSOR_PATCH_BUILDER
|
.B UBUNTUTOOLS_BUILDER
|
||||||
The default builder for \fBsponsor\-patch\fR.
|
The default builder for Ubuntu development tools that support it (including
|
||||||
|
\fBsponsor\-patch\fR).
|
||||||
Supported are \fBpbuilder\fR(8) and \fBsbuild\fR(1).
|
Supported are \fBpbuilder\fR(8) and \fBsbuild\fR(1).
|
||||||
If unset and not provided on the command line, \fBpbuilder\fR(8) is used.
|
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
|
.TP
|
||||||
.B SPONSOR_PATCH_WORKDIR
|
.B SPONSOR_PATCH_WORKDIR
|
||||||
The default working directory for \fBsponsor\-patch\fR. If unset and not
|
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',
|
setup(name='ubuntu-dev-tools',
|
||||||
version=version,
|
version=version,
|
||||||
scripts=['404main',
|
scripts=['404main',
|
||||||
|
'backportpackage',
|
||||||
'check-symbols',
|
'check-symbols',
|
||||||
'dch-repeat',
|
'dch-repeat',
|
||||||
'dgetlp',
|
'dgetlp',
|
||||||
|
204
sponsor-patch
204
sponsor-patch
@ -28,7 +28,9 @@ import debian.deb822
|
|||||||
import debian.debian_support
|
import debian.debian_support
|
||||||
import launchpadlib.launchpad
|
import launchpadlib.launchpad
|
||||||
|
|
||||||
|
from ubuntutools.builder import getBuilder
|
||||||
import ubuntutools.update_maintainer
|
import ubuntutools.update_maintainer
|
||||||
|
from ubuntutools.logger import Logger
|
||||||
|
|
||||||
USER_ABORT = 2
|
USER_ABORT = 2
|
||||||
|
|
||||||
@ -57,7 +59,7 @@ class BugTask(object):
|
|||||||
dsc_file = None
|
dsc_file = None
|
||||||
for url in source_files:
|
for url in source_files:
|
||||||
filename = urllib.unquote(os.path.basename(url))
|
filename = urllib.unquote(os.path.basename(url))
|
||||||
Print.info("Downloading %s..." % (filename))
|
Logger.info("Downloading %s..." % (filename))
|
||||||
urllib.urlretrieve(url, filename)
|
urllib.urlretrieve(url, filename)
|
||||||
if url.endswith(".dsc"):
|
if url.endswith(".dsc"):
|
||||||
dsc_file = filename
|
dsc_file = filename
|
||||||
@ -148,50 +150,6 @@ class BugTask(object):
|
|||||||
return self.project == "ubuntu"
|
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):
|
class Patch(object):
|
||||||
def __init__(self, patch_file):
|
def __init__(self, patch_file):
|
||||||
self.patch_file = patch_file
|
self.patch_file = patch_file
|
||||||
@ -220,41 +178,6 @@ class Patch(object):
|
|||||||
self.changed_files)) > 0
|
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):
|
def get_source_package_name(bug_task):
|
||||||
package = None
|
package = None
|
||||||
if bug_task.bug_target_name != "ubuntu":
|
if bug_task.bug_target_name != "ubuntu":
|
||||||
@ -334,7 +257,7 @@ def yes_edit_no_question(question, default):
|
|||||||
def edit_source():
|
def edit_source():
|
||||||
# Spawn shell to allow modifications
|
# Spawn shell to allow modifications
|
||||||
cmd = [get_user_shell()]
|
cmd = [get_user_shell()]
|
||||||
Print.command(cmd)
|
Logger.command(cmd)
|
||||||
print """An interactive shell was launched in
|
print """An interactive shell was launched in
|
||||||
file://%s
|
file://%s
|
||||||
Edit your files. When you are done, exit the shell. If you wish to abort the
|
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()),
|
""" % (os.getcwd()),
|
||||||
returncode = subprocess.call(cmd)
|
returncode = subprocess.call(cmd)
|
||||||
if returncode != 0:
|
if returncode != 0:
|
||||||
Print.error("Shell exited with exit value %i." % (returncode))
|
Logger.error("Shell exited with exit value %i." % (returncode))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def get_fixed_lauchpad_bugs(changes_file):
|
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)
|
linked_branches = map(lambda b: b.branch, bug.linked_branches)
|
||||||
if len(attached_patches) == 0 and len(linked_branches) == 0:
|
if len(attached_patches) == 0 and len(linked_branches) == 0:
|
||||||
if len(bug.attachments) == 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))
|
% (bug.id))
|
||||||
else:
|
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 " \
|
"to https://launchpad.net/bugs/%i and mark an " \
|
||||||
"attachment as patch.") % (bug.id))
|
"attachment as patch.") % (bug.id))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@ -393,13 +316,13 @@ def get_patch_or_branch(bug):
|
|||||||
branch = linked_branches[0].bzr_identity
|
branch = linked_branches[0].bzr_identity
|
||||||
else:
|
else:
|
||||||
if len(attached_patches) == 0:
|
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)))
|
"linked:" % (bug.id, len(linked_branches)))
|
||||||
elif len(linked_branches) == 0:
|
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)))
|
" attached:" % (bug.id, len(attached_patches)))
|
||||||
else:
|
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:" % \
|
" linked and %i patch(es) attached:" % \
|
||||||
(bug.id, len(linked_branches), len(attached_patches)))
|
(bug.id, len(linked_branches), len(attached_patches)))
|
||||||
i = 0
|
i = 0
|
||||||
@ -421,11 +344,11 @@ def download_patch(patch):
|
|||||||
patch_filename = re.sub(" ", "_", patch.title)
|
patch_filename = re.sub(" ", "_", patch.title)
|
||||||
if not reduce(lambda r, x: r or patch.title.endswith(x),
|
if not reduce(lambda r, x: r or patch.title.endswith(x),
|
||||||
(".debdiff", ".diff", ".patch"), False):
|
(".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.title))
|
||||||
patch_filename += ".patch"
|
patch_filename += ".patch"
|
||||||
|
|
||||||
Print.info("Downloading %s." % (patch_filename))
|
Logger.info("Downloading %s." % (patch_filename))
|
||||||
patch_file = open(patch_filename, "w")
|
patch_file = open(patch_filename, "w")
|
||||||
patch_file.write(patch.data.open().read())
|
patch_file.write(patch.data.open().read())
|
||||||
patch_file.close()
|
patch_file.close()
|
||||||
@ -436,18 +359,18 @@ def download_branch(branch):
|
|||||||
if os.path.isdir(dir_name):
|
if os.path.isdir(dir_name):
|
||||||
shutil.rmtree(dir_name)
|
shutil.rmtree(dir_name)
|
||||||
cmd = ["bzr", "branch", branch]
|
cmd = ["bzr", "branch", branch]
|
||||||
Print.command(cmd)
|
Logger.command(cmd)
|
||||||
if subprocess.call(cmd) != 0:
|
if subprocess.call(cmd) != 0:
|
||||||
Print.error("Failed to download branch %s." % (branch))
|
Logger.error("Failed to download branch %s." % (branch))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
return dir_name
|
return dir_name
|
||||||
|
|
||||||
def merge_branch(branch):
|
def merge_branch(branch):
|
||||||
edit = False
|
edit = False
|
||||||
cmd = ["bzr", "merge", branch]
|
cmd = ["bzr", "merge", branch]
|
||||||
Print.command(cmd)
|
Logger.command(cmd)
|
||||||
if subprocess.call(cmd) != 0:
|
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()
|
ask_for_manual_fixing()
|
||||||
edit = True
|
edit = True
|
||||||
return edit
|
return edit
|
||||||
@ -456,9 +379,9 @@ def extract_source(dsc_file, verbose=False):
|
|||||||
cmd = ["dpkg-source", "--no-preparation", "-x", dsc_file]
|
cmd = ["dpkg-source", "--no-preparation", "-x", dsc_file]
|
||||||
if not verbose:
|
if not verbose:
|
||||||
cmd.insert(1, "-q")
|
cmd.insert(1, "-q")
|
||||||
Print.command(cmd)
|
Logger.command(cmd)
|
||||||
if subprocess.call(cmd) != 0:
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
def apply_patch(task, patch):
|
def apply_patch(task, patch):
|
||||||
@ -466,9 +389,9 @@ def apply_patch(task, patch):
|
|||||||
if patch.is_debdiff():
|
if patch.is_debdiff():
|
||||||
cmd = ["patch", "--merge", "--force", "-p",
|
cmd = ["patch", "--merge", "--force", "-p",
|
||||||
str(patch.get_strip_level()), "-i", patch.full_path]
|
str(patch.get_strip_level()), "-i", patch.full_path]
|
||||||
Print.command(cmd)
|
Logger.command(cmd)
|
||||||
if subprocess.call(cmd) != 0:
|
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()))
|
(patch.get_name(), task.package, task.get_version()))
|
||||||
if not edit:
|
if not edit:
|
||||||
ask_for_manual_fixing()
|
ask_for_manual_fixing()
|
||||||
@ -477,9 +400,9 @@ def apply_patch(task, patch):
|
|||||||
# FIXME: edit-patch needs a non-interactive mode
|
# FIXME: edit-patch needs a non-interactive mode
|
||||||
# https://launchpad.net/bugs/612566
|
# https://launchpad.net/bugs/612566
|
||||||
cmd = ["edit-patch", patch.full_path]
|
cmd = ["edit-patch", patch.full_path]
|
||||||
Print.command(cmd)
|
Logger.command(cmd)
|
||||||
if subprocess.call(cmd) != 0:
|
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()))
|
(patch.get_name(), task.package, task.get_version()))
|
||||||
if not edit:
|
if not edit:
|
||||||
ask_for_manual_fixing()
|
ask_for_manual_fixing()
|
||||||
@ -493,11 +416,11 @@ def main(script_name, bug_number, build, edit, keyid, upload, workdir, builder,
|
|||||||
try:
|
try:
|
||||||
os.makedirs(workdir)
|
os.makedirs(workdir)
|
||||||
except os.error, error:
|
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))
|
"%i]: %s." % (workdir, error.errno, error.strerror))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if workdir != os.getcwd():
|
if workdir != os.getcwd():
|
||||||
Print.command(["cd", workdir])
|
Logger.command(["cd", workdir])
|
||||||
os.chdir(workdir)
|
os.chdir(workdir)
|
||||||
|
|
||||||
script_name = os.path.basename(sys.argv[0])
|
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)
|
bug_tasks = map(lambda x: BugTask(x, launchpad), bug.bug_tasks)
|
||||||
ubuntu_tasks = filter(lambda x: x.is_ubuntu_task(), bug_tasks)
|
ubuntu_tasks = filter(lambda x: x.is_ubuntu_task(), bug_tasks)
|
||||||
if len(ubuntu_tasks) == 0:
|
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)
|
sys.exit(1)
|
||||||
elif len(ubuntu_tasks) == 1:
|
elif len(ubuntu_tasks) == 1:
|
||||||
task = ubuntu_tasks[0]
|
task = ubuntu_tasks[0]
|
||||||
if len(ubuntu_tasks) > 1:
|
if len(ubuntu_tasks) > 1:
|
||||||
if verbose:
|
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))
|
(len(ubuntu_tasks), bug_number))
|
||||||
for task in ubuntu_tasks:
|
for task in ubuntu_tasks:
|
||||||
print task.get_short_info()
|
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:
|
if len(open_ubuntu_tasks) == 1:
|
||||||
task = open_ubuntu_tasks[0]
|
task = open_ubuntu_tasks[0]
|
||||||
else:
|
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)))
|
% (bug_number, len(ubuntu_tasks)))
|
||||||
for i in xrange(len(ubuntu_tasks)):
|
for i in xrange(len(ubuntu_tasks)):
|
||||||
print "%i) %s" % (i + 1,
|
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",
|
selected = input_number("To which Ubuntu tasks do the patch belong",
|
||||||
1, len(ubuntu_tasks))
|
1, len(ubuntu_tasks))
|
||||||
task = ubuntu_tasks[selected - 1]
|
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()
|
dsc_file = task.download_source()
|
||||||
assert os.path.isfile(dsc_file), "%s does not exist." % (dsc_file)
|
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:
|
if patch:
|
||||||
patch = download_patch(patch)
|
patch = download_patch(patch)
|
||||||
|
|
||||||
Print.info("Ubuntu package: %s" % (task.package))
|
Logger.info("Ubuntu package: %s" % (task.package))
|
||||||
if task.is_merge():
|
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)
|
extract_source(dsc_file, verbose)
|
||||||
|
|
||||||
# change directory
|
# change directory
|
||||||
directory = task.package + '-' + task.get_version().upstream_version
|
directory = task.package + '-' + task.get_version().upstream_version
|
||||||
Print.command(["cd", directory])
|
Logger.command(["cd", directory])
|
||||||
os.chdir(directory)
|
os.chdir(directory)
|
||||||
|
|
||||||
edit |= apply_patch(task, patch)
|
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())
|
branch_dir = download_branch(task.get_branch_link())
|
||||||
|
|
||||||
# change directory
|
# change directory
|
||||||
Print.command(["cd", branch_dir])
|
Logger.command(["cd", branch_dir])
|
||||||
os.chdir(branch_dir)
|
os.chdir(branch_dir)
|
||||||
|
|
||||||
edit |= merge_branch(branch)
|
edit |= merge_branch(branch)
|
||||||
@ -568,9 +491,9 @@ def main(script_name, bug_number, build, edit, keyid, upload, workdir, builder,
|
|||||||
edit = True
|
edit = True
|
||||||
|
|
||||||
# update the Maintainer field
|
# update the Maintainer field
|
||||||
Print.command(["update-maintainer"])
|
Logger.command(["update-maintainer"])
|
||||||
if ubuntutools.update_maintainer.update_maintainer(verbose) != 0:
|
if ubuntutools.update_maintainer.update_maintainer(verbose) != 0:
|
||||||
Print.error("update-maintainer script failed.")
|
Logger.error("update-maintainer script failed.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Get new version of package
|
# Get new version of package
|
||||||
@ -578,7 +501,7 @@ def main(script_name, bug_number, build, edit, keyid, upload, workdir, builder,
|
|||||||
try:
|
try:
|
||||||
new_version = changelog.get_version()
|
new_version = changelog.get_version()
|
||||||
except IndexError:
|
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.")
|
"debian/changelog is probably malformed.")
|
||||||
ask_for_manual_fixing()
|
ask_for_manual_fixing()
|
||||||
continue
|
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
|
# Check if version of the new package is greater than the version in
|
||||||
# the archive.
|
# the archive.
|
||||||
if new_version <= task.get_version():
|
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()))
|
"available %s." % (new_version, task.get_version()))
|
||||||
ask_for_manual_fixing()
|
ask_for_manual_fixing()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
cmd = ["dch", "--maintmaint", "--edit", ""]
|
cmd = ["dch", "--maintmaint", "--edit", ""]
|
||||||
Print.command(cmd)
|
Logger.command(cmd)
|
||||||
if subprocess.call(cmd) != 0:
|
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
|
# Build source package
|
||||||
if patch:
|
if patch:
|
||||||
@ -615,9 +538,9 @@ def main(script_name, bug_number, build, edit, keyid, upload, workdir, builder,
|
|||||||
env = os.environ
|
env = os.environ
|
||||||
if upload == 'ubuntu':
|
if upload == 'ubuntu':
|
||||||
env['DEB_VENDOR'] = 'Ubuntu'
|
env['DEB_VENDOR'] = 'Ubuntu'
|
||||||
Print.command(cmd)
|
Logger.command(cmd)
|
||||||
if subprocess.call(cmd, env=env) != 0:
|
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
|
# TODO: Add a "retry" option
|
||||||
ask_for_manual_fixing()
|
ask_for_manual_fixing()
|
||||||
continue
|
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)
|
debdiff_filename = os.path.join(workdir, debdiff_name)
|
||||||
if not verbose:
|
if not verbose:
|
||||||
cmd.insert(1, "-q")
|
cmd.insert(1, "-q")
|
||||||
Print.command(cmd + [">", debdiff_filename])
|
Logger.command(cmd + [">", debdiff_filename])
|
||||||
process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
||||||
debdiff = process.communicate()[0]
|
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
|
# Make sure that the Launchpad bug will be closed
|
||||||
changes_file = new_dsc_file[:-4] + "_source.changes"
|
changes_file = new_dsc_file[:-4] + "_source.changes"
|
||||||
if bug_number not in get_fixed_lauchpad_bugs(changes_file):
|
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))
|
(bug_number))
|
||||||
ask_for_manual_fixing()
|
ask_for_manual_fixing()
|
||||||
continue
|
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) + \
|
allowed = map(lambda s: s + "-proposed", supported_series) + \
|
||||||
[devel_series]
|
[devel_series]
|
||||||
if changelog.distributions not in allowed:
|
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,
|
"of %s." % (changelog.distributions,
|
||||||
", ".join(allowed)))
|
", ".join(allowed)))
|
||||||
ask_for_manual_fixing()
|
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/"):
|
elif upload and upload.startwith("ppa/"):
|
||||||
allowed = supported_series + [devel_series]
|
allowed = supported_series + [devel_series]
|
||||||
if changelog.distributions not in allowed:
|
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,
|
"of %s." % (changelog.distributions,
|
||||||
", ".join(allowed)))
|
", ".join(allowed)))
|
||||||
ask_for_manual_fixing()
|
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)
|
dist = re.sub("-.*$", "", changelog.distributions)
|
||||||
result = builder.build(new_dsc_file, dist, buildresult)
|
result = builder.build(new_dsc_file, dist, buildresult)
|
||||||
if result != 0:
|
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),
|
(os.path.basename(new_dsc_file),
|
||||||
builder.get_name()))
|
builder.get_name()))
|
||||||
# TODO: Add "retry" and "update" option
|
# 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]
|
cmd = ["lintian", "-IE", "--pedantic", "-q", build_changes]
|
||||||
lintian_filename = os.path.join(workdir,
|
lintian_filename = os.path.join(workdir,
|
||||||
task.package + "_" + strip_epoch(new_version) + ".lintian")
|
task.package + "_" + strip_epoch(new_version) + ".lintian")
|
||||||
Print.command(cmd + [">", lintian_filename])
|
Logger.command(cmd + [">", lintian_filename])
|
||||||
process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
||||||
report = process.communicate()[0]
|
report = process.communicate()[0]
|
||||||
|
|
||||||
@ -727,26 +650,26 @@ def main(script_name, bug_number, build, edit, keyid, upload, workdir, builder,
|
|||||||
print "Abort."
|
print "Abort."
|
||||||
sys.exit(USER_ABORT)
|
sys.exit(USER_ABORT)
|
||||||
cmd = ["dput", "--force", upload, changes_file]
|
cmd = ["dput", "--force", upload, changes_file]
|
||||||
Print.command(cmd)
|
Logger.command(cmd)
|
||||||
if subprocess.call(cmd) != 0:
|
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))
|
(os.path.basename(changes_file), upload))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if branch:
|
if branch:
|
||||||
cmd = ['debcommit']
|
cmd = ['debcommit']
|
||||||
Print.command(cmd)
|
Logger.command(cmd)
|
||||||
if subprocess.call(cmd) != 0:
|
if subprocess.call(cmd) != 0:
|
||||||
Print.error('Bzr commit failed.')
|
Logger.error('Bzr commit failed.')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
cmd = ['bzr', 'mark-uploaded']
|
cmd = ['bzr', 'mark-uploaded']
|
||||||
Print.command(cmd)
|
Logger.command(cmd)
|
||||||
if subprocess.call(cmd) != 0:
|
if subprocess.call(cmd) != 0:
|
||||||
Print.error('Bzr tagging failed.')
|
Logger.error('Bzr tagging failed.')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
cmd = ['bzr', 'push', ':parent']
|
cmd = ['bzr', 'push', ':parent']
|
||||||
Print.command(cmd)
|
Logger.command(cmd)
|
||||||
if subprocess.call(cmd) != 0:
|
if subprocess.call(cmd) != 0:
|
||||||
Print.error('Bzr push failed.')
|
Logger.error('Bzr push failed.')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Leave while loop if everything worked
|
# Leave while loop if everything worked
|
||||||
@ -766,7 +689,7 @@ if __name__ == "__main__":
|
|||||||
if "SPONSOR_PATCH_BUILDER" in os.environ:
|
if "SPONSOR_PATCH_BUILDER" in os.environ:
|
||||||
default_builder = os.environ["SPONSOR_PATCH_BUILDER"]
|
default_builder = os.environ["SPONSOR_PATCH_BUILDER"]
|
||||||
else:
|
else:
|
||||||
default_builder = "pbuilder"
|
default_builder = None
|
||||||
|
|
||||||
parser.add_option("-b", "--build", dest="build",
|
parser.add_option("-b", "--build", dest="build",
|
||||||
help="Build the package with the specified builder.",
|
help="Build the package with the specified builder.",
|
||||||
@ -790,29 +713,24 @@ if __name__ == "__main__":
|
|||||||
help="Specify a working directory.")
|
help="Specify a working directory.")
|
||||||
|
|
||||||
(options, args) = parser.parse_args()
|
(options, args) = parser.parse_args()
|
||||||
Print.set_verbosity(options.verbose)
|
Logger.set_verbosity(options.verbose)
|
||||||
|
|
||||||
if len(args) == 0:
|
if len(args) == 0:
|
||||||
Print.error("No bug number specified.")
|
Logger.error("No bug number specified.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
elif len(args) > 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)
|
sys.exit(1)
|
||||||
|
|
||||||
bug_number = args[0]
|
bug_number = args[0]
|
||||||
if bug_number.isdigit():
|
if bug_number.isdigit():
|
||||||
bug_number = int(bug_number)
|
bug_number = int(bug_number)
|
||||||
else:
|
else:
|
||||||
Print.error("Invalid bug number specified: %s" % (bug_number))
|
Logger.error("Invalid bug number specified: %s" % (bug_number))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if options.builder == "pbuilder":
|
builder = getBuilder(options.builder)
|
||||||
builder = Pbuilder()
|
if not builder:
|
||||||
elif options.builder == "sbuild":
|
|
||||||
builder = Sbuild()
|
|
||||||
else:
|
|
||||||
Print.error("Unsupported builder specified: %s. Only pbuilder and "
|
|
||||||
"sbuild are supported." % (options.builder))
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if options.sponsoring:
|
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