mirror of
https://git.launchpad.net/ubuntu-dev-tools
synced 2025-03-12 23:51:08 +00:00
Merge trunk.
This commit is contained in:
commit
8627e0a626
165
backportpackage
165
backportpackage
@ -21,16 +21,15 @@ import shutil
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import urllib
|
||||||
|
|
||||||
from debian.deb822 import Dsc
|
from debian.deb822 import Dsc
|
||||||
import launchpadlib.launchpad
|
import launchpadlib.launchpad
|
||||||
|
import lsb_release
|
||||||
|
|
||||||
from ubuntutools.builder import getBuilder
|
from ubuntutools.builder import getBuilder
|
||||||
from ubuntutools.logger import Logger
|
from ubuntutools.logger import Logger
|
||||||
|
|
||||||
devnull = open('/dev/null', 'r+')
|
|
||||||
lp = None
|
|
||||||
|
|
||||||
def error(msg):
|
def error(msg):
|
||||||
Logger.error(msg)
|
Logger.error(msg)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@ -42,15 +41,15 @@ def check_call(cmd, *args, **kwargs):
|
|||||||
error('%s returned %d' % (cmd, ret))
|
error('%s returned %d' % (cmd, ret))
|
||||||
|
|
||||||
def parse(args):
|
def parse(args):
|
||||||
usage = 'Usage: %prog [options] <source package>'
|
usage = 'Usage: %prog [options] <source package name or .dsc URL/file>'
|
||||||
p = optparse.OptionParser(usage)
|
p = optparse.OptionParser(usage)
|
||||||
p.add_option('-t', '--to',
|
p.add_option('-d', '--destination',
|
||||||
dest='dest_releases',
|
dest='dest_releases',
|
||||||
default=[],
|
default=[],
|
||||||
action='append',
|
action='append',
|
||||||
help='Backport to DEST release (required)',
|
help='Backport to DEST release (default: current release)',
|
||||||
metavar='DEST')
|
metavar='DEST')
|
||||||
p.add_option('-f', '--from',
|
p.add_option('-s', '--source',
|
||||||
dest='source_release',
|
dest='source_release',
|
||||||
default=None,
|
default=None,
|
||||||
help='Backport from SOURCE release (default: devel release)',
|
help='Backport from SOURCE release (default: devel release)',
|
||||||
@ -79,6 +78,11 @@ def parse(args):
|
|||||||
default=None,
|
default=None,
|
||||||
help='Package version to backport (or verify)',
|
help='Package version to backport (or verify)',
|
||||||
metavar='VERSION')
|
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',
|
p.add_option('-l', '--launchpad',
|
||||||
dest='launchpad',
|
dest='launchpad',
|
||||||
default='production',
|
default='production',
|
||||||
@ -87,16 +91,16 @@ def parse(args):
|
|||||||
|
|
||||||
opts, args = p.parse_args(args)
|
opts, args = p.parse_args(args)
|
||||||
if len(args) != 1:
|
if len(args) != 1:
|
||||||
p.error('You must specify a source package')
|
p.error('You must specify a single source package or a .dsc URL/path')
|
||||||
if not opts.dest_releases:
|
if not opts.upload and not opts.build:
|
||||||
p.error('You must specify at least one destination release')
|
p.error('Nothing to do')
|
||||||
|
|
||||||
return opts, args
|
return opts, args
|
||||||
|
|
||||||
def find_release_package(package, opts):
|
def find_release_package(lp, package, version, source_release):
|
||||||
ubuntu = lp.distributions['ubuntu']
|
ubuntu = lp.distributions['ubuntu']
|
||||||
archive = ubuntu.main_archive
|
archive = ubuntu.main_archive
|
||||||
series = ubuntu.getSeries(name_or_version=opts.source_release)
|
series = ubuntu.getSeries(name_or_version=source_release)
|
||||||
status = 'Published'
|
status = 'Published'
|
||||||
for pocket in ('Updates', 'Security', 'Release'):
|
for pocket in ('Updates', 'Security', 'Release'):
|
||||||
try:
|
try:
|
||||||
@ -110,50 +114,67 @@ def find_release_package(package, opts):
|
|||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
error('Unable to find package %s in release %s' %
|
error('Unable to find package %s in release %s' %
|
||||||
(package, opts.source_release))
|
(package, source_release))
|
||||||
|
|
||||||
if opts.version and opts.version != srcpkg.source_package_version:
|
if version and version != srcpkg.source_package_version:
|
||||||
error('Requested backport of version %s but %s is at version %s' %
|
error('Requested backport of version %s but %s is at version %s' %
|
||||||
(opts.version, package, srcpkg.source_package_version))
|
(version, package, srcpkg.source_package_version))
|
||||||
|
|
||||||
return srcpkg
|
return srcpkg
|
||||||
|
|
||||||
def find_version_package(package, opts):
|
def find_version_package(lp, package, version):
|
||||||
ubuntu = lp.distributions['ubuntu']
|
ubuntu = lp.distributions['ubuntu']
|
||||||
archive = ubuntu.main_archive
|
archive = ubuntu.main_archive
|
||||||
try:
|
try:
|
||||||
# Might get more than one (i.e. same version in multiple
|
# Might get more than one (i.e. same version in multiple
|
||||||
# releases), but they should all be identical
|
# releases), but they should all be identical
|
||||||
return archive.getPublishedSources(source_name=package,
|
return archive.getPublishedSources(source_name=package,
|
||||||
version=opts.version)[0]
|
version=version)[0]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
error('Package %s was never published with version %s in Ubuntu' %
|
error('Version %s of package %s was never published in Ubuntu' %
|
||||||
(package, opts.version))
|
(version, package))
|
||||||
|
|
||||||
def fetch_package(workdir, package, opts):
|
def dscurl_from_package(lp, workdir, package, version, source_release):
|
||||||
# Returns the path to the .dsc file that was fetched
|
if not source_release and not version:
|
||||||
|
source_release = lp.distributions['ubuntu'].current_series.name
|
||||||
if not opts.source_release and not opts.version:
|
|
||||||
opts.source_release = lp.distributions['ubuntu'].current_series.name
|
|
||||||
|
|
||||||
# If source_release is specified, then version is just for
|
# If source_release is specified, then version is just for
|
||||||
# verification
|
# verification
|
||||||
if opts.source_release:
|
if source_release:
|
||||||
srcpkg = find_release_package(package, opts)
|
srcpkg = find_release_package(lp, package, version, source_release)
|
||||||
else:
|
else:
|
||||||
srcpkg = find_version_package(package, opts)
|
srcpkg = find_version_package(lp, package, version)
|
||||||
|
|
||||||
for f in srcpkg.sourceFileUrls():
|
for f in srcpkg.sourceFileUrls():
|
||||||
if f.endswith('.dsc'):
|
if f.endswith('.dsc'):
|
||||||
check_call(['dget',
|
return urllib.unquote(f)
|
||||||
'--download-only',
|
|
||||||
'--allow-unauthenticated',
|
|
||||||
f],
|
|
||||||
cwd=workdir)
|
|
||||||
return os.path.join(workdir, os.path.basename(f))
|
|
||||||
else:
|
else:
|
||||||
error('Package %s contains no .dsc file' % package)
|
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):
|
def get_backport_version(version, upload, release):
|
||||||
v = version + ('~%s1' % release)
|
v = version + ('~%s1' % release)
|
||||||
if upload and upload.startswith('ppa:'):
|
if upload and upload.startswith('ppa:'):
|
||||||
@ -166,21 +187,21 @@ def get_backport_dist(upload, release):
|
|||||||
else:
|
else:
|
||||||
return release
|
return release
|
||||||
|
|
||||||
def do_build(workdir, package, release, bp_version, opts):
|
def do_build(workdir, package, release, bp_version, builder):
|
||||||
builder = getBuilder(opts.builder)
|
builder = getBuilder(builder)
|
||||||
if not builder:
|
if not builder:
|
||||||
return
|
return
|
||||||
|
|
||||||
if opts.update:
|
if opts.update:
|
||||||
builder.update(release)
|
builder.update(release)
|
||||||
|
|
||||||
builder.build(os.path.join(workdir,
|
return builder.build(os.path.join(workdir,
|
||||||
'%s_%s.dsc' % (package, bp_version)),
|
'%s_%s.dsc' % (package, bp_version)),
|
||||||
release,
|
release,
|
||||||
workdir)
|
workdir)
|
||||||
|
|
||||||
def do_upload(workdir, package, bp_version, opts):
|
def do_upload(workdir, package, bp_version, upload):
|
||||||
prompt = 'Do you want to upload this to %s? [Y/n]' % opts.upload
|
prompt = 'Do you want to upload this to %s? [Y/n]' % upload
|
||||||
while True:
|
while True:
|
||||||
answer = raw_input(prompt).strip().lower()
|
answer = raw_input(prompt).strip().lower()
|
||||||
if answer in ('', 'y', 'yes'):
|
if answer in ('', 'y', 'yes'):
|
||||||
@ -189,16 +210,13 @@ def do_upload(workdir, package, bp_version, opts):
|
|||||||
return
|
return
|
||||||
|
|
||||||
check_call(['dput',
|
check_call(['dput',
|
||||||
opts.upload,
|
upload,
|
||||||
'%s_%s_source.changes' %
|
'%s_%s_source.changes' %
|
||||||
(package, bp_version)],
|
(package, bp_version)],
|
||||||
cwd=workdir)
|
cwd=workdir)
|
||||||
|
|
||||||
|
|
||||||
def do_backport(workdir, package, dscfile, release, opts):
|
def do_backport(workdir, package, dscfile, version, release, build, builder, upload):
|
||||||
dsc = Dsc(open(os.path.join(workdir, dscfile)))
|
|
||||||
v = dsc['Version']
|
|
||||||
|
|
||||||
check_call(['dpkg-source',
|
check_call(['dpkg-source',
|
||||||
'-x',
|
'-x',
|
||||||
dscfile,
|
dscfile,
|
||||||
@ -206,8 +224,8 @@ def do_backport(workdir, package, dscfile, release, opts):
|
|||||||
cwd=workdir)
|
cwd=workdir)
|
||||||
srcdir = os.path.join(workdir, package)
|
srcdir = os.path.join(workdir, package)
|
||||||
|
|
||||||
bp_version = get_backport_version(v, opts.upload, release)
|
bp_version = get_backport_version(version, upload, release)
|
||||||
bp_dist = get_backport_dist(opts.upload, release)
|
bp_dist = get_backport_dist(upload, release)
|
||||||
|
|
||||||
check_call(['dch',
|
check_call(['dch',
|
||||||
'--force-bad-version',
|
'--force-bad-version',
|
||||||
@ -223,32 +241,61 @@ def do_backport(workdir, package, dscfile, release, opts):
|
|||||||
bp_version = bp_version[bp_version.find(':')+1:]
|
bp_version = bp_version[bp_version.find(':')+1:]
|
||||||
|
|
||||||
print 'Please check the package in file://%s carefully' % workdir
|
print 'Please check the package in file://%s carefully' % workdir
|
||||||
if opts.build:
|
if build:
|
||||||
do_build(workdir, package, release, bp_version, opts)
|
if 0 != do_build(workdir, package, release, bp_version, builder):
|
||||||
if opts.upload:
|
error('Package failed to build; aborting')
|
||||||
do_upload(workdir, package, bp_version, opts)
|
if upload:
|
||||||
|
do_upload(workdir, package, bp_version, upload)
|
||||||
|
|
||||||
shutil.rmtree(srcdir)
|
shutil.rmtree(srcdir)
|
||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
global lp
|
|
||||||
|
|
||||||
os.environ['DEB_VENDOR'] = 'Ubuntu'
|
os.environ['DEB_VENDOR'] = 'Ubuntu'
|
||||||
|
|
||||||
opts, (package,) = parse(args[1:])
|
opts, (package_or_dsc,) = parse(args[1:])
|
||||||
|
|
||||||
script_name = os.path.basename(sys.argv[0])
|
script_name = os.path.basename(sys.argv[0])
|
||||||
lp = launchpadlib.launchpad.Launchpad.login_anonymously(script_name,
|
lp = launchpadlib.launchpad.Launchpad.login_anonymously(script_name,
|
||||||
opts.launchpad)
|
opts.launchpad)
|
||||||
|
|
||||||
workdir = tempfile.mkdtemp(prefix='backportpackage-')
|
if not opts.dest_releases:
|
||||||
|
try:
|
||||||
|
distinfo = lsb_release.get_distro_information()
|
||||||
|
opts.dest_releases = [distinfo['CODENAME']]
|
||||||
|
except:
|
||||||
|
error('No destination release specified and unable to guess yours')
|
||||||
|
|
||||||
|
if opts.workdir:
|
||||||
|
workdir = os.path.expanduser(opts.workdir)
|
||||||
|
else:
|
||||||
|
workdir = tempfile.mkdtemp(prefix='backportpackage-')
|
||||||
|
|
||||||
|
if not os.path.exists(workdir):
|
||||||
|
os.makedirs(workdir)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
dscfile = fetch_package(workdir, package, opts)
|
dscfile = fetch_package(lp,
|
||||||
|
workdir,
|
||||||
|
package_or_dsc,
|
||||||
|
opts.version,
|
||||||
|
opts.source_release)
|
||||||
|
|
||||||
|
dsc = Dsc(open(os.path.join(workdir, dscfile)))
|
||||||
|
package = dsc['Source']
|
||||||
|
version = dsc['Version']
|
||||||
|
|
||||||
for release in opts.dest_releases:
|
for release in opts.dest_releases:
|
||||||
do_backport(workdir, package, dscfile, release, opts)
|
do_backport(workdir,
|
||||||
|
package,
|
||||||
|
dscfile,
|
||||||
|
version,
|
||||||
|
release,
|
||||||
|
opts.build,
|
||||||
|
opts.builder,
|
||||||
|
opts.upload)
|
||||||
finally:
|
finally:
|
||||||
shutil.rmtree(workdir)
|
if not opts.workdir:
|
||||||
|
shutil.rmtree(workdir)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
sys.exit(main(sys.argv))
|
sys.exit(main(sys.argv))
|
||||||
|
@ -4,21 +4,19 @@ backportpackage \- helper to test package backports
|
|||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.TP
|
.TP
|
||||||
.B backportpackage \fR[\fIadditional options\fR]
|
.B backportpackage \fR[\fIadditional options\fR]
|
||||||
\-\-to <\fIdest release\fR>
|
|
||||||
.br
|
|
||||||
\-\-upload <\fIupload target\fR>
|
\-\-upload <\fIupload target\fR>
|
||||||
.br
|
.br
|
||||||
<\fIsource package\fR>
|
<\fIsource package name or .dsc URL/file\fR>
|
||||||
.PP
|
.PP
|
||||||
.B backportpackage \-h
|
.B backportpackage \-h
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.TP
|
.TP
|
||||||
.B \-t \fIDEST\fR, \-\-to=\fIDEST\fR
|
.B \-d \fIDEST\fR, \-\-destination=\fIDEST\fR
|
||||||
\fBRequired\fR. Backport the package to the specified Ubuntu
|
\fBRequired\fR. Backport the package to the specified Ubuntu
|
||||||
release. This option may be specified multiple times, but must be
|
release. If this option is unspecified, then \fBbackportpackage\fR
|
||||||
specified at least once.
|
defaults to the release on which it is currently running.
|
||||||
.TP
|
.TP
|
||||||
.B \-f \fISOURCE\fR, \-\-from=\fISOURCE\fR
|
.B \-s \fISOURCE\fR, \-\-source=\fISOURCE\fR
|
||||||
Backport the package from the specified Ubuntu release. If neither
|
Backport the package from the specified Ubuntu release. If neither
|
||||||
this option nor \fB\-\-version\fR are specified, then
|
this option nor \fB\-\-version\fR are specified, then
|
||||||
\fBbackportpackage\fR defaults to the current Ubuntu development
|
\fBbackportpackage\fR defaults to the current Ubuntu development
|
||||||
@ -42,24 +40,32 @@ Update the builder environment before attempting to build.
|
|||||||
Upload to \fIUPLOAD\fR with \fBdput\fR(1) (after confirmation).
|
Upload to \fIUPLOAD\fR with \fBdput\fR(1) (after confirmation).
|
||||||
.TP
|
.TP
|
||||||
.B \-v \fIVERSION\fR, \-\-version=\fIVERSION\fR
|
.B \-v \fIVERSION\fR, \-\-version=\fIVERSION\fR
|
||||||
If the \fB\-\-from\fR option is specified, then \fBbackportpackage\fR
|
If the \fB\-\-source\fR option is specified, then
|
||||||
verifies that the current version of \fIsource package\fR in
|
\fBbackportpackage\fR verifies that the current version of \fIsource
|
||||||
\fISOURCE\fR is the same as \fIVERSION\fR. Otherwise,
|
package\fR in \fISOURCE\fR is the same as \fIVERSION\fR. Otherwise,
|
||||||
\fBbackportpackage\fR finds version \fIVERSION\fR of \fIsource
|
\fBbackportpackage\fR finds version \fIVERSION\fR of \fIsource
|
||||||
package\fR, regardless of the release in which it was published (or if
|
package\fR, regardless of the release in which it was published (or if
|
||||||
that version is still current).
|
that version is still current). This option is ignored if a .dsc URL
|
||||||
|
or path is passed in instead of a source package name.
|
||||||
|
.TP
|
||||||
|
.B \-w \fIWORKDIR\fR, \-\-workdir=\fIWORKDIR\fR
|
||||||
|
If \fIWORKDIR\fR is specified, then all files are downloaded,
|
||||||
|
unpacked, built into, and otherwise manipulated in
|
||||||
|
\fIWORKDIR\fR. Otherwise, a temporary directory is created, which is
|
||||||
|
deleted before \fIbackportpackage\fR exits.
|
||||||
.TP
|
.TP
|
||||||
.B \-l \fIINSTANCE\fR, \-\-launchpad=\fIINSTANCE\fR
|
.B \-l \fIINSTANCE\fR, \-\-launchpad=\fIINSTANCE\fR
|
||||||
Use the specified instance of Launchpad (e.g. "staging"), instead of
|
Use the specified instance of Launchpad (e.g. "staging"), instead of
|
||||||
the default of "production".
|
the default of "production".
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
\fBbackportpackage\fR fetches a package from one Ubuntu release and
|
\fBbackportpackage\fR fetches a package from one Ubuntu release or
|
||||||
creates a no-change backport of that package to a previous release,
|
from a specified .dsc path or URL and creates a no-change backport of
|
||||||
optionally doing a test build of the package and/or uploading the
|
that package to a previous release, optionally doing a test build of
|
||||||
resulting backport for testing.
|
the package and/or uploading the resulting backport for testing.
|
||||||
.PP
|
.PP
|
||||||
The backported package is fetched and built in a temporary directory
|
Unless a working directory is specified, the backported package is
|
||||||
in \fB/tmp\fR, which is removed once the script finishes running.
|
fetched and built in a temporary directory in \fB/tmp\fR, which is
|
||||||
|
removed once the script finishes running.
|
||||||
.PP
|
.PP
|
||||||
\fBbackportpackage\fR is only recommended for testing backports in a
|
\fBbackportpackage\fR is only recommended for testing backports in a
|
||||||
PPA, not uploading backports to the Ubuntu archive.
|
PPA, not uploading backports to the Ubuntu archive.
|
||||||
@ -67,9 +73,34 @@ PPA, not uploading backports to the Ubuntu archive.
|
|||||||
.TP
|
.TP
|
||||||
.B UBUNTUTOOLS_BUILDER
|
.B UBUNTUTOOLS_BUILDER
|
||||||
The default builder for Ubuntu development tools that support it
|
The default builder for Ubuntu development tools that support it
|
||||||
(including \fBbackportpackage\fR. Supported are \fBpbuilder\fR(8) and
|
(including \fBbackportpackage\fR). Supported are \fBpbuilder\fR(8) and
|
||||||
\fBsbuild\fR(1). If unset and not provided on the command line,
|
\fBsbuild\fR(1). If unset and not provided on the command line,
|
||||||
\fBpbuilder\fR(8) is used.
|
\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
|
.SH AUTHOR
|
||||||
\fBbackportpackage\fR and this manpage were written by Evan Broder
|
\fBbackportpackage\fR and this manpage were written by Evan Broder
|
||||||
<evan@ebroder.net>
|
<evan@ebroder.net>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user