mirror of
https://git.launchpad.net/ubuntu-dev-tools
synced 2025-03-12 15:41:09 +00:00
style: Format Python code with black
``` PYTHON_SCRIPTS=$(grep -l -r '^#! */usr/bin/python3$' .) black -C -l 99 . $PYTHON_SCRIPTS ``` Signed-off-by: Benjamin Drung <benjamin.drung@canonical.com>
This commit is contained in:
parent
79d24c9df1
commit
3354b526b5
441
backportpackage
441
backportpackage
@ -34,18 +34,20 @@ except ImportError:
|
||||
from httplib2 import Http, HttpLib2Error
|
||||
from distro_info import DebianDistroInfo, UbuntuDistroInfo
|
||||
|
||||
from ubuntutools.archive import (DebianSourcePackage,
|
||||
UbuntuSourcePackage, DownloadError)
|
||||
from ubuntutools.archive import DebianSourcePackage, UbuntuSourcePackage, DownloadError
|
||||
from ubuntutools.config import UDTConfig, ubu_email
|
||||
from ubuntutools.builder import get_builder
|
||||
from ubuntutools.lp.lpapicache import (Launchpad, Distribution,
|
||||
SeriesNotFoundException,
|
||||
PackageNotFoundException)
|
||||
from ubuntutools.misc import (system_distribution, vendor_to_distroinfo,
|
||||
codename_to_distribution)
|
||||
from ubuntutools.lp.lpapicache import (
|
||||
Launchpad,
|
||||
Distribution,
|
||||
SeriesNotFoundException,
|
||||
PackageNotFoundException,
|
||||
)
|
||||
from ubuntutools.misc import system_distribution, vendor_to_distroinfo, codename_to_distribution
|
||||
from ubuntutools.question import YesNoQuestion
|
||||
|
||||
from ubuntutools import getLogger
|
||||
|
||||
Logger = getLogger()
|
||||
|
||||
|
||||
@ -55,130 +57,152 @@ def error(msg):
|
||||
|
||||
|
||||
def check_call(cmd, *args, **kwargs):
|
||||
Logger.debug(' '.join(cmd))
|
||||
Logger.debug(" ".join(cmd))
|
||||
ret = subprocess.call(cmd, *args, **kwargs)
|
||||
if ret != 0:
|
||||
error('%s returned %d.' % (cmd[0], ret))
|
||||
error("%s returned %d." % (cmd[0], ret))
|
||||
|
||||
|
||||
def parse(args):
|
||||
usage = 'Usage: %prog [options] <source package name or .dsc URL/file>'
|
||||
usage = "Usage: %prog [options] <source package name or .dsc URL/file>"
|
||||
parser = optparse.OptionParser(usage)
|
||||
parser.add_option('-d', '--destination',
|
||||
metavar='DEST',
|
||||
dest='dest_releases',
|
||||
default=[],
|
||||
action='append',
|
||||
help='Backport to DEST release '
|
||||
'(default: current release)')
|
||||
parser.add_option('-s', '--source',
|
||||
metavar='SOURCE',
|
||||
dest='source_release',
|
||||
help='Backport from SOURCE release '
|
||||
'(default: devel release)')
|
||||
parser.add_option('-S', '--suffix',
|
||||
metavar='SUFFIX',
|
||||
help='Suffix to append to version number '
|
||||
'(default: ~ppa1 when uploading to a PPA)')
|
||||
parser.add_option('-e', '--message',
|
||||
metavar='MESSAGE',
|
||||
default="No-change",
|
||||
help='Changelog message to use instead of "No-change" '
|
||||
'(default: No-change backport to DEST.)')
|
||||
parser.add_option('-b', '--build',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Build the package before uploading '
|
||||
'(default: %default)')
|
||||
parser.add_option('-B', '--builder',
|
||||
metavar='BUILDER',
|
||||
help='Specify the package builder (default: pbuilder)')
|
||||
parser.add_option('-U', '--update',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Update the build environment before '
|
||||
'attempting to build')
|
||||
parser.add_option('-u', '--upload',
|
||||
metavar='UPLOAD',
|
||||
help='Specify an upload destination')
|
||||
parser.add_option("-k", "--key",
|
||||
dest='keyid',
|
||||
help="Specify the key ID to be used for signing.")
|
||||
parser.add_option('--dont-sign',
|
||||
dest='keyid', action='store_false',
|
||||
help='Do not sign the upload.')
|
||||
parser.add_option('-y', '--yes',
|
||||
dest='prompt',
|
||||
default=True,
|
||||
action='store_false',
|
||||
help='Do not prompt before uploading to a PPA')
|
||||
parser.add_option('-v', '--version',
|
||||
metavar='VERSION',
|
||||
help='Package version to backport (or verify)')
|
||||
parser.add_option('-w', '--workdir',
|
||||
metavar='WORKDIR',
|
||||
help='Specify a working directory '
|
||||
'(default: temporary dir)')
|
||||
parser.add_option('-r', '--release-pocket',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Target the release pocket in the .changes file. '
|
||||
'Necessary (and default) for uploads to PPAs')
|
||||
parser.add_option('-c', '--close',
|
||||
metavar='BUG',
|
||||
help='Bug to close in the changelog entry.')
|
||||
parser.add_option('-m', '--mirror',
|
||||
metavar='URL',
|
||||
help='Preferred mirror (default: Launchpad)')
|
||||
parser.add_option('-l', '--lpinstance',
|
||||
metavar='INSTANCE',
|
||||
help='Launchpad instance to connect to '
|
||||
'(default: production)')
|
||||
parser.add_option('--no-conf',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help="Don't read config files or environment variables")
|
||||
parser.add_option(
|
||||
"-d",
|
||||
"--destination",
|
||||
metavar="DEST",
|
||||
dest="dest_releases",
|
||||
default=[],
|
||||
action="append",
|
||||
help="Backport to DEST release (default: current release)",
|
||||
)
|
||||
parser.add_option(
|
||||
"-s",
|
||||
"--source",
|
||||
metavar="SOURCE",
|
||||
dest="source_release",
|
||||
help="Backport from SOURCE release (default: devel release)",
|
||||
)
|
||||
parser.add_option(
|
||||
"-S",
|
||||
"--suffix",
|
||||
metavar="SUFFIX",
|
||||
help="Suffix to append to version number (default: ~ppa1 when uploading to a PPA)",
|
||||
)
|
||||
parser.add_option(
|
||||
"-e",
|
||||
"--message",
|
||||
metavar="MESSAGE",
|
||||
default="No-change",
|
||||
help='Changelog message to use instead of "No-change" '
|
||||
"(default: No-change backport to DEST.)",
|
||||
)
|
||||
parser.add_option(
|
||||
"-b",
|
||||
"--build",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Build the package before uploading (default: %default)",
|
||||
)
|
||||
parser.add_option(
|
||||
"-B",
|
||||
"--builder",
|
||||
metavar="BUILDER",
|
||||
help="Specify the package builder (default: pbuilder)",
|
||||
)
|
||||
parser.add_option(
|
||||
"-U",
|
||||
"--update",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Update the build environment before attempting to build",
|
||||
)
|
||||
parser.add_option("-u", "--upload", metavar="UPLOAD", help="Specify an upload destination")
|
||||
parser.add_option(
|
||||
"-k", "--key", dest="keyid", help="Specify the key ID to be used for signing."
|
||||
)
|
||||
parser.add_option(
|
||||
"--dont-sign", dest="keyid", action="store_false", help="Do not sign the upload."
|
||||
)
|
||||
parser.add_option(
|
||||
"-y",
|
||||
"--yes",
|
||||
dest="prompt",
|
||||
default=True,
|
||||
action="store_false",
|
||||
help="Do not prompt before uploading to a PPA",
|
||||
)
|
||||
parser.add_option(
|
||||
"-v", "--version", metavar="VERSION", help="Package version to backport (or verify)"
|
||||
)
|
||||
parser.add_option(
|
||||
"-w",
|
||||
"--workdir",
|
||||
metavar="WORKDIR",
|
||||
help="Specify a working directory (default: temporary dir)",
|
||||
)
|
||||
parser.add_option(
|
||||
"-r",
|
||||
"--release-pocket",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Target the release pocket in the .changes file. "
|
||||
"Necessary (and default) for uploads to PPAs",
|
||||
)
|
||||
parser.add_option("-c", "--close", metavar="BUG", help="Bug to close in the changelog entry.")
|
||||
parser.add_option(
|
||||
"-m", "--mirror", metavar="URL", help="Preferred mirror (default: Launchpad)"
|
||||
)
|
||||
parser.add_option(
|
||||
"-l",
|
||||
"--lpinstance",
|
||||
metavar="INSTANCE",
|
||||
help="Launchpad instance to connect to (default: production)",
|
||||
)
|
||||
parser.add_option(
|
||||
"--no-conf",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Don't read config files or environment variables",
|
||||
)
|
||||
|
||||
opts, args = parser.parse_args(args)
|
||||
if len(args) != 1:
|
||||
parser.error('You must specify a single source package or a .dsc '
|
||||
'URL/path.')
|
||||
parser.error("You must specify a single source package or a .dsc URL/path.")
|
||||
config = UDTConfig(opts.no_conf)
|
||||
if opts.builder is None:
|
||||
opts.builder = config.get_value('BUILDER')
|
||||
opts.builder = config.get_value("BUILDER")
|
||||
if not opts.update:
|
||||
opts.update = config.get_value('UPDATE_BUILDER', boolean=True)
|
||||
opts.update = config.get_value("UPDATE_BUILDER", boolean=True)
|
||||
if opts.workdir is None:
|
||||
opts.workdir = config.get_value('WORKDIR')
|
||||
opts.workdir = config.get_value("WORKDIR")
|
||||
if opts.lpinstance is None:
|
||||
opts.lpinstance = config.get_value('LPINSTANCE')
|
||||
opts.lpinstance = config.get_value("LPINSTANCE")
|
||||
if opts.upload is None:
|
||||
opts.upload = config.get_value('UPLOAD')
|
||||
opts.upload = config.get_value("UPLOAD")
|
||||
if opts.keyid is None:
|
||||
opts.keyid = config.get_value('KEYID')
|
||||
opts.keyid = config.get_value("KEYID")
|
||||
if not opts.upload and not opts.workdir:
|
||||
parser.error('Please specify either a working dir or an upload target!')
|
||||
if opts.upload and opts.upload.startswith('ppa:'):
|
||||
parser.error("Please specify either a working dir or an upload target!")
|
||||
if opts.upload and opts.upload.startswith("ppa:"):
|
||||
opts.release_pocket = True
|
||||
|
||||
return opts, args, config
|
||||
|
||||
|
||||
def find_release_package(mirror, workdir, package, version, source_release,
|
||||
config):
|
||||
def find_release_package(mirror, workdir, package, version, source_release, config):
|
||||
srcpkg = None
|
||||
|
||||
if source_release:
|
||||
distribution = codename_to_distribution(source_release)
|
||||
if not distribution:
|
||||
error('Unknown release codename %s' % source_release)
|
||||
error("Unknown release codename %s" % source_release)
|
||||
info = vendor_to_distroinfo(distribution)()
|
||||
source_release = info.codename(source_release, default=source_release)
|
||||
else:
|
||||
distribution = system_distribution()
|
||||
mirrors = [mirror] if mirror else []
|
||||
|
||||
mirrors.append(config.get_value('%s_MIRROR' % distribution.upper()))
|
||||
mirrors.append(config.get_value("%s_MIRROR" % distribution.upper()))
|
||||
|
||||
if not version:
|
||||
archive = Distribution(distribution.lower()).getArchive()
|
||||
@ -188,39 +212,35 @@ def find_release_package(mirror, workdir, package, version, source_release,
|
||||
error(str(e))
|
||||
version = spph.getVersion()
|
||||
|
||||
if distribution == 'Debian':
|
||||
srcpkg = DebianSourcePackage(package,
|
||||
version,
|
||||
workdir=workdir,
|
||||
mirrors=mirrors)
|
||||
elif distribution == 'Ubuntu':
|
||||
srcpkg = UbuntuSourcePackage(package,
|
||||
version,
|
||||
workdir=workdir,
|
||||
mirrors=mirrors)
|
||||
if distribution == "Debian":
|
||||
srcpkg = DebianSourcePackage(package, version, workdir=workdir, mirrors=mirrors)
|
||||
elif distribution == "Ubuntu":
|
||||
srcpkg = UbuntuSourcePackage(package, version, workdir=workdir, mirrors=mirrors)
|
||||
|
||||
return srcpkg
|
||||
|
||||
|
||||
def find_package(mirror, workdir, package, version, source_release, config):
|
||||
"Returns the SourcePackage"
|
||||
if package.endswith('.dsc'):
|
||||
if package.endswith(".dsc"):
|
||||
# Here we are using UbuntuSourcePackage just because we don't have any
|
||||
# "general" class that is safely instantiable (as SourcePackage is an
|
||||
# abstract class). None of the distribution-specific details within
|
||||
# UbuntuSourcePackage is relevant for this use case.
|
||||
return UbuntuSourcePackage(version=version, dscfile=package,
|
||||
workdir=workdir, mirrors=(mirror,))
|
||||
return UbuntuSourcePackage(
|
||||
version=version, dscfile=package, workdir=workdir, mirrors=(mirror,)
|
||||
)
|
||||
|
||||
if not source_release and not version:
|
||||
info = vendor_to_distroinfo(system_distribution())
|
||||
source_release = info().devel()
|
||||
|
||||
srcpkg = find_release_package(mirror, workdir, package, version,
|
||||
source_release, config)
|
||||
srcpkg = find_release_package(mirror, workdir, package, version, source_release, config)
|
||||
if version and srcpkg.version != version:
|
||||
error('Requested backport of version %s but version of %s in %s is %s'
|
||||
% (version, package, source_release, srcpkg.version))
|
||||
error(
|
||||
"Requested backport of version %s but version of %s in %s is %s"
|
||||
% (version, package, source_release, srcpkg.version)
|
||||
)
|
||||
|
||||
return srcpkg
|
||||
|
||||
@ -228,30 +248,27 @@ def find_package(mirror, workdir, package, version, source_release, config):
|
||||
def get_backport_version(version, suffix, upload, release):
|
||||
distribution = codename_to_distribution(release)
|
||||
if not distribution:
|
||||
error('Unknown release codename %s' % release)
|
||||
if distribution == 'Debian':
|
||||
error("Unknown release codename %s" % release)
|
||||
if distribution == "Debian":
|
||||
debian_distro_info = DebianDistroInfo()
|
||||
debian_codenames = debian_distro_info.supported()
|
||||
if release in debian_codenames:
|
||||
release_version = debian_distro_info.version(release)
|
||||
if not release_version:
|
||||
error(f"Can't find the release version for {release}")
|
||||
backport_version = "{}~bpo{}+1".format(
|
||||
version, release_version
|
||||
)
|
||||
backport_version = "{}~bpo{}+1".format(version, release_version)
|
||||
else:
|
||||
error(f"{release} is not a supported release ({debian_codenames})")
|
||||
elif distribution == 'Ubuntu':
|
||||
series = Distribution(distribution.lower()).\
|
||||
getSeries(name_or_version=release)
|
||||
elif distribution == "Ubuntu":
|
||||
series = Distribution(distribution.lower()).getSeries(name_or_version=release)
|
||||
|
||||
backport_version = version + ('~bpo%s.1' % (series.version))
|
||||
backport_version = version + ("~bpo%s.1" % (series.version))
|
||||
else:
|
||||
error('Unknown distribution «%s» for release «%s»' % (distribution, release))
|
||||
error("Unknown distribution «%s» for release «%s»" % (distribution, release))
|
||||
if suffix is not None:
|
||||
backport_version += suffix
|
||||
elif upload and upload.startswith('ppa:'):
|
||||
backport_version += '~ppa1'
|
||||
elif upload and upload.startswith("ppa:"):
|
||||
backport_version += "~ppa1"
|
||||
return backport_version
|
||||
|
||||
|
||||
@ -259,10 +276,9 @@ def get_old_version(source, release):
|
||||
try:
|
||||
distribution = codename_to_distribution(release)
|
||||
archive = Distribution(distribution.lower()).getArchive()
|
||||
pkg = archive.getSourcePackage(source,
|
||||
release,
|
||||
('Release', 'Security', 'Updates',
|
||||
'Proposed', 'Backports'))
|
||||
pkg = archive.getSourcePackage(
|
||||
source, release, ("Release", "Security", "Updates", "Proposed", "Backports")
|
||||
)
|
||||
return pkg.getVersion()
|
||||
except (SeriesNotFoundException, PackageNotFoundException):
|
||||
pass
|
||||
@ -272,7 +288,7 @@ def get_backport_dist(release, release_pocket):
|
||||
if release_pocket:
|
||||
return release
|
||||
else:
|
||||
return '%s-backports' % release
|
||||
return "%s-backports" % release
|
||||
|
||||
|
||||
def do_build(workdir, dsc, release, builder, update):
|
||||
@ -286,41 +302,43 @@ def do_build(workdir, dsc, release, builder, update):
|
||||
|
||||
# builder.build is going to chdir to buildresult:
|
||||
workdir = os.path.realpath(workdir)
|
||||
return builder.build(os.path.join(workdir, dsc),
|
||||
release,
|
||||
os.path.join(workdir, "buildresult"))
|
||||
return builder.build(os.path.join(workdir, dsc), release, os.path.join(workdir, "buildresult"))
|
||||
|
||||
|
||||
def do_upload(workdir, package, bp_version, changes, upload, prompt):
|
||||
print('Please check %s %s in file://%s carefully!' % (package, bp_version, workdir))
|
||||
if prompt or upload == 'ubuntu':
|
||||
question = 'Do you want to upload the package to %s' % upload
|
||||
print("Please check %s %s in file://%s carefully!" % (package, bp_version, workdir))
|
||||
if prompt or upload == "ubuntu":
|
||||
question = "Do you want to upload the package to %s" % upload
|
||||
answer = YesNoQuestion().ask(question, "yes")
|
||||
if answer == "no":
|
||||
return
|
||||
|
||||
check_call(['dput', upload, changes], cwd=workdir)
|
||||
check_call(["dput", upload, changes], cwd=workdir)
|
||||
|
||||
|
||||
def orig_needed(upload, workdir, pkg):
|
||||
'''Avoid a -sa if possible'''
|
||||
if not upload or not upload.startswith('ppa:'):
|
||||
"""Avoid a -sa if possible"""
|
||||
if not upload or not upload.startswith("ppa:"):
|
||||
return True
|
||||
ppa = upload.split(':', 1)[1]
|
||||
user, ppa = ppa.split('/', 1)
|
||||
ppa = upload.split(":", 1)[1]
|
||||
user, ppa = ppa.split("/", 1)
|
||||
|
||||
version = pkg.version.upstream_version
|
||||
|
||||
h = Http()
|
||||
for filename in glob.glob(os.path.join(workdir, '%s_%s.orig*' % (pkg.source, version))):
|
||||
url = ('https://launchpad.net/~%s/+archive/%s/+sourcefiles/%s/%s/%s'
|
||||
% (quote(user), quote(ppa), quote(pkg.source),
|
||||
quote(pkg.version.full_version),
|
||||
quote(os.path.basename(filename))))
|
||||
for filename in glob.glob(os.path.join(workdir, "%s_%s.orig*" % (pkg.source, version))):
|
||||
url = "https://launchpad.net/~%s/+archive/%s/+sourcefiles/%s/%s/%s" % (
|
||||
quote(user),
|
||||
quote(ppa),
|
||||
quote(pkg.source),
|
||||
quote(pkg.version.full_version),
|
||||
quote(os.path.basename(filename)),
|
||||
)
|
||||
try:
|
||||
headers, body = h.request(url, 'HEAD')
|
||||
if (headers.status != 200 or
|
||||
not headers['content-location'].startswith('https://launchpadlibrarian.net')):
|
||||
headers, body = h.request(url, "HEAD")
|
||||
if headers.status != 200 or not headers["content-location"].startswith(
|
||||
"https://launchpadlibrarian.net"
|
||||
):
|
||||
return True
|
||||
except HttpLib2Error as e:
|
||||
Logger.debug(e)
|
||||
@ -328,61 +346,79 @@ def orig_needed(upload, workdir, pkg):
|
||||
return False
|
||||
|
||||
|
||||
def do_backport(workdir, pkg, suffix, message, close, release, release_pocket,
|
||||
build, builder, update, upload, keyid, prompt):
|
||||
dirname = '%s-%s' % (pkg.source, release)
|
||||
def do_backport(
|
||||
workdir,
|
||||
pkg,
|
||||
suffix,
|
||||
message,
|
||||
close,
|
||||
release,
|
||||
release_pocket,
|
||||
build,
|
||||
builder,
|
||||
update,
|
||||
upload,
|
||||
keyid,
|
||||
prompt,
|
||||
):
|
||||
dirname = "%s-%s" % (pkg.source, release)
|
||||
srcdir = os.path.join(workdir, dirname)
|
||||
|
||||
if os.path.exists(srcdir):
|
||||
question = 'Working directory %s already exists. Delete it?' % srcdir
|
||||
if YesNoQuestion().ask(question, 'no') == 'no':
|
||||
question = "Working directory %s already exists. Delete it?" % srcdir
|
||||
if YesNoQuestion().ask(question, "no") == "no":
|
||||
sys.exit(1)
|
||||
shutil.rmtree(srcdir)
|
||||
|
||||
pkg.unpack(dirname)
|
||||
|
||||
bp_version = get_backport_version(pkg.version.full_version, suffix,
|
||||
upload, release)
|
||||
bp_version = get_backport_version(pkg.version.full_version, suffix, upload, release)
|
||||
old_version = get_old_version(pkg.source, release)
|
||||
bp_dist = get_backport_dist(release, release_pocket)
|
||||
|
||||
changelog = '%s backport to %s.' % (message, release,)
|
||||
changelog = "%s backport to %s." % (message, release)
|
||||
if close:
|
||||
changelog += ' (LP: #%s)' % (close,)
|
||||
check_call(['dch',
|
||||
'--force-bad-version',
|
||||
'--force-distribution',
|
||||
'--preserve',
|
||||
'--newversion', bp_version,
|
||||
'--distribution', bp_dist,
|
||||
changelog],
|
||||
cwd=srcdir)
|
||||
changelog += " (LP: #%s)" % (close,)
|
||||
check_call(
|
||||
[
|
||||
"dch",
|
||||
"--force-bad-version",
|
||||
"--force-distribution",
|
||||
"--preserve",
|
||||
"--newversion",
|
||||
bp_version,
|
||||
"--distribution",
|
||||
bp_dist,
|
||||
changelog,
|
||||
],
|
||||
cwd=srcdir,
|
||||
)
|
||||
|
||||
cmd = ['debuild', '--no-lintian', '-S', '-nc', '-uc', '-us']
|
||||
cmd = ["debuild", "--no-lintian", "-S", "-nc", "-uc", "-us"]
|
||||
if orig_needed(upload, workdir, pkg):
|
||||
cmd.append('-sa')
|
||||
cmd.append("-sa")
|
||||
else:
|
||||
cmd.append('-sd')
|
||||
cmd.append("-sd")
|
||||
if old_version:
|
||||
cmd.append('-v%s' % old_version)
|
||||
cmd.append("-v%s" % old_version)
|
||||
env = os.environ.copy()
|
||||
# An ubuntu.com e-mail address would make dpkg-buildpackage fail if there
|
||||
# wasn't an Ubuntu maintainer for an ubuntu-versioned package. LP: #1007042
|
||||
env.pop('DEBEMAIL', None)
|
||||
env.pop("DEBEMAIL", None)
|
||||
check_call(cmd, cwd=srcdir, env=env)
|
||||
|
||||
fn_base = pkg.source + '_' + bp_version.split(':', 1)[-1]
|
||||
changes = fn_base + '_source.changes'
|
||||
fn_base = pkg.source + "_" + bp_version.split(":", 1)[-1]
|
||||
changes = fn_base + "_source.changes"
|
||||
|
||||
if build:
|
||||
if 0 != do_build(workdir, fn_base + '.dsc', release, builder, update):
|
||||
if 0 != do_build(workdir, fn_base + ".dsc", release, builder, update):
|
||||
sys.exit(1)
|
||||
|
||||
# None: sign with the default signature. False: don't sign
|
||||
if keyid is not False:
|
||||
cmd = ['debsign']
|
||||
cmd = ["debsign"]
|
||||
if keyid:
|
||||
cmd.append('-k' + keyid)
|
||||
cmd.append("-k" + keyid)
|
||||
cmd.append(changes)
|
||||
check_call(cmd, cwd=workdir)
|
||||
if upload:
|
||||
@ -402,13 +438,13 @@ def main(args):
|
||||
if lsb_release:
|
||||
distinfo = lsb_release.get_distro_information()
|
||||
try:
|
||||
current_distro = distinfo['ID']
|
||||
current_distro = distinfo["ID"]
|
||||
except KeyError:
|
||||
error('No destination release specified and unable to guess yours.')
|
||||
error("No destination release specified and unable to guess yours.")
|
||||
else:
|
||||
err, current_distro = subprocess.getstatusoutput('lsb_release --id --short')
|
||||
err, current_distro = subprocess.getstatusoutput("lsb_release --id --short")
|
||||
if err:
|
||||
error('Could not run lsb_release to retrieve distribution')
|
||||
error("Could not run lsb_release to retrieve distribution")
|
||||
|
||||
if current_distro == "Ubuntu":
|
||||
opts.dest_releases = [UbuntuDistroInfo().lts()]
|
||||
@ -420,34 +456,33 @@ def main(args):
|
||||
if opts.workdir:
|
||||
workdir = os.path.expanduser(opts.workdir)
|
||||
else:
|
||||
workdir = tempfile.mkdtemp(prefix='backportpackage-')
|
||||
workdir = tempfile.mkdtemp(prefix="backportpackage-")
|
||||
|
||||
if not os.path.exists(workdir):
|
||||
os.makedirs(workdir)
|
||||
|
||||
try:
|
||||
pkg = find_package(opts.mirror,
|
||||
workdir,
|
||||
package_or_dsc,
|
||||
opts.version,
|
||||
opts.source_release,
|
||||
config)
|
||||
pkg = find_package(
|
||||
opts.mirror, workdir, package_or_dsc, opts.version, opts.source_release, config
|
||||
)
|
||||
pkg.pull()
|
||||
|
||||
for release in opts.dest_releases:
|
||||
do_backport(workdir,
|
||||
pkg,
|
||||
opts.suffix,
|
||||
opts.message,
|
||||
opts.close,
|
||||
release,
|
||||
opts.release_pocket,
|
||||
opts.build,
|
||||
opts.builder,
|
||||
opts.update,
|
||||
opts.upload,
|
||||
opts.keyid,
|
||||
opts.prompt)
|
||||
do_backport(
|
||||
workdir,
|
||||
pkg,
|
||||
opts.suffix,
|
||||
opts.message,
|
||||
opts.close,
|
||||
release,
|
||||
opts.release_pocket,
|
||||
opts.build,
|
||||
opts.builder,
|
||||
opts.update,
|
||||
opts.upload,
|
||||
opts.keyid,
|
||||
opts.prompt,
|
||||
)
|
||||
except DownloadError as e:
|
||||
error(str(e))
|
||||
finally:
|
||||
@ -455,5 +490,5 @@ def main(args):
|
||||
shutil.rmtree(workdir)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv))
|
||||
|
45
bitesize
45
bitesize
@ -30,6 +30,7 @@ from launchpadlib.errors import HTTPError
|
||||
from ubuntutools.config import UDTConfig
|
||||
|
||||
from ubuntutools import getLogger
|
||||
|
||||
Logger = getLogger()
|
||||
|
||||
|
||||
@ -46,21 +47,28 @@ def save_entry(entry):
|
||||
|
||||
|
||||
def tag_bug(bug):
|
||||
bug.tags = bug.tags + ['bitesize'] # LP: #254901 workaround
|
||||
bug.tags = bug.tags + ["bitesize"] # LP: #254901 workaround
|
||||
save_entry(bug)
|
||||
|
||||
|
||||
def main():
|
||||
usage = "Usage: %prog <bug number>"
|
||||
opt_parser = OptionParser(usage)
|
||||
opt_parser.add_option("-l", "--lpinstance", metavar="INSTANCE",
|
||||
help="Launchpad instance to connect to "
|
||||
"(default: production)",
|
||||
dest="lpinstance", default=None)
|
||||
opt_parser.add_option("--no-conf",
|
||||
help="Don't read config files or "
|
||||
"environment variables.",
|
||||
dest="no_conf", default=False, action="store_true")
|
||||
opt_parser.add_option(
|
||||
"-l",
|
||||
"--lpinstance",
|
||||
metavar="INSTANCE",
|
||||
help="Launchpad instance to connect to (default: production)",
|
||||
dest="lpinstance",
|
||||
default=None,
|
||||
)
|
||||
opt_parser.add_option(
|
||||
"--no-conf",
|
||||
help="Don't read config files or environment variables.",
|
||||
dest="no_conf",
|
||||
default=False,
|
||||
action="store_true",
|
||||
)
|
||||
(options, args) = opt_parser.parse_args()
|
||||
config = UDTConfig(options.no_conf)
|
||||
if options.lpinstance is None:
|
||||
@ -77,19 +85,22 @@ def main():
|
||||
bug = launchpad.bugs[args[0]]
|
||||
except HTTPError as error:
|
||||
if error.response.status == 401:
|
||||
error_out("Don't have enough permissions to access bug %s. %s" %
|
||||
(args[0], error.content))
|
||||
error_out(
|
||||
"Don't have enough permissions to access bug %s. %s" % (args[0], error.content)
|
||||
)
|
||||
else:
|
||||
raise
|
||||
if 'bitesize' in bug.tags:
|
||||
if "bitesize" in bug.tags:
|
||||
error_out("Bug is already marked as 'bitesize'.")
|
||||
bug.newMessage(content="I'm marking this bug as 'bitesize' as it looks "
|
||||
"like an issue that is easy to fix and suitable "
|
||||
"for newcomers in Ubuntu development. If you need "
|
||||
"any help with fixing it, talk to me about it.")
|
||||
bug.newMessage(
|
||||
content="I'm marking this bug as 'bitesize' as it looks "
|
||||
"like an issue that is easy to fix and suitable "
|
||||
"for newcomers in Ubuntu development. If you need "
|
||||
"any help with fixing it, talk to me about it."
|
||||
)
|
||||
bug.subscribe(person=launchpad.me)
|
||||
tag_bug(launchpad.bugs[bug.id]) # fresh bug object, LP: #336866 workaround
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
80
check-mir
80
check-mir
@ -29,57 +29,58 @@ import apt
|
||||
|
||||
|
||||
def check_support(apt_cache, pkgname, alt=False):
|
||||
'''Check if pkgname is in main or restricted.
|
||||
"""Check if pkgname is in main or restricted.
|
||||
|
||||
This prints messages if a package is not in main/restricted, or only
|
||||
partially (i. e. source in main, but binary in universe).
|
||||
'''
|
||||
"""
|
||||
if alt:
|
||||
prefix = ' ... alternative ' + pkgname
|
||||
prefix = " ... alternative " + pkgname
|
||||
else:
|
||||
prefix = ' * ' + pkgname
|
||||
prefix = " * " + pkgname
|
||||
|
||||
try:
|
||||
pkg = apt_cache[pkgname]
|
||||
except KeyError:
|
||||
print(prefix, 'does not exist (pure virtual?)', file=sys.stderr)
|
||||
print(prefix, "does not exist (pure virtual?)", file=sys.stderr)
|
||||
return False
|
||||
|
||||
section = pkg.candidate.section
|
||||
if section.startswith('universe') or section.startswith('multiverse'):
|
||||
if section.startswith("universe") or section.startswith("multiverse"):
|
||||
# check if the source package is in main and thus will only need binary
|
||||
# promotion
|
||||
source_records = apt.apt_pkg.SourceRecords()
|
||||
if not source_records.lookup(pkg.candidate.source_name):
|
||||
print('ERROR: Cannot lookup source package for', pkg.name,
|
||||
file=sys.stderr)
|
||||
print(prefix, 'package is in', section.split('/')[0])
|
||||
print("ERROR: Cannot lookup source package for", pkg.name, file=sys.stderr)
|
||||
print(prefix, "package is in", section.split("/")[0])
|
||||
return False
|
||||
src = apt.apt_pkg.TagSection(source_records.record)
|
||||
if (src['Section'].startswith('universe') or
|
||||
src['Section'].startswith('multiverse')):
|
||||
print(prefix, 'binary and source package is in',
|
||||
section.split('/')[0])
|
||||
if src["Section"].startswith("universe") or src["Section"].startswith("multiverse"):
|
||||
print(prefix, "binary and source package is in", section.split("/")[0])
|
||||
return False
|
||||
else:
|
||||
print(prefix, 'is in', section.split('/')[0] + ', but its source',
|
||||
pkg.candidate.source_name,
|
||||
'is already in main; file an ubuntu-archive bug for '
|
||||
'promoting the current preferred alternative')
|
||||
print(
|
||||
prefix,
|
||||
"is in",
|
||||
section.split("/")[0] + ", but its source",
|
||||
pkg.candidate.source_name,
|
||||
"is already in main; file an ubuntu-archive bug for "
|
||||
"promoting the current preferred alternative",
|
||||
)
|
||||
return True
|
||||
|
||||
if alt:
|
||||
print(prefix, 'is already in main; consider preferring it')
|
||||
print(prefix, "is already in main; consider preferring it")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def check_build_dependencies(apt_cache, control):
|
||||
print('Checking support status of build dependencies...')
|
||||
print("Checking support status of build dependencies...")
|
||||
|
||||
any_unsupported = False
|
||||
|
||||
for field in ('Build-Depends', 'Build-Depends-Indep'):
|
||||
for field in ("Build-Depends", "Build-Depends-Indep"):
|
||||
if field not in control.section:
|
||||
continue
|
||||
for or_group in apt.apt_pkg.parse_src_depends(control.section[field]):
|
||||
@ -98,20 +99,19 @@ def check_build_dependencies(apt_cache, control):
|
||||
def check_binary_dependencies(apt_cache, control):
|
||||
any_unsupported = False
|
||||
|
||||
print('\nChecking support status of binary dependencies...')
|
||||
print("\nChecking support status of binary dependencies...")
|
||||
while True:
|
||||
try:
|
||||
next(control)
|
||||
except StopIteration:
|
||||
break
|
||||
|
||||
for field in ('Depends', 'Pre-Depends', 'Recommends'):
|
||||
for field in ("Depends", "Pre-Depends", "Recommends"):
|
||||
if field not in control.section:
|
||||
continue
|
||||
for or_group in apt.apt_pkg.parse_src_depends(
|
||||
control.section[field]):
|
||||
for or_group in apt.apt_pkg.parse_src_depends(control.section[field]):
|
||||
pkgname = or_group[0][0]
|
||||
if pkgname.startswith('$'):
|
||||
if pkgname.startswith("$"):
|
||||
continue
|
||||
if not check_support(apt_cache, pkgname):
|
||||
# check non-preferred alternatives
|
||||
@ -125,32 +125,38 @@ def check_binary_dependencies(apt_cache, control):
|
||||
|
||||
|
||||
def main():
|
||||
description = "Check if any of a package's build or binary " + \
|
||||
"dependencies are in universe or multiverse. " + \
|
||||
"Run this inside an unpacked source package"
|
||||
description = (
|
||||
"Check if any of a package's build or binary "
|
||||
+ "dependencies are in universe or multiverse. "
|
||||
+ "Run this inside an unpacked source package"
|
||||
)
|
||||
parser = optparse.OptionParser(description=description)
|
||||
parser.parse_args()
|
||||
apt_cache = apt.Cache()
|
||||
|
||||
if not os.path.exists('debian/control'):
|
||||
print('debian/control not found. You need to run this tool in a '
|
||||
'source package directory', file=sys.stderr)
|
||||
if not os.path.exists("debian/control"):
|
||||
print(
|
||||
"debian/control not found. You need to run this tool in a source package directory",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# get build dependencies from debian/control
|
||||
control = apt.apt_pkg.TagFile(open('debian/control'))
|
||||
control = apt.apt_pkg.TagFile(open("debian/control"))
|
||||
next(control)
|
||||
|
||||
unsupported_build_deps = check_build_dependencies(apt_cache, control)
|
||||
unsupported_binary_deps = check_binary_dependencies(apt_cache, control)
|
||||
|
||||
if unsupported_build_deps or unsupported_binary_deps:
|
||||
print('\nPlease check https://wiki.ubuntu.com/MainInclusionProcess if '
|
||||
'this source package needs to get into in main/restricted, or '
|
||||
'reconsider if the package really needs above dependencies.')
|
||||
print(
|
||||
"\nPlease check https://wiki.ubuntu.com/MainInclusionProcess if "
|
||||
"this source package needs to get into in main/restricted, or "
|
||||
"reconsider if the package really needs above dependencies."
|
||||
)
|
||||
else:
|
||||
print('All dependencies are supported in main or restricted.')
|
||||
print("All dependencies are supported in main or restricted.")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
@ -30,33 +30,33 @@ from ubuntutools.question import EditFile
|
||||
|
||||
|
||||
def main():
|
||||
parser = optparse.OptionParser('%prog [options] filename')
|
||||
parser = optparse.OptionParser("%prog [options] filename")
|
||||
options, args = parser.parse_args()
|
||||
|
||||
if len(args) != 1:
|
||||
parser.error('A filename must be specified')
|
||||
parser.error("A filename must be specified")
|
||||
body = args[0]
|
||||
if not os.path.isfile(body):
|
||||
parser.error('File %s does not exist' % body)
|
||||
parser.error("File %s does not exist" % body)
|
||||
|
||||
if 'UDT_EDIT_WRAPPER_EDITOR' in os.environ:
|
||||
os.environ['EDITOR'] = os.environ['UDT_EDIT_WRAPPER_EDITOR']
|
||||
if "UDT_EDIT_WRAPPER_EDITOR" in os.environ:
|
||||
os.environ["EDITOR"] = os.environ["UDT_EDIT_WRAPPER_EDITOR"]
|
||||
else:
|
||||
del os.environ['EDITOR']
|
||||
del os.environ["EDITOR"]
|
||||
|
||||
if 'UDT_EDIT_WRAPPER_VISUAL' in os.environ:
|
||||
os.environ['VISUAL'] = os.environ['UDT_EDIT_WRAPPER_VISUAL']
|
||||
if "UDT_EDIT_WRAPPER_VISUAL" in os.environ:
|
||||
os.environ["VISUAL"] = os.environ["UDT_EDIT_WRAPPER_VISUAL"]
|
||||
else:
|
||||
del os.environ['VISUAL']
|
||||
del os.environ["VISUAL"]
|
||||
|
||||
placeholders = []
|
||||
if 'UDT_EDIT_WRAPPER_TEMPLATE_RE' in os.environ:
|
||||
placeholders.append(re.compile(
|
||||
os.environ['UDT_EDIT_WRAPPER_TEMPLATE_RE']))
|
||||
if "UDT_EDIT_WRAPPER_TEMPLATE_RE" in os.environ:
|
||||
placeholders.append(re.compile(os.environ["UDT_EDIT_WRAPPER_TEMPLATE_RE"]))
|
||||
|
||||
description = os.environ.get('UDT_EDIT_WRAPPER_FILE_DESCRIPTION', 'file')
|
||||
description = os.environ.get("UDT_EDIT_WRAPPER_FILE_DESCRIPTION", "file")
|
||||
|
||||
EditFile(body, description, placeholders).edit()
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
56
grep-merges
56
grep-merges
@ -28,17 +28,19 @@ from httplib2 import Http, HttpLib2Error
|
||||
import ubuntutools.misc
|
||||
|
||||
from ubuntutools import getLogger
|
||||
|
||||
Logger = getLogger()
|
||||
|
||||
|
||||
def main():
|
||||
parser = optparse.OptionParser(
|
||||
usage='%prog [options] [string]',
|
||||
description='List pending merges from Debian matching string')
|
||||
usage="%prog [options] [string]",
|
||||
description="List pending merges from Debian matching string",
|
||||
)
|
||||
args = parser.parse_args()[1]
|
||||
|
||||
if len(args) > 1:
|
||||
parser.error('Too many arguments')
|
||||
parser.error("Too many arguments")
|
||||
elif len(args) == 1:
|
||||
match = args[0]
|
||||
else:
|
||||
@ -46,36 +48,46 @@ def main():
|
||||
|
||||
ubuntutools.misc.require_utf8()
|
||||
|
||||
for component in ('main', 'main-manual',
|
||||
'restricted', 'restricted-manual',
|
||||
'universe', 'universe-manual',
|
||||
'multiverse', 'multiverse-manual'):
|
||||
for component in (
|
||||
"main",
|
||||
"main-manual",
|
||||
"restricted",
|
||||
"restricted-manual",
|
||||
"universe",
|
||||
"universe-manual",
|
||||
"multiverse",
|
||||
"multiverse-manual",
|
||||
):
|
||||
|
||||
url = 'https://merges.ubuntu.com/%s.json' % component
|
||||
url = "https://merges.ubuntu.com/%s.json" % component
|
||||
try:
|
||||
headers, page = Http().request(url)
|
||||
except HttpLib2Error as e:
|
||||
Logger.exception(e)
|
||||
sys.exit(1)
|
||||
if headers.status != 200:
|
||||
Logger.error("%s: %s %s" % (url, headers.status,
|
||||
headers.reason))
|
||||
Logger.error("%s: %s %s" % (url, headers.status, headers.reason))
|
||||
sys.exit(1)
|
||||
|
||||
for merge in json.loads(page):
|
||||
package = merge['source_package']
|
||||
author, uploader = '', ''
|
||||
if merge.get('user'):
|
||||
author = merge['user']
|
||||
if merge.get('uploader'):
|
||||
uploader = '(%s)' % merge['uploader']
|
||||
teams = merge.get('teams', [])
|
||||
package = merge["source_package"]
|
||||
author, uploader = "", ""
|
||||
if merge.get("user"):
|
||||
author = merge["user"]
|
||||
if merge.get("uploader"):
|
||||
uploader = "(%s)" % merge["uploader"]
|
||||
teams = merge.get("teams", [])
|
||||
|
||||
pretty_uploader = '{} {}'.format(author, uploader)
|
||||
if (match is None or match in package or match in author
|
||||
or match in uploader or match in teams):
|
||||
Logger.info('%s\t%s' % (package, pretty_uploader))
|
||||
pretty_uploader = "{} {}".format(author, uploader)
|
||||
if (
|
||||
match is None
|
||||
or match in package
|
||||
or match in author
|
||||
or match in uploader
|
||||
or match in teams
|
||||
):
|
||||
Logger.info("%s\t%s" % (package, pretty_uploader))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
@ -33,6 +33,7 @@ from launchpadlib.launchpad import Launchpad
|
||||
from ubuntutools.config import UDTConfig
|
||||
|
||||
from ubuntutools import getLogger
|
||||
|
||||
Logger = getLogger()
|
||||
|
||||
|
||||
@ -40,19 +41,33 @@ def main():
|
||||
bug_re = re.compile(r"bug=(\d+)")
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-b", "--browserless", action="store_true",
|
||||
help="Don't open the bug in the browser at the end")
|
||||
parser.add_argument("-l", "--lpinstance", metavar="INSTANCE",
|
||||
help="LP instance to connect to (default: production)")
|
||||
parser.add_argument("-v", "--verbose", action="store_true",
|
||||
help="Print info about the bug being imported")
|
||||
parser.add_argument("-n", "--dry-run", action="store_true",
|
||||
help="Don't actually open a bug (also sets verbose)")
|
||||
parser.add_argument("-p", "--package",
|
||||
help="Launchpad package to file bug against "
|
||||
"(default: Same as Debian)")
|
||||
parser.add_argument("--no-conf", action="store_true",
|
||||
help="Don't read config files or environment variables.")
|
||||
parser.add_argument(
|
||||
"-b",
|
||||
"--browserless",
|
||||
action="store_true",
|
||||
help="Don't open the bug in the browser at the end",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--lpinstance",
|
||||
metavar="INSTANCE",
|
||||
help="LP instance to connect to (default: production)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v", "--verbose", action="store_true", help="Print info about the bug being imported"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--dry-run",
|
||||
action="store_true",
|
||||
help="Don't actually open a bug (also sets verbose)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p", "--package", help="Launchpad package to file bug against (default: Same as Debian)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-conf", action="store_true", help="Don't read config files or environment variables."
|
||||
)
|
||||
parser.add_argument("bugs", nargs="+", help="Bug number(s) or URL(s)")
|
||||
options = parser.parse_args()
|
||||
|
||||
@ -69,9 +84,9 @@ def main():
|
||||
if options.verbose:
|
||||
Logger.setLevel(logging.DEBUG)
|
||||
|
||||
debian = launchpad.distributions['debian']
|
||||
ubuntu = launchpad.distributions['ubuntu']
|
||||
lp_debbugs = launchpad.bug_trackers.getByName(name='debbugs')
|
||||
debian = launchpad.distributions["debian"]
|
||||
ubuntu = launchpad.distributions["ubuntu"]
|
||||
lp_debbugs = launchpad.bug_trackers.getByName(name="debbugs")
|
||||
|
||||
bug_nums = []
|
||||
|
||||
@ -101,31 +116,34 @@ def main():
|
||||
bug_num = bug.bug_num
|
||||
subject = bug.subject
|
||||
log = debianbts.get_bug_log(bug_num)
|
||||
summary = log[0]['message'].get_payload()
|
||||
summary = log[0]["message"].get_payload()
|
||||
target = ubuntu.getSourcePackage(name=ubupackage)
|
||||
if target is None:
|
||||
Logger.error("Source package '%s' is not in Ubuntu. Please specify "
|
||||
"the destination source package with --package",
|
||||
ubupackage)
|
||||
Logger.error(
|
||||
"Source package '%s' is not in Ubuntu. Please specify "
|
||||
"the destination source package with --package",
|
||||
ubupackage,
|
||||
)
|
||||
err = True
|
||||
continue
|
||||
|
||||
description = ('Imported from Debian bug http://bugs.debian.org/%d:\n\n%s' %
|
||||
(bug_num, summary))
|
||||
description = "Imported from Debian bug http://bugs.debian.org/%d:\n\n%s" % (
|
||||
bug_num,
|
||||
summary,
|
||||
)
|
||||
# LP limits descriptions to 50K chars
|
||||
description = (description[:49994] + ' [...]') if len(description) > 50000 else description
|
||||
description = (description[:49994] + " [...]") if len(description) > 50000 else description
|
||||
|
||||
Logger.debug('Target: %s' % target)
|
||||
Logger.debug('Subject: %s' % subject)
|
||||
Logger.debug('Description: ')
|
||||
Logger.debug("Target: %s" % target)
|
||||
Logger.debug("Subject: %s" % subject)
|
||||
Logger.debug("Description: ")
|
||||
Logger.debug(description)
|
||||
|
||||
if options.dry_run:
|
||||
Logger.info('Dry-Run: not creating Ubuntu bug.')
|
||||
Logger.info("Dry-Run: not creating Ubuntu bug.")
|
||||
continue
|
||||
|
||||
u_bug = launchpad.bugs.createBug(target=target, title=subject,
|
||||
description=description)
|
||||
u_bug = launchpad.bugs.createBug(target=target, title=subject, description=description)
|
||||
d_sp = debian.getSourcePackage(name=package)
|
||||
if d_sp is None and options.package:
|
||||
d_sp = debian.getSourcePackage(name=options.package)
|
||||
@ -141,5 +159,5 @@ def main():
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
@ -23,19 +23,23 @@ import sys
|
||||
from debian.changelog import Changelog
|
||||
|
||||
from ubuntutools import getLogger
|
||||
|
||||
Logger = getLogger()
|
||||
|
||||
|
||||
def usage(exit_code=1):
|
||||
Logger.info('''Usage: merge-changelog <left changelog> <right changelog>
|
||||
Logger.info(
|
||||
"""Usage: merge-changelog <left changelog> <right changelog>
|
||||
|
||||
merge-changelog takes two changelogs that once shared a common source,
|
||||
merges them back together, and prints the merged result to stdout. This
|
||||
is useful if you need to manually merge a ubuntu package with a new
|
||||
Debian release of the package.
|
||||
''')
|
||||
"""
|
||||
)
|
||||
sys.exit(exit_code)
|
||||
|
||||
|
||||
########################################################################
|
||||
# Changelog Management
|
||||
########################################################################
|
||||
@ -67,11 +71,11 @@ def merge_changelog(left_changelog, right_changelog):
|
||||
|
||||
assert block.version == version
|
||||
|
||||
Logger.info(str(block).strip() + ('\n' if ci else ''))
|
||||
Logger.info(str(block).strip() + ("\n" if ci else ""))
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) > 1 and sys.argv[1] in ('-h', '--help'):
|
||||
if len(sys.argv) > 1 and sys.argv[1] in ("-h", "--help"):
|
||||
usage(0)
|
||||
if len(sys.argv) != 3:
|
||||
usage(1)
|
||||
@ -83,5 +87,5 @@ def main():
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
380
pbuilder-dist
380
pbuilder-dist
@ -45,6 +45,7 @@ from ubuntutools.config import UDTConfig
|
||||
from ubuntutools.question import YesNoQuestion
|
||||
|
||||
from ubuntutools import getLogger
|
||||
|
||||
Logger = getLogger()
|
||||
|
||||
|
||||
@ -87,32 +88,32 @@ class PbuilderDist(object):
|
||||
self.chroot_string = None
|
||||
|
||||
# Authentication method
|
||||
self.auth = 'sudo'
|
||||
self.auth = "sudo"
|
||||
|
||||
# Builder
|
||||
self.builder = builder
|
||||
|
||||
self._debian_distros = DebianDistroInfo().all + \
|
||||
['stable', 'testing', 'unstable']
|
||||
self._debian_distros = DebianDistroInfo().all + ["stable", "testing", "unstable"]
|
||||
|
||||
# Ensure that the used builder is installed
|
||||
paths = set(os.environ['PATH'].split(':'))
|
||||
paths |= set(('/sbin', '/usr/sbin', '/usr/local/sbin'))
|
||||
paths = set(os.environ["PATH"].split(":"))
|
||||
paths |= set(("/sbin", "/usr/sbin", "/usr/local/sbin"))
|
||||
if not any(os.path.exists(os.path.join(p, builder)) for p in paths):
|
||||
Logger.error('Could not find "%s".', builder)
|
||||
sys.exit(1)
|
||||
|
||||
##############################################################
|
||||
|
||||
self.base = os.path.expanduser(os.environ.get('PBUILDFOLDER',
|
||||
'~/pbuilder/'))
|
||||
self.base = os.path.expanduser(os.environ.get("PBUILDFOLDER", "~/pbuilder/"))
|
||||
|
||||
if 'SUDO_USER' in os.environ:
|
||||
Logger.warning('Running under sudo. '
|
||||
'This is probably not what you want. '
|
||||
'pbuilder-dist will use sudo itself, '
|
||||
'when necessary.')
|
||||
if os.stat(os.environ['HOME']).st_uid != os.getuid():
|
||||
if "SUDO_USER" in os.environ:
|
||||
Logger.warning(
|
||||
"Running under sudo. "
|
||||
"This is probably not what you want. "
|
||||
"pbuilder-dist will use sudo itself, "
|
||||
"when necessary."
|
||||
)
|
||||
if os.stat(os.environ["HOME"]).st_uid != os.getuid():
|
||||
Logger.error("You don't own $HOME")
|
||||
sys.exit(1)
|
||||
|
||||
@ -123,8 +124,8 @@ class PbuilderDist(object):
|
||||
Logger.error('Cannot create base directory "%s"', self.base)
|
||||
sys.exit(1)
|
||||
|
||||
if 'PBUILDAUTH' in os.environ:
|
||||
self.auth = os.environ['PBUILDAUTH']
|
||||
if "PBUILDAUTH" in os.environ:
|
||||
self.auth = os.environ["PBUILDAUTH"]
|
||||
|
||||
self.system_architecture = ubuntutools.misc.host_architecture()
|
||||
self.system_distro = ubuntutools.misc.system_distribution()
|
||||
@ -134,7 +135,7 @@ class PbuilderDist(object):
|
||||
self.target_distro = self.system_distro
|
||||
|
||||
def set_target_distro(self, distro):
|
||||
""" PbuilderDist.set_target_distro(distro) -> None
|
||||
"""PbuilderDist.set_target_distro(distro) -> None
|
||||
|
||||
Check if the given target distribution name is correct, if it
|
||||
isn't know to the system ask the user for confirmation before
|
||||
@ -145,16 +146,16 @@ class PbuilderDist(object):
|
||||
Logger.error('"%s" is an invalid distribution codename.', distro)
|
||||
sys.exit(1)
|
||||
|
||||
if not os.path.isfile(os.path.join('/usr/share/debootstrap/scripts/',
|
||||
distro)):
|
||||
if os.path.isdir('/usr/share/debootstrap/scripts/'):
|
||||
if not os.path.isfile(os.path.join("/usr/share/debootstrap/scripts/", distro)):
|
||||
if os.path.isdir("/usr/share/debootstrap/scripts/"):
|
||||
# Debian experimental doesn't have a debootstrap file but
|
||||
# should work nevertheless.
|
||||
if distro not in self._debian_distros:
|
||||
question = ('Warning: Unknown distribution "%s". '
|
||||
'Do you want to continue' % distro)
|
||||
answer = YesNoQuestion().ask(question, 'no')
|
||||
if answer == 'no':
|
||||
question = (
|
||||
'Warning: Unknown distribution "%s". ' "Do you want to continue" % distro
|
||||
)
|
||||
answer = YesNoQuestion().ask(question, "no")
|
||||
if answer == "no":
|
||||
sys.exit(0)
|
||||
else:
|
||||
Logger.error('Please install package "debootstrap".')
|
||||
@ -163,33 +164,35 @@ class PbuilderDist(object):
|
||||
self.target_distro = distro
|
||||
|
||||
def set_operation(self, operation):
|
||||
""" PbuilderDist.set_operation -> None
|
||||
"""PbuilderDist.set_operation -> None
|
||||
|
||||
Check if the given string is a valid pbuilder operation and
|
||||
depending on this either save it into the appropiate variable
|
||||
or finalize pbuilder-dist's execution.
|
||||
"""
|
||||
arguments = ('create', 'update', 'build', 'clean', 'login', 'execute')
|
||||
arguments = ("create", "update", "build", "clean", "login", "execute")
|
||||
|
||||
if operation not in arguments:
|
||||
if operation.endswith('.dsc'):
|
||||
if operation.endswith(".dsc"):
|
||||
if os.path.isfile(operation):
|
||||
self.operation = 'build'
|
||||
self.operation = "build"
|
||||
return [operation]
|
||||
else:
|
||||
Logger.error('Could not find file "%s".', operation)
|
||||
sys.exit(1)
|
||||
else:
|
||||
Logger.error('"%s" is not a recognized argument.\n'
|
||||
'Please use one of these: %s.',
|
||||
operation, ', '.join(arguments))
|
||||
Logger.error(
|
||||
'"%s" is not a recognized argument.\nPlease use one of these: %s.',
|
||||
operation,
|
||||
", ".join(arguments),
|
||||
)
|
||||
sys.exit(1)
|
||||
else:
|
||||
self.operation = operation
|
||||
return []
|
||||
|
||||
def get_command(self, remaining_arguments=None):
|
||||
""" PbuilderDist.get_command -> string
|
||||
"""PbuilderDist.get_command -> string
|
||||
|
||||
Generate the pbuilder command which matches the given configuration
|
||||
and return it as a string.
|
||||
@ -200,30 +203,34 @@ class PbuilderDist(object):
|
||||
if self.build_architecture == self.system_architecture:
|
||||
self.chroot_string = self.target_distro
|
||||
else:
|
||||
self.chroot_string = (self.target_distro + '-'
|
||||
+ self.build_architecture)
|
||||
self.chroot_string = self.target_distro + "-" + self.build_architecture
|
||||
|
||||
prefix = os.path.join(self.base, self.chroot_string)
|
||||
if '--buildresult' not in remaining_arguments:
|
||||
result = os.path.normpath('%s_result/' % prefix)
|
||||
if "--buildresult" not in remaining_arguments:
|
||||
result = os.path.normpath("%s_result/" % prefix)
|
||||
else:
|
||||
location_of_arg = remaining_arguments.index('--buildresult')
|
||||
result = os.path.normpath(remaining_arguments[location_of_arg+1])
|
||||
remaining_arguments.pop(location_of_arg+1)
|
||||
location_of_arg = remaining_arguments.index("--buildresult")
|
||||
result = os.path.normpath(remaining_arguments[location_of_arg + 1])
|
||||
remaining_arguments.pop(location_of_arg + 1)
|
||||
remaining_arguments.pop(location_of_arg)
|
||||
|
||||
if not self.logfile and self.operation != 'login':
|
||||
if self.operation == 'build':
|
||||
dsc_files = [a for a in remaining_arguments
|
||||
if a.strip().endswith('.dsc')]
|
||||
if not self.logfile and self.operation != "login":
|
||||
if self.operation == "build":
|
||||
dsc_files = [a for a in remaining_arguments if a.strip().endswith(".dsc")]
|
||||
assert len(dsc_files) == 1
|
||||
dsc = debian.deb822.Dsc(open(dsc_files[0]))
|
||||
version = ubuntutools.version.Version(dsc['Version'])
|
||||
name = (dsc['Source'] + '_' + version.strip_epoch() + '_' +
|
||||
self.build_architecture + '.build')
|
||||
version = ubuntutools.version.Version(dsc["Version"])
|
||||
name = (
|
||||
dsc["Source"]
|
||||
+ "_"
|
||||
+ version.strip_epoch()
|
||||
+ "_"
|
||||
+ self.build_architecture
|
||||
+ ".build"
|
||||
)
|
||||
self.logfile = os.path.join(result, name)
|
||||
else:
|
||||
self.logfile = os.path.join(result, 'last_operation.log')
|
||||
self.logfile = os.path.join(result, "last_operation.log")
|
||||
|
||||
if not os.path.isdir(result):
|
||||
try:
|
||||
@ -233,87 +240,89 @@ class PbuilderDist(object):
|
||||
sys.exit(1)
|
||||
|
||||
arguments = [
|
||||
'--%s' % self.operation,
|
||||
'--distribution', self.target_distro,
|
||||
'--buildresult', result,
|
||||
"--%s" % self.operation,
|
||||
"--distribution",
|
||||
self.target_distro,
|
||||
"--buildresult",
|
||||
result,
|
||||
]
|
||||
|
||||
if self.operation == 'update':
|
||||
arguments += ['--override-config']
|
||||
if self.operation == "update":
|
||||
arguments += ["--override-config"]
|
||||
|
||||
if self.builder == 'pbuilder':
|
||||
arguments += ['--basetgz', prefix + '-base.tgz']
|
||||
elif self.builder == 'cowbuilder':
|
||||
arguments += ['--basepath', prefix + '-base.cow']
|
||||
if self.builder == "pbuilder":
|
||||
arguments += ["--basetgz", prefix + "-base.tgz"]
|
||||
elif self.builder == "cowbuilder":
|
||||
arguments += ["--basepath", prefix + "-base.cow"]
|
||||
else:
|
||||
Logger.error('Unrecognized builder "%s".', self.builder)
|
||||
sys.exit(1)
|
||||
|
||||
if self.logfile:
|
||||
arguments += ['--logfile', self.logfile]
|
||||
arguments += ["--logfile", self.logfile]
|
||||
|
||||
if os.path.exists('/var/cache/archive/'):
|
||||
arguments += ['--bindmounts', '/var/cache/archive/']
|
||||
if os.path.exists("/var/cache/archive/"):
|
||||
arguments += ["--bindmounts", "/var/cache/archive/"]
|
||||
|
||||
config = UDTConfig()
|
||||
if self.target_distro in self._debian_distros:
|
||||
mirror = os.environ.get('MIRRORSITE',
|
||||
config.get_value('DEBIAN_MIRROR'))
|
||||
components = 'main'
|
||||
mirror = os.environ.get("MIRRORSITE", config.get_value("DEBIAN_MIRROR"))
|
||||
components = "main"
|
||||
if self.extra_components:
|
||||
components += ' contrib non-free'
|
||||
components += " contrib non-free"
|
||||
else:
|
||||
mirror = os.environ.get('MIRRORSITE',
|
||||
config.get_value('UBUNTU_MIRROR'))
|
||||
if self.build_architecture not in ('amd64', 'i386'):
|
||||
mirror = os.environ.get(
|
||||
'MIRRORSITE', config.get_value('UBUNTU_PORTS_MIRROR'))
|
||||
components = 'main restricted'
|
||||
mirror = os.environ.get("MIRRORSITE", config.get_value("UBUNTU_MIRROR"))
|
||||
if self.build_architecture not in ("amd64", "i386"):
|
||||
mirror = os.environ.get("MIRRORSITE", config.get_value("UBUNTU_PORTS_MIRROR"))
|
||||
components = "main restricted"
|
||||
if self.extra_components:
|
||||
components += ' universe multiverse'
|
||||
components += " universe multiverse"
|
||||
|
||||
arguments += ['--mirror', mirror]
|
||||
arguments += ["--mirror", mirror]
|
||||
|
||||
othermirrors = []
|
||||
localrepo = '/var/cache/archive/' + self.target_distro
|
||||
localrepo = "/var/cache/archive/" + self.target_distro
|
||||
if os.path.exists(localrepo):
|
||||
repo = 'deb file:///var/cache/archive/ %s/' % self.target_distro
|
||||
repo = "deb file:///var/cache/archive/ %s/" % self.target_distro
|
||||
othermirrors.append(repo)
|
||||
|
||||
if self.target_distro in self._debian_distros:
|
||||
debian_info = DebianDistroInfo()
|
||||
try:
|
||||
codename = debian_info.codename(self.target_distro,
|
||||
default=self.target_distro)
|
||||
codename = debian_info.codename(self.target_distro, default=self.target_distro)
|
||||
except DistroDataOutdated as error:
|
||||
Logger.warning(error)
|
||||
if codename in (debian_info.devel(), 'experimental'):
|
||||
if codename in (debian_info.devel(), "experimental"):
|
||||
self.enable_security = False
|
||||
self.enable_updates = False
|
||||
self.enable_proposed = False
|
||||
elif codename in (debian_info.testing(), 'testing'):
|
||||
elif codename in (debian_info.testing(), "testing"):
|
||||
self.enable_updates = False
|
||||
|
||||
if self.enable_security:
|
||||
pocket = '-security'
|
||||
pocket = "-security"
|
||||
with suppress(ValueError):
|
||||
# before bullseye (version 11) security suite is /updates
|
||||
if float(debian_info.version(codename)) < 11.0:
|
||||
pocket = '/updates'
|
||||
othermirrors.append('deb %s %s%s %s'
|
||||
% (config.get_value('DEBSEC_MIRROR'),
|
||||
self.target_distro, pocket, components))
|
||||
pocket = "/updates"
|
||||
othermirrors.append(
|
||||
"deb %s %s%s %s"
|
||||
% (config.get_value("DEBSEC_MIRROR"), self.target_distro, pocket, components)
|
||||
)
|
||||
if self.enable_updates:
|
||||
othermirrors.append('deb %s %s-updates %s'
|
||||
% (mirror, self.target_distro, components))
|
||||
othermirrors.append(
|
||||
"deb %s %s-updates %s" % (mirror, self.target_distro, components)
|
||||
)
|
||||
if self.enable_proposed:
|
||||
othermirrors.append('deb %s %s-proposed-updates %s'
|
||||
% (mirror, self.target_distro, components))
|
||||
othermirrors.append(
|
||||
"deb %s %s-proposed-updates %s" % (mirror, self.target_distro, components)
|
||||
)
|
||||
if self.enable_backports:
|
||||
othermirrors.append('deb %s %s-backports %s'
|
||||
% (mirror, self.target_distro, components))
|
||||
othermirrors.append(
|
||||
"deb %s %s-backports %s" % (mirror, self.target_distro, components)
|
||||
)
|
||||
|
||||
aptcache = os.path.join(self.base, 'aptcache', 'debian')
|
||||
aptcache = os.path.join(self.base, "aptcache", "debian")
|
||||
else:
|
||||
try:
|
||||
dev_release = self.target_distro == UbuntuDistroInfo().devel()
|
||||
@ -326,46 +335,51 @@ class PbuilderDist(object):
|
||||
self.enable_updates = False
|
||||
|
||||
if self.enable_security:
|
||||
othermirrors.append('deb %s %s-security %s'
|
||||
% (mirror, self.target_distro, components))
|
||||
othermirrors.append(
|
||||
"deb %s %s-security %s" % (mirror, self.target_distro, components)
|
||||
)
|
||||
if self.enable_updates:
|
||||
othermirrors.append('deb %s %s-updates %s'
|
||||
% (mirror, self.target_distro, components))
|
||||
othermirrors.append(
|
||||
"deb %s %s-updates %s" % (mirror, self.target_distro, components)
|
||||
)
|
||||
if self.enable_proposed:
|
||||
othermirrors.append('deb %s %s-proposed %s'
|
||||
% (mirror, self.target_distro, components))
|
||||
othermirrors.append(
|
||||
"deb %s %s-proposed %s" % (mirror, self.target_distro, components)
|
||||
)
|
||||
|
||||
aptcache = os.path.join(self.base, 'aptcache', 'ubuntu')
|
||||
aptcache = os.path.join(self.base, "aptcache", "ubuntu")
|
||||
|
||||
if 'OTHERMIRROR' in os.environ:
|
||||
othermirrors += os.environ['OTHERMIRROR'].split('|')
|
||||
if "OTHERMIRROR" in os.environ:
|
||||
othermirrors += os.environ["OTHERMIRROR"].split("|")
|
||||
|
||||
if othermirrors:
|
||||
arguments += ['--othermirror', '|'.join(othermirrors)]
|
||||
arguments += ["--othermirror", "|".join(othermirrors)]
|
||||
|
||||
# Work around LP:#599695
|
||||
if (ubuntutools.misc.system_distribution() == 'Debian'
|
||||
and self.target_distro not in self._debian_distros):
|
||||
if not os.path.exists(
|
||||
'/usr/share/keyrings/ubuntu-archive-keyring.gpg'):
|
||||
Logger.error('ubuntu-keyring not installed')
|
||||
if (
|
||||
ubuntutools.misc.system_distribution() == "Debian"
|
||||
and self.target_distro not in self._debian_distros
|
||||
):
|
||||
if not os.path.exists("/usr/share/keyrings/ubuntu-archive-keyring.gpg"):
|
||||
Logger.error("ubuntu-keyring not installed")
|
||||
sys.exit(1)
|
||||
arguments += [
|
||||
'--debootstrapopts',
|
||||
'--keyring=/usr/share/keyrings/ubuntu-archive-keyring.gpg',
|
||||
"--debootstrapopts",
|
||||
"--keyring=/usr/share/keyrings/ubuntu-archive-keyring.gpg",
|
||||
]
|
||||
elif (ubuntutools.misc.system_distribution() == 'Ubuntu'
|
||||
and self.target_distro in self._debian_distros):
|
||||
if not os.path.exists(
|
||||
'/usr/share/keyrings/debian-archive-keyring.gpg'):
|
||||
Logger.error('debian-archive-keyring not installed')
|
||||
elif (
|
||||
ubuntutools.misc.system_distribution() == "Ubuntu"
|
||||
and self.target_distro in self._debian_distros
|
||||
):
|
||||
if not os.path.exists("/usr/share/keyrings/debian-archive-keyring.gpg"):
|
||||
Logger.error("debian-archive-keyring not installed")
|
||||
sys.exit(1)
|
||||
arguments += [
|
||||
'--debootstrapopts',
|
||||
'--keyring=/usr/share/keyrings/debian-archive-keyring.gpg',
|
||||
"--debootstrapopts",
|
||||
"--keyring=/usr/share/keyrings/debian-archive-keyring.gpg",
|
||||
]
|
||||
|
||||
arguments += ['--aptcache', aptcache, '--components', components]
|
||||
arguments += ["--aptcache", aptcache, "--components", components]
|
||||
|
||||
if not os.path.isdir(aptcache):
|
||||
try:
|
||||
@ -375,13 +389,11 @@ class PbuilderDist(object):
|
||||
sys.exit(1)
|
||||
|
||||
if self.build_architecture != self.system_architecture:
|
||||
arguments += ['--debootstrapopts',
|
||||
'--arch=' + self.build_architecture]
|
||||
arguments += ["--debootstrapopts", "--arch=" + self.build_architecture]
|
||||
|
||||
apt_conf_dir = os.path.join(self.base,
|
||||
'etc/%s/apt.conf' % self.target_distro)
|
||||
apt_conf_dir = os.path.join(self.base, "etc/%s/apt.conf" % self.target_distro)
|
||||
if os.path.exists(apt_conf_dir):
|
||||
arguments += ['--aptconfdir', apt_conf_dir]
|
||||
arguments += ["--aptconfdir", apt_conf_dir]
|
||||
|
||||
# Append remaining arguments
|
||||
if remaining_arguments:
|
||||
@ -392,28 +404,28 @@ class PbuilderDist(object):
|
||||
# With both common variable name schemes (BTS: #659060).
|
||||
return [
|
||||
self.auth,
|
||||
'HOME=' + os.path.expanduser('~'),
|
||||
'ARCHITECTURE=' + self.build_architecture,
|
||||
'DISTRIBUTION=' + self.target_distro,
|
||||
'ARCH=' + self.build_architecture,
|
||||
'DIST=' + self.target_distro,
|
||||
'DEB_BUILD_OPTIONS=' + os.environ.get('DEB_BUILD_OPTIONS', ''),
|
||||
"HOME=" + os.path.expanduser("~"),
|
||||
"ARCHITECTURE=" + self.build_architecture,
|
||||
"DISTRIBUTION=" + self.target_distro,
|
||||
"ARCH=" + self.build_architecture,
|
||||
"DIST=" + self.target_distro,
|
||||
"DEB_BUILD_OPTIONS=" + os.environ.get("DEB_BUILD_OPTIONS", ""),
|
||||
self.builder,
|
||||
] + arguments
|
||||
|
||||
|
||||
def show_help(exit_code=0):
|
||||
""" help() -> None
|
||||
"""help() -> None
|
||||
|
||||
Print a help message for pbuilder-dist, and exit with the given code.
|
||||
"""
|
||||
Logger.info('See man pbuilder-dist for more information.')
|
||||
Logger.info("See man pbuilder-dist for more information.")
|
||||
|
||||
sys.exit(exit_code)
|
||||
|
||||
|
||||
def main():
|
||||
""" main() -> None
|
||||
"""main() -> None
|
||||
|
||||
This is pbuilder-dist's main function. It creates a PbuilderDist
|
||||
object, modifies all necessary settings taking data from the
|
||||
@ -421,27 +433,25 @@ def main():
|
||||
the script and runs pbuilder itself or exists with an error message.
|
||||
"""
|
||||
script_name = os.path.basename(sys.argv[0])
|
||||
parts = script_name.split('-')
|
||||
parts = script_name.split("-")
|
||||
|
||||
# Copy arguments into another list for save manipulation
|
||||
args = sys.argv[1:]
|
||||
|
||||
if ('-' in script_name and parts[0] not in ('pbuilder', 'cowbuilder')
|
||||
or len(parts) > 3):
|
||||
Logger.error('"%s" is not a valid name for a "pbuilder-dist" '
|
||||
'executable.', script_name)
|
||||
if "-" in script_name and parts[0] not in ("pbuilder", "cowbuilder") or len(parts) > 3:
|
||||
Logger.error('"%s" is not a valid name for a "pbuilder-dist" executable.', script_name)
|
||||
sys.exit(1)
|
||||
|
||||
if len(args) < 1:
|
||||
Logger.error('Insufficient number of arguments.')
|
||||
Logger.error("Insufficient number of arguments.")
|
||||
show_help(1)
|
||||
|
||||
if args[0] in ('-h', '--help', 'help'):
|
||||
if args[0] in ("-h", "--help", "help"):
|
||||
show_help(0)
|
||||
|
||||
app = PbuilderDist(parts[0])
|
||||
|
||||
if len(parts) > 1 and parts[1] != 'dist' and '.' not in parts[1]:
|
||||
if len(parts) > 1 and parts[1] != "dist" and "." not in parts[1]:
|
||||
app.set_target_distro(parts[1])
|
||||
else:
|
||||
app.set_target_distro(args.pop(0))
|
||||
@ -449,24 +459,28 @@ def main():
|
||||
if len(parts) > 2:
|
||||
requested_arch = parts[2]
|
||||
elif len(args) > 0:
|
||||
if shutil.which('arch-test') is not None:
|
||||
if subprocess.run(
|
||||
['arch-test', args[0]],
|
||||
stdout=subprocess.DEVNULL).returncode == 0:
|
||||
if shutil.which("arch-test") is not None:
|
||||
if subprocess.run(["arch-test", args[0]], stdout=subprocess.DEVNULL).returncode == 0:
|
||||
requested_arch = args.pop(0)
|
||||
elif (os.path.isdir('/usr/lib/arch-test')
|
||||
and args[0] in os.listdir('/usr/lib/arch-test/')):
|
||||
Logger.error('Architecture "%s" is not supported on your '
|
||||
'currently running kernel. Consider installing '
|
||||
'the qemu-user-static package to enable the use of '
|
||||
'foreign architectures.', args[0])
|
||||
elif os.path.isdir("/usr/lib/arch-test") and args[0] in os.listdir(
|
||||
"/usr/lib/arch-test/"
|
||||
):
|
||||
Logger.error(
|
||||
'Architecture "%s" is not supported on your '
|
||||
"currently running kernel. Consider installing "
|
||||
"the qemu-user-static package to enable the use of "
|
||||
"foreign architectures.",
|
||||
args[0],
|
||||
)
|
||||
sys.exit(1)
|
||||
else:
|
||||
requested_arch = None
|
||||
else:
|
||||
Logger.error('Cannot determine if "%s" is a valid architecture. '
|
||||
'Please install the arch-test package and retry.',
|
||||
args[0])
|
||||
Logger.error(
|
||||
'Cannot determine if "%s" is a valid architecture. '
|
||||
"Please install the arch-test package and retry.",
|
||||
args[0],
|
||||
)
|
||||
sys.exit(1)
|
||||
else:
|
||||
requested_arch = None
|
||||
@ -474,62 +488,74 @@ def main():
|
||||
if requested_arch:
|
||||
app.build_architecture = requested_arch
|
||||
# For some foreign architectures we need to use qemu
|
||||
if (requested_arch != app.system_architecture
|
||||
and (app.system_architecture, requested_arch) not in [
|
||||
('amd64', 'i386'), ('amd64', 'lpia'), ('arm', 'armel'),
|
||||
('armel', 'arm'), ('armel', 'armhf'), ('armhf', 'armel'),
|
||||
('arm64', 'arm'), ('arm64', 'armhf'), ('arm64', 'armel'),
|
||||
('i386', 'lpia'), ('lpia', 'i386'), ('powerpc', 'ppc64'),
|
||||
('ppc64', 'powerpc'), ('sparc', 'sparc64'),
|
||||
('sparc64', 'sparc')]):
|
||||
args += ['--debootstrap', 'qemu-debootstrap']
|
||||
if requested_arch != app.system_architecture and (
|
||||
app.system_architecture,
|
||||
requested_arch,
|
||||
) not in [
|
||||
("amd64", "i386"),
|
||||
("amd64", "lpia"),
|
||||
("arm", "armel"),
|
||||
("armel", "arm"),
|
||||
("armel", "armhf"),
|
||||
("armhf", "armel"),
|
||||
("arm64", "arm"),
|
||||
("arm64", "armhf"),
|
||||
("arm64", "armel"),
|
||||
("i386", "lpia"),
|
||||
("lpia", "i386"),
|
||||
("powerpc", "ppc64"),
|
||||
("ppc64", "powerpc"),
|
||||
("sparc", "sparc64"),
|
||||
("sparc64", "sparc"),
|
||||
]:
|
||||
args += ["--debootstrap", "qemu-debootstrap"]
|
||||
|
||||
if 'mainonly' in sys.argv or '--main-only' in sys.argv:
|
||||
if "mainonly" in sys.argv or "--main-only" in sys.argv:
|
||||
app.extra_components = False
|
||||
if 'mainonly' in sys.argv:
|
||||
args.remove('mainonly')
|
||||
if "mainonly" in sys.argv:
|
||||
args.remove("mainonly")
|
||||
else:
|
||||
args.remove('--main-only')
|
||||
args.remove("--main-only")
|
||||
|
||||
if '--release-only' in sys.argv:
|
||||
args.remove('--release-only')
|
||||
if "--release-only" in sys.argv:
|
||||
args.remove("--release-only")
|
||||
app.enable_security = False
|
||||
app.enable_updates = False
|
||||
app.enable_proposed = False
|
||||
elif '--security-only' in sys.argv:
|
||||
args.remove('--security-only')
|
||||
elif "--security-only" in sys.argv:
|
||||
args.remove("--security-only")
|
||||
app.enable_updates = False
|
||||
app.enable_proposed = False
|
||||
elif '--updates-only' in sys.argv:
|
||||
args.remove('--updates-only')
|
||||
elif "--updates-only" in sys.argv:
|
||||
args.remove("--updates-only")
|
||||
app.enable_proposed = False
|
||||
elif '--backports' in sys.argv:
|
||||
args.remove('--backports')
|
||||
elif "--backports" in sys.argv:
|
||||
args.remove("--backports")
|
||||
app.enable_backports = True
|
||||
|
||||
if len(args) < 1:
|
||||
Logger.error('Insufficient number of arguments.')
|
||||
Logger.error("Insufficient number of arguments.")
|
||||
show_help(1)
|
||||
|
||||
# Parse the operation
|
||||
args = app.set_operation(args.pop(0)) + args
|
||||
|
||||
if app.operation == 'build':
|
||||
if len([a for a in args if a.strip().endswith('.dsc')]) != 1:
|
||||
msg = 'You have to specify one .dsc file if you want to build.'
|
||||
if app.operation == "build":
|
||||
if len([a for a in args if a.strip().endswith(".dsc")]) != 1:
|
||||
msg = "You have to specify one .dsc file if you want to build."
|
||||
Logger.error(msg)
|
||||
sys.exit(1)
|
||||
|
||||
# Execute the pbuilder command
|
||||
if '--debug-echo' not in args:
|
||||
if "--debug-echo" not in args:
|
||||
sys.exit(subprocess.call(app.get_command(args)))
|
||||
else:
|
||||
Logger.info(app.get_command([arg for arg in args if arg != '--debug-echo']))
|
||||
Logger.info(app.get_command([arg for arg in args if arg != "--debug-echo"]))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
Logger.error('Manually aborted.')
|
||||
Logger.error("Manually aborted.")
|
||||
sys.exit(1)
|
||||
|
@ -7,5 +7,5 @@
|
||||
|
||||
from ubuntutools.pullpkg import PullPkg
|
||||
|
||||
if __name__ == '__main__':
|
||||
PullPkg.main(distro='debian', pull='ddebs')
|
||||
if __name__ == "__main__":
|
||||
PullPkg.main(distro="debian", pull="ddebs")
|
||||
|
@ -27,19 +27,20 @@ from ubuntutools.config import UDTConfig
|
||||
from ubuntutools.version import Version
|
||||
|
||||
from ubuntutools import getLogger
|
||||
|
||||
Logger = getLogger()
|
||||
|
||||
|
||||
def previous_version(package, version, distance):
|
||||
"Given an (extracted) package, determine the version distance versions ago"
|
||||
upver = Version(version).upstream_version
|
||||
filename = '%s-%s/debian/changelog' % (package, upver)
|
||||
changelog_file = open(filename, 'r')
|
||||
filename = "%s-%s/debian/changelog" % (package, upver)
|
||||
changelog_file = open(filename, "r")
|
||||
changelog = debian.changelog.Changelog(changelog_file.read())
|
||||
changelog_file.close()
|
||||
seen = 0
|
||||
for entry in changelog:
|
||||
if entry.distributions == 'UNRELEASED':
|
||||
if entry.distributions == "UNRELEASED":
|
||||
continue
|
||||
if seen == distance:
|
||||
return entry.version.full_version
|
||||
@ -48,46 +49,60 @@ def previous_version(package, version, distance):
|
||||
|
||||
|
||||
def main():
|
||||
parser = optparse.OptionParser('%prog [options] <package> <version> '
|
||||
'[distance]')
|
||||
parser.add_option('-f', '--fetch',
|
||||
dest='fetch_only', default=False, action='store_true',
|
||||
help="Only fetch the source packages, don't diff.")
|
||||
parser.add_option('-d', '--debian-mirror', metavar='DEBIAN_MIRROR',
|
||||
dest='debian_mirror',
|
||||
help='Preferred Debian mirror '
|
||||
'(default: http://deb.debian.org/debian)')
|
||||
parser.add_option('-s', '--debsec-mirror', metavar='DEBSEC_MIRROR',
|
||||
dest='debsec_mirror',
|
||||
help='Preferred Debian Security mirror '
|
||||
'(default: http://security.debian.org)')
|
||||
parser.add_option('--no-conf',
|
||||
dest='no_conf', default=False, action='store_true',
|
||||
help="Don't read config files or environment variables")
|
||||
parser = optparse.OptionParser("%prog [options] <package> <version> [distance]")
|
||||
parser.add_option(
|
||||
"-f",
|
||||
"--fetch",
|
||||
dest="fetch_only",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Only fetch the source packages, don't diff.",
|
||||
)
|
||||
parser.add_option(
|
||||
"-d",
|
||||
"--debian-mirror",
|
||||
metavar="DEBIAN_MIRROR",
|
||||
dest="debian_mirror",
|
||||
help="Preferred Debian mirror (default: http://deb.debian.org/debian)",
|
||||
)
|
||||
parser.add_option(
|
||||
"-s",
|
||||
"--debsec-mirror",
|
||||
metavar="DEBSEC_MIRROR",
|
||||
dest="debsec_mirror",
|
||||
help="Preferred Debian Security mirror (default: http://security.debian.org)",
|
||||
)
|
||||
parser.add_option(
|
||||
"--no-conf",
|
||||
dest="no_conf",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Don't read config files or environment variables",
|
||||
)
|
||||
|
||||
opts, args = parser.parse_args()
|
||||
if len(args) < 2:
|
||||
parser.error('Must specify package and version')
|
||||
parser.error("Must specify package and version")
|
||||
elif len(args) > 3:
|
||||
parser.error('Too many arguments')
|
||||
parser.error("Too many arguments")
|
||||
package = args[0]
|
||||
version = args[1]
|
||||
distance = int(args[2]) if len(args) > 2 else 1
|
||||
|
||||
config = UDTConfig(opts.no_conf)
|
||||
if opts.debian_mirror is None:
|
||||
opts.debian_mirror = config.get_value('DEBIAN_MIRROR')
|
||||
opts.debian_mirror = config.get_value("DEBIAN_MIRROR")
|
||||
if opts.debsec_mirror is None:
|
||||
opts.debsec_mirror = config.get_value('DEBSEC_MIRROR')
|
||||
opts.debsec_mirror = config.get_value("DEBSEC_MIRROR")
|
||||
mirrors = [opts.debsec_mirror, opts.debian_mirror]
|
||||
|
||||
Logger.info('Downloading %s %s', package, version)
|
||||
Logger.info("Downloading %s %s", package, version)
|
||||
|
||||
newpkg = DebianSourcePackage(package, version, mirrors=mirrors)
|
||||
try:
|
||||
newpkg.pull()
|
||||
except DownloadError as e:
|
||||
Logger.error('Failed to download: %s', str(e))
|
||||
Logger.error("Failed to download: %s", str(e))
|
||||
sys.exit(1)
|
||||
newpkg.unpack()
|
||||
|
||||
@ -96,21 +111,21 @@ def main():
|
||||
|
||||
oldversion = previous_version(package, version, distance)
|
||||
if not oldversion:
|
||||
Logger.error('No previous version could be found')
|
||||
Logger.error("No previous version could be found")
|
||||
sys.exit(1)
|
||||
Logger.info('Downloading %s %s', package, oldversion)
|
||||
Logger.info("Downloading %s %s", package, oldversion)
|
||||
|
||||
oldpkg = DebianSourcePackage(package, oldversion, mirrors=mirrors)
|
||||
try:
|
||||
oldpkg.pull()
|
||||
except DownloadError as e:
|
||||
Logger.error('Failed to download: %s', str(e))
|
||||
Logger.error("Failed to download: %s", str(e))
|
||||
sys.exit(1)
|
||||
Logger.info('file://' + oldpkg.debdiff(newpkg, diffstat=True))
|
||||
Logger.info("file://" + oldpkg.debdiff(newpkg, diffstat=True))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
Logger.info('User abort.')
|
||||
Logger.info("User abort.")
|
||||
|
@ -7,5 +7,5 @@
|
||||
|
||||
from ubuntutools.pullpkg import PullPkg
|
||||
|
||||
if __name__ == '__main__':
|
||||
PullPkg.main(distro='debian', pull='debs')
|
||||
if __name__ == "__main__":
|
||||
PullPkg.main(distro="debian", pull="debs")
|
||||
|
@ -7,5 +7,5 @@
|
||||
|
||||
from ubuntutools.pullpkg import PullPkg
|
||||
|
||||
if __name__ == '__main__':
|
||||
PullPkg.main(distro='debian', pull='source')
|
||||
if __name__ == "__main__":
|
||||
PullPkg.main(distro="debian", pull="source")
|
||||
|
@ -7,5 +7,5 @@
|
||||
|
||||
from ubuntutools.pullpkg import PullPkg
|
||||
|
||||
if __name__ == '__main__':
|
||||
PullPkg.main(distro='debian', pull='udebs')
|
||||
if __name__ == "__main__":
|
||||
PullPkg.main(distro="debian", pull="udebs")
|
||||
|
@ -7,5 +7,5 @@
|
||||
|
||||
from ubuntutools.pullpkg import PullPkg
|
||||
|
||||
if __name__ == '__main__':
|
||||
PullPkg.main(distro='ubuntu', pull='ddebs')
|
||||
if __name__ == "__main__":
|
||||
PullPkg.main(distro="ubuntu", pull="ddebs")
|
||||
|
@ -7,5 +7,5 @@
|
||||
|
||||
from ubuntutools.pullpkg import PullPkg
|
||||
|
||||
if __name__ == '__main__':
|
||||
PullPkg.main(distro='ubuntu', pull='debs')
|
||||
if __name__ == "__main__":
|
||||
PullPkg.main(distro="ubuntu", pull="debs")
|
||||
|
@ -7,5 +7,5 @@
|
||||
|
||||
from ubuntutools.pullpkg import PullPkg
|
||||
|
||||
if __name__ == '__main__':
|
||||
PullPkg.main(distro='ubuntu', pull='source')
|
||||
if __name__ == "__main__":
|
||||
PullPkg.main(distro="ubuntu", pull="source")
|
||||
|
@ -7,5 +7,5 @@
|
||||
|
||||
from ubuntutools.pullpkg import PullPkg
|
||||
|
||||
if __name__ == '__main__':
|
||||
PullPkg.main(distro='ubuntu', pull='udebs')
|
||||
if __name__ == "__main__":
|
||||
PullPkg.main(distro="ubuntu", pull="udebs")
|
||||
|
2
pull-pkg
2
pull-pkg
@ -25,5 +25,5 @@
|
||||
|
||||
from ubuntutools.pullpkg import PullPkg
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
PullPkg.main()
|
||||
|
@ -8,5 +8,5 @@
|
||||
|
||||
from ubuntutools.pullpkg import PullPkg
|
||||
|
||||
if __name__ == '__main__':
|
||||
PullPkg.main(distro='ppa', pull='ddebs')
|
||||
if __name__ == "__main__":
|
||||
PullPkg.main(distro="ppa", pull="ddebs")
|
||||
|
@ -8,5 +8,5 @@
|
||||
|
||||
from ubuntutools.pullpkg import PullPkg
|
||||
|
||||
if __name__ == '__main__':
|
||||
PullPkg.main(distro='ppa', pull='debs')
|
||||
if __name__ == "__main__":
|
||||
PullPkg.main(distro="ppa", pull="debs")
|
||||
|
@ -8,5 +8,5 @@
|
||||
|
||||
from ubuntutools.pullpkg import PullPkg
|
||||
|
||||
if __name__ == '__main__':
|
||||
PullPkg.main(distro='ppa', pull='source')
|
||||
if __name__ == "__main__":
|
||||
PullPkg.main(distro="ppa", pull="source")
|
||||
|
@ -8,5 +8,5 @@
|
||||
|
||||
from ubuntutools.pullpkg import PullPkg
|
||||
|
||||
if __name__ == '__main__':
|
||||
PullPkg.main(distro='ppa', pull='udebs')
|
||||
if __name__ == "__main__":
|
||||
PullPkg.main(distro="ppa", pull="udebs")
|
||||
|
@ -7,5 +7,5 @@
|
||||
|
||||
from ubuntutools.pullpkg import PullPkg
|
||||
|
||||
if __name__ == '__main__':
|
||||
PullPkg.main(distro='uca', pull='ddebs')
|
||||
if __name__ == "__main__":
|
||||
PullPkg.main(distro="uca", pull="ddebs")
|
||||
|
@ -7,5 +7,5 @@
|
||||
|
||||
from ubuntutools.pullpkg import PullPkg
|
||||
|
||||
if __name__ == '__main__':
|
||||
PullPkg.main(distro='uca', pull='debs')
|
||||
if __name__ == "__main__":
|
||||
PullPkg.main(distro="uca", pull="debs")
|
||||
|
@ -7,5 +7,5 @@
|
||||
|
||||
from ubuntutools.pullpkg import PullPkg
|
||||
|
||||
if __name__ == '__main__':
|
||||
PullPkg.main(distro='uca', pull='source')
|
||||
if __name__ == "__main__":
|
||||
PullPkg.main(distro="uca", pull="source")
|
||||
|
@ -7,5 +7,5 @@
|
||||
|
||||
from ubuntutools.pullpkg import PullPkg
|
||||
|
||||
if __name__ == '__main__':
|
||||
PullPkg.main(distro='uca', pull='udebs')
|
||||
if __name__ == "__main__":
|
||||
PullPkg.main(distro="uca", pull="udebs")
|
||||
|
201
requestbackport
201
requestbackport
@ -24,11 +24,11 @@ from distro_info import UbuntuDistroInfo
|
||||
from ubuntutools.config import UDTConfig
|
||||
from ubuntutools.lp.lpapicache import Launchpad, Distribution
|
||||
from ubuntutools.lp.udtexceptions import PackageNotFoundException
|
||||
from ubuntutools.question import (YesNoQuestion, EditBugReport,
|
||||
confirmation_prompt)
|
||||
from ubuntutools.question import YesNoQuestion, EditBugReport, confirmation_prompt
|
||||
from ubuntutools.rdepends import query_rdepends, RDependsException
|
||||
|
||||
from ubuntutools import getLogger
|
||||
|
||||
Logger = getLogger()
|
||||
|
||||
|
||||
@ -44,11 +44,9 @@ def determine_destinations(source, destination):
|
||||
if source not in ubuntu_info.all:
|
||||
raise DestinationException("Source release %s does not exist" % source)
|
||||
if destination not in ubuntu_info.all:
|
||||
raise DestinationException("Destination release %s does not exist"
|
||||
% destination)
|
||||
raise DestinationException("Destination release %s does not exist" % destination)
|
||||
if destination not in ubuntu_info.supported():
|
||||
raise DestinationException("Destination release %s is not supported"
|
||||
% destination)
|
||||
raise DestinationException("Destination release %s is not supported" % destination)
|
||||
|
||||
found = False
|
||||
destinations = []
|
||||
@ -76,30 +74,34 @@ def determine_destinations(source, destination):
|
||||
|
||||
|
||||
def disclaimer():
|
||||
print("Ubuntu's backports are not for fixing bugs in stable releases, "
|
||||
"but for bringing new features to older, stable releases.\n"
|
||||
"See https://wiki.ubuntu.com/UbuntuBackports for the Ubuntu "
|
||||
"Backports policy and processes.\n"
|
||||
"See https://wiki.ubuntu.com/StableReleaseUpdates for the process "
|
||||
"for fixing bugs in stable releases.")
|
||||
print(
|
||||
"Ubuntu's backports are not for fixing bugs in stable releases, "
|
||||
"but for bringing new features to older, stable releases.\n"
|
||||
"See https://wiki.ubuntu.com/UbuntuBackports for the Ubuntu "
|
||||
"Backports policy and processes.\n"
|
||||
"See https://wiki.ubuntu.com/StableReleaseUpdates for the process "
|
||||
"for fixing bugs in stable releases."
|
||||
)
|
||||
confirmation_prompt()
|
||||
|
||||
|
||||
def check_existing(package):
|
||||
"""Search for possible existing bug reports"""
|
||||
distro = Distribution('ubuntu')
|
||||
distro = Distribution("ubuntu")
|
||||
srcpkg = distro.getSourcePackage(name=package.getPackageName())
|
||||
|
||||
bugs = srcpkg.searchTasks(omit_duplicates=True,
|
||||
search_text="[BPO]",
|
||||
status=["Incomplete", "New", "Confirmed",
|
||||
"Triaged", "In Progress",
|
||||
"Fix Committed"])
|
||||
bugs = srcpkg.searchTasks(
|
||||
omit_duplicates=True,
|
||||
search_text="[BPO]",
|
||||
status=["Incomplete", "New", "Confirmed", "Triaged", "In Progress", "Fix Committed"],
|
||||
)
|
||||
if not bugs:
|
||||
return
|
||||
|
||||
Logger.info("There are existing bug reports that look similar to your "
|
||||
"request. Please check before continuing:")
|
||||
Logger.info(
|
||||
"There are existing bug reports that look similar to your "
|
||||
"request. Please check before continuing:"
|
||||
)
|
||||
|
||||
for bug in sorted([bug_task.bug for bug_task in bugs], key=lambda bug: bug.id):
|
||||
Logger.info(" * LP: #%-7i: %s %s", bug.id, bug.title, bug.web_link)
|
||||
@ -114,7 +116,7 @@ def find_rdepends(releases, published_binaries):
|
||||
for binpkg in published_binaries:
|
||||
intermediate[binpkg]
|
||||
|
||||
for arch in ('any', 'source'):
|
||||
for arch in ("any", "source"):
|
||||
for release in releases:
|
||||
for binpkg in published_binaries:
|
||||
try:
|
||||
@ -125,20 +127,20 @@ def find_rdepends(releases, published_binaries):
|
||||
for relationship, rdeps in raw_rdeps.items():
|
||||
for rdep in rdeps:
|
||||
# Ignore circular deps:
|
||||
if rdep['Package'] in published_binaries:
|
||||
if rdep["Package"] in published_binaries:
|
||||
continue
|
||||
# arch==any queries return Reverse-Build-Deps:
|
||||
if arch == 'any' and rdep.get('Architectures', []) == ['source']:
|
||||
if arch == "any" and rdep.get("Architectures", []) == ["source"]:
|
||||
continue
|
||||
intermediate[binpkg][rdep['Package']].append((release, relationship))
|
||||
intermediate[binpkg][rdep["Package"]].append((release, relationship))
|
||||
|
||||
output = []
|
||||
for binpkg, rdeps in intermediate.items():
|
||||
output += ['', binpkg, '-' * len(binpkg)]
|
||||
output += ["", binpkg, "-" * len(binpkg)]
|
||||
for pkg, appearences in rdeps.items():
|
||||
output += ['* %s' % pkg]
|
||||
output += ["* %s" % pkg]
|
||||
for release, relationship in appearences:
|
||||
output += [' [ ] %s (%s)' % (release, relationship)]
|
||||
output += [" [ ] %s (%s)" % (release, relationship)]
|
||||
|
||||
found_any = sum(len(rdeps) for rdeps in intermediate.values())
|
||||
if found_any:
|
||||
@ -153,8 +155,8 @@ def find_rdepends(releases, published_binaries):
|
||||
"package currently in the release still works with the new "
|
||||
"%(package)s installed. "
|
||||
"Reverse- Recommends, Suggests, and Enhances don't need to be "
|
||||
"tested, and are listed for completeness-sake."
|
||||
] + output
|
||||
"tested, and are listed for completeness-sake.",
|
||||
] + output
|
||||
else:
|
||||
output = ["No reverse dependencies"]
|
||||
|
||||
@ -162,8 +164,8 @@ def find_rdepends(releases, published_binaries):
|
||||
|
||||
|
||||
def locate_package(package, distribution):
|
||||
archive = Distribution('ubuntu').getArchive()
|
||||
for pass_ in ('source', 'binary'):
|
||||
archive = Distribution("ubuntu").getArchive()
|
||||
for pass_ in ("source", "binary"):
|
||||
try:
|
||||
package_spph = archive.getSourcePackage(package, distribution)
|
||||
return package_spph
|
||||
@ -174,8 +176,9 @@ def locate_package(package, distribution):
|
||||
Logger.error(str(e))
|
||||
sys.exit(1)
|
||||
package = apt_pkg.candidate.source_name
|
||||
Logger.info("Binary package specified, considering its source "
|
||||
"package instead: %s", package)
|
||||
Logger.info(
|
||||
"Binary package specified, considering its source package instead: %s", package
|
||||
)
|
||||
|
||||
|
||||
def request_backport(package_spph, source, destinations):
|
||||
@ -184,67 +187,77 @@ def request_backport(package_spph, source, destinations):
|
||||
published_binaries.add(bpph.getPackageName())
|
||||
|
||||
if not published_binaries:
|
||||
Logger.error("%s (%s) has no published binaries in %s. ",
|
||||
package_spph.getPackageName(), package_spph.getVersion(),
|
||||
source)
|
||||
Logger.info("Is it stuck in bin-NEW? It can't be backported until "
|
||||
"the binaries have been accepted.")
|
||||
Logger.error(
|
||||
"%s (%s) has no published binaries in %s. ",
|
||||
package_spph.getPackageName(),
|
||||
package_spph.getVersion(),
|
||||
source,
|
||||
)
|
||||
Logger.info(
|
||||
"Is it stuck in bin-NEW? It can't be backported until "
|
||||
"the binaries have been accepted."
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
testing = ["[Testing]", ""]
|
||||
for dest in destinations:
|
||||
testing += [" * %s:" % dest.capitalize()]
|
||||
testing += [" [ ] Package builds without modification"]
|
||||
testing += [" [ ] %s installs cleanly and runs" % binary
|
||||
for binary in published_binaries]
|
||||
testing += [
|
||||
" [ ] %s installs cleanly and runs" % binary for binary in published_binaries
|
||||
]
|
||||
|
||||
subst = {
|
||||
'package': package_spph.getPackageName(),
|
||||
'version': package_spph.getVersion(),
|
||||
'component': package_spph.getComponent(),
|
||||
'source': package_spph.getSeriesAndPocket(),
|
||||
'destinations': ', '.join(destinations),
|
||||
"package": package_spph.getPackageName(),
|
||||
"version": package_spph.getVersion(),
|
||||
"component": package_spph.getComponent(),
|
||||
"source": package_spph.getSeriesAndPocket(),
|
||||
"destinations": ", ".join(destinations),
|
||||
}
|
||||
subject = "[BPO] %(package)s %(version)s to %(destinations)s" % subst
|
||||
body = ('\n'.join(
|
||||
body = (
|
||||
"\n".join(
|
||||
[
|
||||
"[Impact]",
|
||||
"",
|
||||
" * Justification for backporting the new version to the stable release.",
|
||||
"",
|
||||
"[Scope]",
|
||||
"",
|
||||
" * List the Ubuntu release you will backport from,"
|
||||
" and the specific package version.",
|
||||
"",
|
||||
" * List the Ubuntu release(s) you will backport to.",
|
||||
"",
|
||||
"[Other Info]",
|
||||
"",
|
||||
" * Anything else you think is useful to include",
|
||||
""
|
||||
"[Impact]",
|
||||
"",
|
||||
" * Justification for backporting the new version to the stable release.",
|
||||
"",
|
||||
"[Scope]",
|
||||
"",
|
||||
" * List the Ubuntu release you will backport from,"
|
||||
" and the specific package version.",
|
||||
"",
|
||||
" * List the Ubuntu release(s) you will backport to.",
|
||||
"",
|
||||
"[Other Info]",
|
||||
"",
|
||||
" * Anything else you think is useful to include",
|
||||
"",
|
||||
]
|
||||
+ testing
|
||||
+ [""]
|
||||
+ find_rdepends(destinations, published_binaries)
|
||||
+ [""]) % subst)
|
||||
+ [""]
|
||||
)
|
||||
% subst
|
||||
)
|
||||
|
||||
editor = EditBugReport(subject, body)
|
||||
editor.edit()
|
||||
subject, body = editor.get_report()
|
||||
|
||||
Logger.info('The final report is:\nSummary: %s\nDescription:\n%s\n',
|
||||
subject, body)
|
||||
Logger.info("The final report is:\nSummary: %s\nDescription:\n%s\n", subject, body)
|
||||
if YesNoQuestion().ask("Request this backport", "yes") == "no":
|
||||
sys.exit(1)
|
||||
|
||||
distro = Distribution('ubuntu')
|
||||
distro = Distribution("ubuntu")
|
||||
pkgname = package_spph.getPackageName()
|
||||
|
||||
bug = Launchpad.bugs.createBug(title=subject, description=body,
|
||||
target=distro.getSourcePackage(name=pkgname))
|
||||
bug = Launchpad.bugs.createBug(
|
||||
title=subject, description=body, target=distro.getSourcePackage(name=pkgname)
|
||||
)
|
||||
|
||||
bug.subscribe(person=Launchpad.people['ubuntu-backporters'])
|
||||
bug.subscribe(person=Launchpad.people["ubuntu-backporters"])
|
||||
|
||||
for dest in destinations:
|
||||
series = distro.getSeries(dest)
|
||||
@ -257,20 +270,35 @@ def request_backport(package_spph, source, destinations):
|
||||
|
||||
|
||||
def main():
|
||||
parser = optparse.OptionParser('%prog [options] package')
|
||||
parser.add_option('-d', '--destination', metavar='DEST',
|
||||
help='Backport to DEST release and necessary '
|
||||
'intermediate releases '
|
||||
'(default: current LTS release)')
|
||||
parser.add_option('-s', '--source', metavar='SOURCE',
|
||||
help='Backport from SOURCE release '
|
||||
'(default: current devel release)')
|
||||
parser.add_option('-l', '--lpinstance', metavar='INSTANCE', default=None,
|
||||
help='Launchpad instance to connect to '
|
||||
'(default: production).')
|
||||
parser.add_option('--no-conf', action='store_true',
|
||||
dest='no_conf', default=False,
|
||||
help="Don't read config files or environment variables")
|
||||
parser = optparse.OptionParser("%prog [options] package")
|
||||
parser.add_option(
|
||||
"-d",
|
||||
"--destination",
|
||||
metavar="DEST",
|
||||
help="Backport to DEST release and necessary "
|
||||
"intermediate releases "
|
||||
"(default: current LTS release)",
|
||||
)
|
||||
parser.add_option(
|
||||
"-s",
|
||||
"--source",
|
||||
metavar="SOURCE",
|
||||
help="Backport from SOURCE release (default: current devel release)",
|
||||
)
|
||||
parser.add_option(
|
||||
"-l",
|
||||
"--lpinstance",
|
||||
metavar="INSTANCE",
|
||||
default=None,
|
||||
help="Launchpad instance to connect to (default: production).",
|
||||
)
|
||||
parser.add_option(
|
||||
"--no-conf",
|
||||
action="store_true",
|
||||
dest="no_conf",
|
||||
default=False,
|
||||
help="Don't read config files or environment variables",
|
||||
)
|
||||
options, args = parser.parse_args()
|
||||
|
||||
if len(args) != 1:
|
||||
@ -280,15 +308,14 @@ def main():
|
||||
config = UDTConfig(options.no_conf)
|
||||
|
||||
if options.lpinstance is None:
|
||||
options.lpinstance = config.get_value('LPINSTANCE')
|
||||
options.lpinstance = config.get_value("LPINSTANCE")
|
||||
Launchpad.login(options.lpinstance)
|
||||
|
||||
if options.source is None:
|
||||
options.source = Distribution('ubuntu').getDevelopmentSeries().name
|
||||
options.source = Distribution("ubuntu").getDevelopmentSeries().name
|
||||
|
||||
try:
|
||||
destinations = determine_destinations(options.source,
|
||||
options.destination)
|
||||
destinations = determine_destinations(options.source, options.destination)
|
||||
except DestinationException as e:
|
||||
Logger.error(str(e))
|
||||
sys.exit(1)
|
||||
@ -301,5 +328,5 @@ def main():
|
||||
request_backport(package_spph, options.source, destinations)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
316
requestsync
316
requestsync
@ -39,6 +39,7 @@ from ubuntutools.question import confirmation_prompt, EditBugReport
|
||||
from ubuntutools.version import Version
|
||||
|
||||
from ubuntutools import getLogger
|
||||
|
||||
Logger = getLogger()
|
||||
|
||||
#
|
||||
@ -48,46 +49,78 @@ Logger = getLogger()
|
||||
|
||||
def main():
|
||||
# Our usage options.
|
||||
usage = ('Usage: %prog [options] '
|
||||
'<source package> [<target release> [base version]]')
|
||||
usage = "Usage: %prog [options] <source package> [<target release> [base version]]"
|
||||
parser = optparse.OptionParser(usage)
|
||||
|
||||
parser.add_option('-d', type='string',
|
||||
dest='dist', default='unstable',
|
||||
help='Debian distribution to sync from.')
|
||||
parser.add_option('-k', type='string',
|
||||
dest='keyid', default=None,
|
||||
help='GnuPG key ID to use for signing report '
|
||||
'(only used when emailing the sync request).')
|
||||
parser.add_option('-n', action='store_true',
|
||||
dest='newpkg', default=False,
|
||||
help='Whether package to sync is a new package in '
|
||||
'Ubuntu.')
|
||||
parser.add_option('--email', action='store_true', default=False,
|
||||
help='Use a PGP-signed email for filing the sync '
|
||||
'request, rather than the LP API.')
|
||||
parser.add_option('--lp', dest='deprecated_lp_flag',
|
||||
action='store_true', default=False,
|
||||
help=optparse.SUPPRESS_HELP)
|
||||
parser.add_option('-l', '--lpinstance', metavar='INSTANCE',
|
||||
dest='lpinstance', default=None,
|
||||
help='Launchpad instance to connect to '
|
||||
'(default: production).')
|
||||
parser.add_option('-s', action='store_true',
|
||||
dest='sponsorship', default=False,
|
||||
help='Force sponsorship')
|
||||
parser.add_option('-C', action='store_true',
|
||||
dest='missing_changelog_ok', default=False,
|
||||
help='Allow changelog to be manually filled in '
|
||||
'when missing')
|
||||
parser.add_option('-e', action='store_true',
|
||||
dest='ffe', default=False,
|
||||
help='Use this after FeatureFreeze for non-bug fix '
|
||||
'syncs, changes default subscription to the '
|
||||
'appropriate release team.')
|
||||
parser.add_option('--no-conf', action='store_true',
|
||||
dest='no_conf', default=False,
|
||||
help="Don't read config files or environment variables")
|
||||
parser.add_option(
|
||||
"-d",
|
||||
type="string",
|
||||
dest="dist",
|
||||
default="unstable",
|
||||
help="Debian distribution to sync from.",
|
||||
)
|
||||
parser.add_option(
|
||||
"-k",
|
||||
type="string",
|
||||
dest="keyid",
|
||||
default=None,
|
||||
help="GnuPG key ID to use for signing report "
|
||||
"(only used when emailing the sync request).",
|
||||
)
|
||||
parser.add_option(
|
||||
"-n",
|
||||
action="store_true",
|
||||
dest="newpkg",
|
||||
default=False,
|
||||
help="Whether package to sync is a new package in Ubuntu.",
|
||||
)
|
||||
parser.add_option(
|
||||
"--email",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Use a PGP-signed email for filing the sync request, rather than the LP API.",
|
||||
)
|
||||
parser.add_option(
|
||||
"--lp",
|
||||
dest="deprecated_lp_flag",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help=optparse.SUPPRESS_HELP,
|
||||
)
|
||||
parser.add_option(
|
||||
"-l",
|
||||
"--lpinstance",
|
||||
metavar="INSTANCE",
|
||||
dest="lpinstance",
|
||||
default=None,
|
||||
help="Launchpad instance to connect to (default: production).",
|
||||
)
|
||||
parser.add_option(
|
||||
"-s", action="store_true", dest="sponsorship", default=False, help="Force sponsorship"
|
||||
)
|
||||
parser.add_option(
|
||||
"-C",
|
||||
action="store_true",
|
||||
dest="missing_changelog_ok",
|
||||
default=False,
|
||||
help="Allow changelog to be manually filled in when missing",
|
||||
)
|
||||
parser.add_option(
|
||||
"-e",
|
||||
action="store_true",
|
||||
dest="ffe",
|
||||
default=False,
|
||||
help="Use this after FeatureFreeze for non-bug fix "
|
||||
"syncs, changes default subscription to the "
|
||||
"appropriate release team.",
|
||||
)
|
||||
parser.add_option(
|
||||
"--no-conf",
|
||||
action="store_true",
|
||||
dest="no_conf",
|
||||
default=False,
|
||||
help="Don't read config files or environment variables",
|
||||
)
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
@ -104,74 +137,78 @@ def main():
|
||||
if options.email:
|
||||
options.lpapi = False
|
||||
else:
|
||||
options.lpapi = config.get_value('USE_LPAPI', default=True,
|
||||
boolean=True)
|
||||
options.lpapi = config.get_value("USE_LPAPI", default=True, boolean=True)
|
||||
if options.lpinstance is None:
|
||||
options.lpinstance = config.get_value('LPINSTANCE')
|
||||
options.lpinstance = config.get_value("LPINSTANCE")
|
||||
|
||||
if options.keyid is None:
|
||||
options.keyid = config.get_value('KEYID')
|
||||
options.keyid = config.get_value("KEYID")
|
||||
|
||||
if not options.lpapi:
|
||||
if options.lpinstance == 'production':
|
||||
bug_mail_domain = 'bugs.launchpad.net'
|
||||
elif options.lpinstance == 'staging':
|
||||
bug_mail_domain = 'bugs.staging.launchpad.net'
|
||||
if options.lpinstance == "production":
|
||||
bug_mail_domain = "bugs.launchpad.net"
|
||||
elif options.lpinstance == "staging":
|
||||
bug_mail_domain = "bugs.staging.launchpad.net"
|
||||
else:
|
||||
Logger.error('Error: Unknown launchpad instance: %s'
|
||||
% options.lpinstance)
|
||||
Logger.error("Error: Unknown launchpad instance: %s" % options.lpinstance)
|
||||
sys.exit(1)
|
||||
|
||||
mailserver_host = config.get_value('SMTP_SERVER',
|
||||
default=None,
|
||||
compat_keys=['UBUSMTP', 'DEBSMTP'])
|
||||
mailserver_host = config.get_value(
|
||||
"SMTP_SERVER", default=None, compat_keys=["UBUSMTP", "DEBSMTP"]
|
||||
)
|
||||
if not options.lpapi and not mailserver_host:
|
||||
try:
|
||||
import DNS
|
||||
|
||||
DNS.DiscoverNameServers()
|
||||
mxlist = DNS.mxlookup(bug_mail_domain)
|
||||
firstmx = mxlist[0]
|
||||
mailserver_host = firstmx[1]
|
||||
except ImportError:
|
||||
Logger.error('Please install python-dns to support '
|
||||
'Launchpad mail server lookup.')
|
||||
Logger.error("Please install python-dns to support Launchpad mail server lookup.")
|
||||
sys.exit(1)
|
||||
|
||||
mailserver_port = config.get_value('SMTP_PORT', default=25,
|
||||
compat_keys=['UBUSMTP_PORT',
|
||||
'DEBSMTP_PORT'])
|
||||
mailserver_user = config.get_value('SMTP_USER',
|
||||
compat_keys=['UBUSMTP_USER',
|
||||
'DEBSMTP_USER'])
|
||||
mailserver_pass = config.get_value('SMTP_PASS',
|
||||
compat_keys=['UBUSMTP_PASS',
|
||||
'DEBSMTP_PASS'])
|
||||
mailserver_port = config.get_value(
|
||||
"SMTP_PORT", default=25, compat_keys=["UBUSMTP_PORT", "DEBSMTP_PORT"]
|
||||
)
|
||||
mailserver_user = config.get_value("SMTP_USER", compat_keys=["UBUSMTP_USER", "DEBSMTP_USER"])
|
||||
mailserver_pass = config.get_value("SMTP_PASS", compat_keys=["UBUSMTP_PASS", "DEBSMTP_PASS"])
|
||||
|
||||
# import the needed requestsync module
|
||||
if options.lpapi:
|
||||
from ubuntutools.requestsync.lp import (check_existing_reports,
|
||||
get_debian_srcpkg,
|
||||
get_ubuntu_srcpkg,
|
||||
get_ubuntu_delta_changelog,
|
||||
need_sponsorship, post_bug)
|
||||
from ubuntutools.requestsync.lp import (
|
||||
check_existing_reports,
|
||||
get_debian_srcpkg,
|
||||
get_ubuntu_srcpkg,
|
||||
get_ubuntu_delta_changelog,
|
||||
need_sponsorship,
|
||||
post_bug,
|
||||
)
|
||||
from ubuntutools.lp.lpapicache import Distribution, Launchpad
|
||||
|
||||
# See if we have LP credentials and exit if we don't -
|
||||
# cannot continue in this case
|
||||
|
||||
try:
|
||||
# devel for changelogUrl()
|
||||
Launchpad.login(service=options.lpinstance, api_version='devel')
|
||||
Launchpad.login(service=options.lpinstance, api_version="devel")
|
||||
except IOError:
|
||||
sys.exit(1)
|
||||
else:
|
||||
from ubuntutools.requestsync.mail import (check_existing_reports,
|
||||
get_debian_srcpkg,
|
||||
get_ubuntu_srcpkg,
|
||||
get_ubuntu_delta_changelog,
|
||||
mail_bug, need_sponsorship)
|
||||
if not any(x in os.environ for x in ('UBUMAIL', 'DEBEMAIL', 'EMAIL')):
|
||||
Logger.error('The environment variable UBUMAIL, DEBEMAIL or EMAIL needs '
|
||||
'to be set to let this script mail the sync request.')
|
||||
from ubuntutools.requestsync.mail import (
|
||||
check_existing_reports,
|
||||
get_debian_srcpkg,
|
||||
get_ubuntu_srcpkg,
|
||||
get_ubuntu_delta_changelog,
|
||||
mail_bug,
|
||||
need_sponsorship,
|
||||
)
|
||||
|
||||
if not any(x in os.environ for x in ("UBUMAIL", "DEBEMAIL", "EMAIL")):
|
||||
Logger.error(
|
||||
"The environment variable UBUMAIL, DEBEMAIL or EMAIL needs "
|
||||
"to be set to let this script mail the sync request."
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
newsource = options.newpkg
|
||||
@ -185,30 +222,30 @@ def main():
|
||||
|
||||
if len(args) == 1:
|
||||
if lpapi:
|
||||
release = Distribution('ubuntu').getDevelopmentSeries().name
|
||||
release = Distribution("ubuntu").getDevelopmentSeries().name
|
||||
else:
|
||||
ubu_info = UbuntuDistroInfo()
|
||||
release = ubu_info.devel()
|
||||
Logger.warning('Target release missing - assuming %s' % release)
|
||||
Logger.warning("Target release missing - assuming %s" % release)
|
||||
elif len(args) == 2:
|
||||
release = args[1]
|
||||
elif len(args) == 3:
|
||||
release = args[1]
|
||||
force_base_version = Version(args[2])
|
||||
else:
|
||||
Logger.error('Too many arguments.')
|
||||
Logger.error("Too many arguments.")
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
# Get the current Ubuntu source package
|
||||
try:
|
||||
ubuntu_srcpkg = get_ubuntu_srcpkg(srcpkg, release, 'Proposed')
|
||||
ubuntu_srcpkg = get_ubuntu_srcpkg(srcpkg, release, "Proposed")
|
||||
ubuntu_version = Version(ubuntu_srcpkg.getVersion())
|
||||
ubuntu_component = ubuntu_srcpkg.getComponent()
|
||||
newsource = False # override the -n flag
|
||||
except udtexceptions.PackageNotFoundException:
|
||||
ubuntu_srcpkg = None
|
||||
ubuntu_version = Version('~')
|
||||
ubuntu_version = Version("~")
|
||||
ubuntu_component = None # Set after getting the Debian info
|
||||
if not newsource:
|
||||
Logger.info("'%s' doesn't exist in 'Ubuntu %s'." % (srcpkg, release))
|
||||
@ -232,15 +269,16 @@ def main():
|
||||
sys.exit(1)
|
||||
|
||||
if ubuntu_component is None:
|
||||
if debian_component == 'main':
|
||||
ubuntu_component = 'universe'
|
||||
if debian_component == "main":
|
||||
ubuntu_component = "universe"
|
||||
else:
|
||||
ubuntu_component = 'multiverse'
|
||||
ubuntu_component = "multiverse"
|
||||
|
||||
# Stop if Ubuntu has already the version from Debian or a newer version
|
||||
if (ubuntu_version >= debian_version) and options.lpapi:
|
||||
# try rmadison
|
||||
import ubuntutools.requestsync.mail
|
||||
|
||||
try:
|
||||
debian_srcpkg = ubuntutools.requestsync.mail.get_debian_srcpkg(srcpkg, distro)
|
||||
debian_version = Version(debian_srcpkg.getVersion())
|
||||
@ -250,13 +288,16 @@ def main():
|
||||
sys.exit(1)
|
||||
|
||||
if ubuntu_version == debian_version:
|
||||
Logger.error('The versions in Debian and Ubuntu are the '
|
||||
'same already (%s). Aborting.' % ubuntu_version)
|
||||
Logger.error(
|
||||
"The versions in Debian and Ubuntu are the "
|
||||
"same already (%s). Aborting." % ubuntu_version
|
||||
)
|
||||
sys.exit(1)
|
||||
if ubuntu_version > debian_version:
|
||||
Logger.error('The version in Ubuntu (%s) is newer than '
|
||||
'the version in Debian (%s). Aborting.'
|
||||
% (ubuntu_version, debian_version))
|
||||
Logger.error(
|
||||
"The version in Ubuntu (%s) is newer than "
|
||||
"the version in Debian (%s). Aborting." % (ubuntu_version, debian_version)
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# -s flag not specified - check if we do need sponsorship
|
||||
@ -264,40 +305,47 @@ def main():
|
||||
sponsorship = need_sponsorship(srcpkg, ubuntu_component, release)
|
||||
|
||||
if not sponsorship and not ffe:
|
||||
Logger.error('Consider using syncpackage(1) for syncs that '
|
||||
'do not require feature freeze exceptions.')
|
||||
Logger.error(
|
||||
"Consider using syncpackage(1) for syncs that "
|
||||
"do not require feature freeze exceptions."
|
||||
)
|
||||
|
||||
# Check for existing package reports
|
||||
if not newsource:
|
||||
check_existing_reports(srcpkg)
|
||||
|
||||
# Generate bug report
|
||||
pkg_to_sync = ('%s %s (%s) from Debian %s (%s)'
|
||||
% (srcpkg, debian_version, ubuntu_component,
|
||||
distro, debian_component))
|
||||
pkg_to_sync = "%s %s (%s) from Debian %s (%s)" % (
|
||||
srcpkg,
|
||||
debian_version,
|
||||
ubuntu_component,
|
||||
distro,
|
||||
debian_component,
|
||||
)
|
||||
title = "Sync %s" % pkg_to_sync
|
||||
if ffe:
|
||||
title = "FFe: " + title
|
||||
report = "Please sync %s\n\n" % pkg_to_sync
|
||||
|
||||
if 'ubuntu' in str(ubuntu_version):
|
||||
if "ubuntu" in str(ubuntu_version):
|
||||
need_interaction = True
|
||||
|
||||
Logger.info('Changes have been made to the package in Ubuntu.')
|
||||
Logger.info('Please edit the report and give an explanation.')
|
||||
Logger.info('Not saving the report file will abort the request.')
|
||||
report += ('Explanation of the Ubuntu delta and why it can be '
|
||||
'dropped:\n%s\n>>> ENTER_EXPLANATION_HERE <<<\n\n'
|
||||
% get_ubuntu_delta_changelog(ubuntu_srcpkg))
|
||||
Logger.info("Changes have been made to the package in Ubuntu.")
|
||||
Logger.info("Please edit the report and give an explanation.")
|
||||
Logger.info("Not saving the report file will abort the request.")
|
||||
report += (
|
||||
"Explanation of the Ubuntu delta and why it can be "
|
||||
"dropped:\n%s\n>>> ENTER_EXPLANATION_HERE <<<\n\n"
|
||||
% get_ubuntu_delta_changelog(ubuntu_srcpkg)
|
||||
)
|
||||
|
||||
if ffe:
|
||||
need_interaction = True
|
||||
|
||||
Logger.info('To approve FeatureFreeze exception, you need to state')
|
||||
Logger.info('the reason why you feel it is necessary.')
|
||||
Logger.info('Not saving the report file will abort the request.')
|
||||
report += ('Explanation of FeatureFreeze exception:\n'
|
||||
'>>> ENTER_EXPLANATION_HERE <<<\n\n')
|
||||
Logger.info("To approve FeatureFreeze exception, you need to state")
|
||||
Logger.info("the reason why you feel it is necessary.")
|
||||
Logger.info("Not saving the report file will abort the request.")
|
||||
report += "Explanation of FeatureFreeze exception:\n>>> ENTER_EXPLANATION_HERE <<<\n\n"
|
||||
|
||||
if need_interaction:
|
||||
confirmation_prompt()
|
||||
@ -305,17 +353,18 @@ def main():
|
||||
base_version = force_base_version or ubuntu_version
|
||||
|
||||
if newsource:
|
||||
report += 'All changelog entries:\n\n'
|
||||
report += "All changelog entries:\n\n"
|
||||
else:
|
||||
report += ('Changelog entries since current %s version %s:\n\n'
|
||||
% (release, ubuntu_version))
|
||||
report += "Changelog entries since current %s version %s:\n\n" % (release, ubuntu_version)
|
||||
changelog = debian_srcpkg.getChangelog(since_version=base_version)
|
||||
if not changelog:
|
||||
if not options.missing_changelog_ok:
|
||||
Logger.error("Did not retrieve any changelog entries. "
|
||||
"Do you need to specify '-C'? "
|
||||
"Was the package recently uploaded? (check "
|
||||
"http://packages.debian.org/changelogs/)")
|
||||
Logger.error(
|
||||
"Did not retrieve any changelog entries. "
|
||||
"Do you need to specify '-C'? "
|
||||
"Was the package recently uploaded? (check "
|
||||
"http://packages.debian.org/changelogs/)"
|
||||
)
|
||||
sys.exit(1)
|
||||
else:
|
||||
need_interaction = True
|
||||
@ -326,36 +375,49 @@ def main():
|
||||
editor.edit(optional=not need_interaction)
|
||||
title, report = editor.get_report()
|
||||
|
||||
if 'XXX FIXME' in report:
|
||||
Logger.error("changelog boilerplate found in report, "
|
||||
"please manually add changelog when using '-C'")
|
||||
if "XXX FIXME" in report:
|
||||
Logger.error(
|
||||
"changelog boilerplate found in report, "
|
||||
"please manually add changelog when using '-C'"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# bug status and bug subscriber
|
||||
status = 'confirmed'
|
||||
subscribe = 'ubuntu-archive'
|
||||
status = "confirmed"
|
||||
subscribe = "ubuntu-archive"
|
||||
if sponsorship:
|
||||
status = 'new'
|
||||
subscribe = 'ubuntu-sponsors'
|
||||
status = "new"
|
||||
subscribe = "ubuntu-sponsors"
|
||||
if ffe:
|
||||
status = 'new'
|
||||
subscribe = 'ubuntu-release'
|
||||
status = "new"
|
||||
subscribe = "ubuntu-release"
|
||||
|
||||
srcpkg = not newsource and srcpkg or None
|
||||
if lpapi:
|
||||
# Map status to the values expected by LP API
|
||||
mapping = {'new': 'New', 'confirmed': 'Confirmed'}
|
||||
mapping = {"new": "New", "confirmed": "Confirmed"}
|
||||
# Post sync request using LP API
|
||||
post_bug(srcpkg, subscribe, mapping[status], title, report)
|
||||
else:
|
||||
email_from = ubu_email(export=False)[1]
|
||||
# Mail sync request
|
||||
mail_bug(srcpkg, subscribe, status, title, report, bug_mail_domain,
|
||||
options.keyid, email_from, mailserver_host, mailserver_port,
|
||||
mailserver_user, mailserver_pass)
|
||||
mail_bug(
|
||||
srcpkg,
|
||||
subscribe,
|
||||
status,
|
||||
title,
|
||||
report,
|
||||
bug_mail_domain,
|
||||
options.keyid,
|
||||
email_from,
|
||||
mailserver_host,
|
||||
mailserver_port,
|
||||
mailserver_user,
|
||||
mailserver_pass,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
|
194
reverse-depends
194
reverse-depends
@ -19,11 +19,11 @@ import sys
|
||||
|
||||
from distro_info import DistroDataOutdated
|
||||
|
||||
from ubuntutools.misc import (system_distribution, vendor_to_distroinfo,
|
||||
codename_to_distribution)
|
||||
from ubuntutools.misc import system_distribution, vendor_to_distroinfo, codename_to_distribution
|
||||
from ubuntutools.rdepends import query_rdepends, RDependsException
|
||||
|
||||
from ubuntutools import getLogger
|
||||
|
||||
Logger = getLogger()
|
||||
|
||||
DEFAULT_MAX_DEPTH = 10 # We want avoid any infinite loop...
|
||||
@ -35,77 +35,107 @@ def main():
|
||||
default_release = system_distro_info.devel()
|
||||
except DistroDataOutdated as e:
|
||||
Logger.warning(e)
|
||||
default_release = 'unstable'
|
||||
default_release = "unstable"
|
||||
|
||||
description = ("List reverse-dependencies of package. "
|
||||
"If the package name is prefixed with src: then the "
|
||||
"reverse-dependencies of all the binary packages that "
|
||||
"the specified source package builds will be listed.")
|
||||
description = (
|
||||
"List reverse-dependencies of package. "
|
||||
"If the package name is prefixed with src: then the "
|
||||
"reverse-dependencies of all the binary packages that "
|
||||
"the specified source package builds will be listed."
|
||||
)
|
||||
|
||||
parser = argparse.ArgumentParser(description=description)
|
||||
parser.add_argument('-r', '--release', default=default_release,
|
||||
help='Query dependencies in RELEASE. '
|
||||
'Default: %s' % default_release)
|
||||
parser.add_argument('-R', '--without-recommends', action='store_false',
|
||||
dest='recommends',
|
||||
help='Only consider Depends relationships, '
|
||||
'not Recommends')
|
||||
parser.add_argument('-s', '--with-suggests', action='store_true',
|
||||
help='Also consider Suggests relationships')
|
||||
parser.add_argument('-b', '--build-depends', action='store_true',
|
||||
help='Query build dependencies (synonym for --arch=source)')
|
||||
parser.add_argument('-a', '--arch', default='any',
|
||||
help='Query dependencies in ARCH. Default: any')
|
||||
parser.add_argument('-c', '--component', action='append',
|
||||
help='Only consider reverse-dependencies in COMPONENT. '
|
||||
'Can be specified multiple times. Default: all')
|
||||
parser.add_argument('-l', '--list', action='store_true',
|
||||
help='Display a simple, machine-readable list')
|
||||
parser.add_argument('-u', '--service-url', metavar='URL',
|
||||
dest='server', default=None,
|
||||
help='Reverse Dependencies webservice URL. '
|
||||
'Default: UbuntuWire')
|
||||
parser.add_argument('-x', '--recursive', action='store_true',
|
||||
help='Consider to find reverse dependencies recursively.')
|
||||
parser.add_argument('-d', '--recursive-depth', type=int,
|
||||
default=DEFAULT_MAX_DEPTH,
|
||||
help='If recusive, you can specify the depth.')
|
||||
parser.add_argument('package')
|
||||
parser.add_argument(
|
||||
"-r",
|
||||
"--release",
|
||||
default=default_release,
|
||||
help="Query dependencies in RELEASE. Default: %s" % default_release,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-R",
|
||||
"--without-recommends",
|
||||
action="store_false",
|
||||
dest="recommends",
|
||||
help="Only consider Depends relationships, not Recommends",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s", "--with-suggests", action="store_true", help="Also consider Suggests relationships"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-b",
|
||||
"--build-depends",
|
||||
action="store_true",
|
||||
help="Query build dependencies (synonym for --arch=source)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-a", "--arch", default="any", help="Query dependencies in ARCH. Default: any"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--component",
|
||||
action="append",
|
||||
help="Only consider reverse-dependencies in COMPONENT. "
|
||||
"Can be specified multiple times. Default: all",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l", "--list", action="store_true", help="Display a simple, machine-readable list"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-u",
|
||||
"--service-url",
|
||||
metavar="URL",
|
||||
dest="server",
|
||||
default=None,
|
||||
help="Reverse Dependencies webservice URL. Default: UbuntuWire",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-x",
|
||||
"--recursive",
|
||||
action="store_true",
|
||||
help="Consider to find reverse dependencies recursively.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--recursive-depth",
|
||||
type=int,
|
||||
default=DEFAULT_MAX_DEPTH,
|
||||
help="If recusive, you can specify the depth.",
|
||||
)
|
||||
parser.add_argument("package")
|
||||
|
||||
options = parser.parse_args()
|
||||
|
||||
opts = {}
|
||||
if options.server is not None:
|
||||
opts['server'] = options.server
|
||||
opts["server"] = options.server
|
||||
|
||||
# Convert unstable/testing aliases to codenames:
|
||||
distribution = codename_to_distribution(options.release)
|
||||
if not distribution:
|
||||
parser.error('Unknown release codename %s' % options.release)
|
||||
parser.error("Unknown release codename %s" % options.release)
|
||||
distro_info = vendor_to_distroinfo(distribution)()
|
||||
try:
|
||||
options.release = distro_info.codename(options.release,
|
||||
default=options.release)
|
||||
options.release = distro_info.codename(options.release, default=options.release)
|
||||
except DistroDataOutdated:
|
||||
# We already logged a warning
|
||||
pass
|
||||
|
||||
if options.build_depends:
|
||||
options.arch = 'source'
|
||||
options.arch = "source"
|
||||
|
||||
if options.arch == 'source':
|
||||
if options.arch == "source":
|
||||
fields = [
|
||||
'Reverse-Build-Depends',
|
||||
'Reverse-Build-Depends-Indep',
|
||||
'Reverse-Build-Depends-Arch',
|
||||
'Reverse-Testsuite-Triggers',
|
||||
"Reverse-Build-Depends",
|
||||
"Reverse-Build-Depends-Indep",
|
||||
"Reverse-Build-Depends-Arch",
|
||||
"Reverse-Testsuite-Triggers",
|
||||
]
|
||||
else:
|
||||
fields = ['Reverse-Depends']
|
||||
fields = ["Reverse-Depends"]
|
||||
if options.recommends:
|
||||
fields.append('Reverse-Recommends')
|
||||
fields.append("Reverse-Recommends")
|
||||
if options.with_suggests:
|
||||
fields.append('Reverse-Suggests')
|
||||
fields.append("Reverse-Suggests")
|
||||
|
||||
def build_results(package, result, fields, component, recursive):
|
||||
try:
|
||||
@ -119,9 +149,9 @@ def main():
|
||||
if fields:
|
||||
data = {k: v for k, v in data.items() if k in fields}
|
||||
if component:
|
||||
data = {k: [rdep for rdep in v
|
||||
if rdep['Component'] in component]
|
||||
for k, v in data.items()}
|
||||
data = {
|
||||
k: [rdep for rdep in v if rdep["Component"] in component] for k, v in data.items()
|
||||
}
|
||||
data = {k: v for k, v in data.items() if v}
|
||||
|
||||
result[package] = data
|
||||
@ -129,13 +159,16 @@ def main():
|
||||
if recursive > 0:
|
||||
for rdeps in result[package].values():
|
||||
for rdep in rdeps:
|
||||
build_results(
|
||||
rdep['Package'], result, fields, component, recursive - 1)
|
||||
build_results(rdep["Package"], result, fields, component, recursive - 1)
|
||||
|
||||
result = {}
|
||||
build_results(
|
||||
options.package, result, fields, options.component,
|
||||
options.recursive and options.recursive_depth or 0)
|
||||
options.package,
|
||||
result,
|
||||
fields,
|
||||
options.component,
|
||||
options.recursive and options.recursive_depth or 0,
|
||||
)
|
||||
|
||||
if options.list:
|
||||
display_consise(result)
|
||||
@ -150,50 +183,55 @@ def display_verbose(package, values):
|
||||
|
||||
def log_field(field):
|
||||
Logger.info(field)
|
||||
Logger.info('=' * len(field))
|
||||
Logger.info("=" * len(field))
|
||||
|
||||
def log_package(values, package, arch, dependency, offset=0):
|
||||
line = ' ' * offset + '* %s' % package
|
||||
line = " " * offset + "* %s" % package
|
||||
if all_archs and set(arch) != all_archs:
|
||||
line += ' [%s]' % ' '.join(sorted(arch))
|
||||
line += " [%s]" % " ".join(sorted(arch))
|
||||
if dependency:
|
||||
if len(line) < 30:
|
||||
line += ' ' * (30 - len(line))
|
||||
line += ' (for %s)' % dependency
|
||||
line += " " * (30 - len(line))
|
||||
line += " (for %s)" % dependency
|
||||
Logger.info(line)
|
||||
data = values.get(package)
|
||||
if data:
|
||||
offset = offset + 1
|
||||
for rdeps in data.values():
|
||||
for rdep in rdeps:
|
||||
log_package(values,
|
||||
rdep['Package'],
|
||||
rdep.get('Architectures', all_archs),
|
||||
rdep.get('Dependency'),
|
||||
offset)
|
||||
log_package(
|
||||
values,
|
||||
rdep["Package"],
|
||||
rdep.get("Architectures", all_archs),
|
||||
rdep.get("Dependency"),
|
||||
offset,
|
||||
)
|
||||
|
||||
all_archs = set()
|
||||
# This isn't accurate, but we make up for it by displaying what we found
|
||||
for data in values.values():
|
||||
for rdeps in data.values():
|
||||
for rdep in rdeps:
|
||||
if 'Architectures' in rdep:
|
||||
all_archs.update(rdep['Architectures'])
|
||||
if "Architectures" in rdep:
|
||||
all_archs.update(rdep["Architectures"])
|
||||
|
||||
for field, rdeps in values[package].items():
|
||||
Logger.info(field)
|
||||
rdeps.sort(key=lambda x: x['Package'])
|
||||
rdeps.sort(key=lambda x: x["Package"])
|
||||
for rdep in rdeps:
|
||||
log_package(values,
|
||||
rdep['Package'],
|
||||
rdep.get('Architectures', all_archs),
|
||||
rdep.get('Dependency'))
|
||||
log_package(
|
||||
values,
|
||||
rdep["Package"],
|
||||
rdep.get("Architectures", all_archs),
|
||||
rdep.get("Dependency"),
|
||||
)
|
||||
Logger.info("")
|
||||
|
||||
if all_archs:
|
||||
Logger.info("Packages without architectures listed are "
|
||||
"reverse-dependencies in: %s"
|
||||
% ', '.join(sorted(list(all_archs))))
|
||||
Logger.info(
|
||||
"Packages without architectures listed are "
|
||||
"reverse-dependencies in: %s" % ", ".join(sorted(list(all_archs)))
|
||||
)
|
||||
|
||||
|
||||
def display_consise(values):
|
||||
@ -201,10 +239,10 @@ def display_consise(values):
|
||||
for data in values.values():
|
||||
for rdeps in data.values():
|
||||
for rdep in rdeps:
|
||||
result.add(rdep['Package'])
|
||||
result.add(rdep["Package"])
|
||||
|
||||
Logger.info('\n'.join(sorted(list(result))))
|
||||
Logger.info("\n".join(sorted(list(result))))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
@ -7,4 +7,4 @@ set -eu
|
||||
PYTHON_SCRIPTS=$(grep -l -r '^#! */usr/bin/python3$' .)
|
||||
|
||||
echo "Running flake8..."
|
||||
flake8 --max-line-length=99 . $PYTHON_SCRIPTS
|
||||
flake8 --max-line-length=99 --ignore=E203,W503 . $PYTHON_SCRIPTS
|
||||
|
@ -22,43 +22,42 @@ import os
|
||||
import time
|
||||
import urllib.request
|
||||
|
||||
from ubuntutools.lp.lpapicache import (Distribution, Launchpad,
|
||||
PackageNotFoundException)
|
||||
from ubuntutools.lp.lpapicache import Distribution, Launchpad, PackageNotFoundException
|
||||
|
||||
from ubuntutools import getLogger
|
||||
|
||||
Logger = getLogger()
|
||||
|
||||
DATA_URL = 'http://qa.ubuntuwire.org/ubuntu-seeded-packages/seeded.json.gz'
|
||||
DATA_URL = "http://qa.ubuntuwire.org/ubuntu-seeded-packages/seeded.json.gz"
|
||||
|
||||
|
||||
def load_index(url):
|
||||
'''Download a new copy of the image contents index, if necessary,
|
||||
"""Download a new copy of the image contents index, if necessary,
|
||||
and read it.
|
||||
'''
|
||||
cachedir = os.path.expanduser('~/.cache/ubuntu-dev-tools')
|
||||
fn = os.path.join(cachedir, 'seeded.json.gz')
|
||||
"""
|
||||
cachedir = os.path.expanduser("~/.cache/ubuntu-dev-tools")
|
||||
fn = os.path.join(cachedir, "seeded.json.gz")
|
||||
|
||||
if (not os.path.isfile(fn)
|
||||
or time.time() - os.path.getmtime(fn) > 60 * 60 * 2):
|
||||
if not os.path.isfile(fn) or time.time() - os.path.getmtime(fn) > 60 * 60 * 2:
|
||||
if not os.path.isdir(cachedir):
|
||||
os.makedirs(cachedir)
|
||||
urllib.request.urlretrieve(url, fn)
|
||||
|
||||
try:
|
||||
with gzip.open(fn, 'r') as f:
|
||||
with gzip.open(fn, "r") as f:
|
||||
return json.load(f)
|
||||
except Exception as e:
|
||||
Logger.error("Unable to parse seed data: %s. "
|
||||
"Deleting cached data, please try again.",
|
||||
str(e))
|
||||
Logger.error(
|
||||
"Unable to parse seed data: %s. Deleting cached data, please try again.", str(e)
|
||||
)
|
||||
os.unlink(fn)
|
||||
|
||||
|
||||
def resolve_binaries(sources):
|
||||
'''Return a dict of source:binaries for all binary packages built by
|
||||
"""Return a dict of source:binaries for all binary packages built by
|
||||
sources
|
||||
'''
|
||||
archive = Distribution('ubuntu').getArchive()
|
||||
"""
|
||||
archive = Distribution("ubuntu").getArchive()
|
||||
binaries = {}
|
||||
for source in sources:
|
||||
try:
|
||||
@ -66,28 +65,26 @@ def resolve_binaries(sources):
|
||||
except PackageNotFoundException as e:
|
||||
Logger.error(str(e))
|
||||
continue
|
||||
binaries[source] = sorted(set(bpph.getPackageName()
|
||||
for bpph in spph.getBinaries()))
|
||||
binaries[source] = sorted(set(bpph.getPackageName() for bpph in spph.getBinaries()))
|
||||
|
||||
return binaries
|
||||
|
||||
|
||||
def present_on(appearences):
|
||||
'''Format a list of (flavor, type) tuples into a human-readable string'''
|
||||
"""Format a list of (flavor, type) tuples into a human-readable string"""
|
||||
present = collections.defaultdict(set)
|
||||
for flavor, type_ in appearences:
|
||||
present[flavor].add(type_)
|
||||
for flavor, types in present.items():
|
||||
if len(types) > 1:
|
||||
types.discard('supported')
|
||||
output = [' %s: %s' % (flavor, ', '.join(sorted(types)))
|
||||
for flavor, types in present.items()]
|
||||
types.discard("supported")
|
||||
output = [" %s: %s" % (flavor, ", ".join(sorted(types))) for flavor, types in present.items()]
|
||||
output.sort()
|
||||
return '\n'.join(output)
|
||||
return "\n".join(output)
|
||||
|
||||
|
||||
def output_binaries(index, binaries):
|
||||
'''Print binaries found in index'''
|
||||
"""Print binaries found in index"""
|
||||
for binary in binaries:
|
||||
if binary in index:
|
||||
Logger.info("%s is seeded in:" % binary)
|
||||
@ -97,13 +94,14 @@ def output_binaries(index, binaries):
|
||||
|
||||
|
||||
def output_by_source(index, by_source):
|
||||
'''Logger.Info(binaries found in index. Grouped by source'''
|
||||
"""Logger.Info(binaries found in index. Grouped by source"""
|
||||
for source, binaries in by_source.items():
|
||||
seen = False
|
||||
if not binaries:
|
||||
Logger.info("Status unknown: No binary packages built by the latest "
|
||||
"%s.\nTry again using -b and the expected binary packages."
|
||||
% source)
|
||||
Logger.info(
|
||||
"Status unknown: No binary packages built by the latest "
|
||||
"%s.\nTry again using -b and the expected binary packages." % source
|
||||
)
|
||||
continue
|
||||
for binary in binaries:
|
||||
if binary in index:
|
||||
@ -115,16 +113,22 @@ def output_by_source(index, by_source):
|
||||
|
||||
|
||||
def main():
|
||||
'''Query which images the specified packages are on'''
|
||||
parser = optparse.OptionParser('%prog [options] package...')
|
||||
parser.add_option('-b', '--binary',
|
||||
default=False, action='store_true',
|
||||
help="Binary packages are being specified, "
|
||||
"not source packages (fast)")
|
||||
parser.add_option('-u', '--data-url', metavar='URL',
|
||||
default=DATA_URL,
|
||||
help='URL for the seeded packages index. '
|
||||
'Default: UbuntuWire')
|
||||
"""Query which images the specified packages are on"""
|
||||
parser = optparse.OptionParser("%prog [options] package...")
|
||||
parser.add_option(
|
||||
"-b",
|
||||
"--binary",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Binary packages are being specified, not source packages (fast)",
|
||||
)
|
||||
parser.add_option(
|
||||
"-u",
|
||||
"--data-url",
|
||||
metavar="URL",
|
||||
default=DATA_URL,
|
||||
help="URL for the seeded packages index. Default: UbuntuWire",
|
||||
)
|
||||
options, args = parser.parse_args()
|
||||
|
||||
if len(args) < 1:
|
||||
@ -141,5 +145,5 @@ def main():
|
||||
output_by_source(index, binaries)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
110
setup.py
110
setup.py
@ -21,74 +21,74 @@ def make_pep440_compliant(version: str) -> str:
|
||||
# look/set what version we have
|
||||
changelog = "debian/changelog"
|
||||
if os.path.exists(changelog):
|
||||
head = open(changelog, 'r', encoding='utf-8').readline()
|
||||
head = open(changelog, "r", encoding="utf-8").readline()
|
||||
match = re.compile(r".*\((.*)\).*").match(head)
|
||||
if match:
|
||||
version = match.group(1)
|
||||
|
||||
scripts = [
|
||||
'backportpackage',
|
||||
'bitesize',
|
||||
'check-mir',
|
||||
'check-symbols',
|
||||
'dch-repeat',
|
||||
'grab-merge',
|
||||
'grep-merges',
|
||||
'import-bug-from-debian',
|
||||
'merge-changelog',
|
||||
'mk-sbuild',
|
||||
'pbuilder-dist',
|
||||
'pbuilder-dist-simple',
|
||||
'pull-pkg',
|
||||
'pull-debian-debdiff',
|
||||
'pull-debian-source',
|
||||
'pull-debian-debs',
|
||||
'pull-debian-ddebs',
|
||||
'pull-debian-udebs',
|
||||
'pull-lp-source',
|
||||
'pull-lp-debs',
|
||||
'pull-lp-ddebs',
|
||||
'pull-lp-udebs',
|
||||
'pull-ppa-source',
|
||||
'pull-ppa-debs',
|
||||
'pull-ppa-ddebs',
|
||||
'pull-ppa-udebs',
|
||||
'pull-uca-source',
|
||||
'pull-uca-debs',
|
||||
'pull-uca-ddebs',
|
||||
'pull-uca-udebs',
|
||||
'requestbackport',
|
||||
'requestsync',
|
||||
'reverse-depends',
|
||||
'seeded-in-ubuntu',
|
||||
'setup-packaging-environment',
|
||||
'sponsor-patch',
|
||||
'submittodebian',
|
||||
'syncpackage',
|
||||
'ubuntu-build',
|
||||
'ubuntu-iso',
|
||||
'ubuntu-upload-permission',
|
||||
'update-maintainer',
|
||||
"backportpackage",
|
||||
"bitesize",
|
||||
"check-mir",
|
||||
"check-symbols",
|
||||
"dch-repeat",
|
||||
"grab-merge",
|
||||
"grep-merges",
|
||||
"import-bug-from-debian",
|
||||
"merge-changelog",
|
||||
"mk-sbuild",
|
||||
"pbuilder-dist",
|
||||
"pbuilder-dist-simple",
|
||||
"pull-pkg",
|
||||
"pull-debian-debdiff",
|
||||
"pull-debian-source",
|
||||
"pull-debian-debs",
|
||||
"pull-debian-ddebs",
|
||||
"pull-debian-udebs",
|
||||
"pull-lp-source",
|
||||
"pull-lp-debs",
|
||||
"pull-lp-ddebs",
|
||||
"pull-lp-udebs",
|
||||
"pull-ppa-source",
|
||||
"pull-ppa-debs",
|
||||
"pull-ppa-ddebs",
|
||||
"pull-ppa-udebs",
|
||||
"pull-uca-source",
|
||||
"pull-uca-debs",
|
||||
"pull-uca-ddebs",
|
||||
"pull-uca-udebs",
|
||||
"requestbackport",
|
||||
"requestsync",
|
||||
"reverse-depends",
|
||||
"seeded-in-ubuntu",
|
||||
"setup-packaging-environment",
|
||||
"sponsor-patch",
|
||||
"submittodebian",
|
||||
"syncpackage",
|
||||
"ubuntu-build",
|
||||
"ubuntu-iso",
|
||||
"ubuntu-upload-permission",
|
||||
"update-maintainer",
|
||||
]
|
||||
data_files = [
|
||||
('share/bash-completion/completions', glob.glob("bash_completion/*")),
|
||||
('share/man/man1', glob.glob("doc/*.1")),
|
||||
('share/man/man5', glob.glob("doc/*.5")),
|
||||
('share/ubuntu-dev-tools', ['enforced-editing-wrapper']),
|
||||
("share/bash-completion/completions", glob.glob("bash_completion/*")),
|
||||
("share/man/man1", glob.glob("doc/*.1")),
|
||||
("share/man/man5", glob.glob("doc/*.5")),
|
||||
("share/ubuntu-dev-tools", ["enforced-editing-wrapper"]),
|
||||
]
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
setup(
|
||||
name='ubuntu-dev-tools',
|
||||
name="ubuntu-dev-tools",
|
||||
version=make_pep440_compliant(version),
|
||||
scripts=scripts,
|
||||
packages=[
|
||||
'ubuntutools',
|
||||
'ubuntutools/lp',
|
||||
'ubuntutools/requestsync',
|
||||
'ubuntutools/sponsor_patch',
|
||||
'ubuntutools/test',
|
||||
"ubuntutools",
|
||||
"ubuntutools/lp",
|
||||
"ubuntutools/requestsync",
|
||||
"ubuntutools/sponsor_patch",
|
||||
"ubuntutools/test",
|
||||
],
|
||||
data_files=data_files,
|
||||
test_suite='ubuntutools.test',
|
||||
test_suite="ubuntutools.test",
|
||||
)
|
||||
|
139
sponsor-patch
139
sponsor-patch
@ -26,45 +26,103 @@ from ubuntutools.config import UDTConfig
|
||||
from ubuntutools.sponsor_patch.sponsor_patch import sponsor_patch, check_dependencies
|
||||
|
||||
from ubuntutools import getLogger
|
||||
|
||||
Logger = getLogger()
|
||||
|
||||
|
||||
def parse(script_name):
|
||||
"""Parse the command line parameters."""
|
||||
usage = ("%s [options] <bug number>\n" % (script_name)
|
||||
+ "One of --upload, --workdir, or --sponsor must be specified.")
|
||||
usage = (
|
||||
"%s [options] <bug number>\n" % (script_name)
|
||||
+ "One of --upload, --workdir, or --sponsor must be specified."
|
||||
)
|
||||
epilog = "See %s(1) for more info." % (script_name)
|
||||
parser = optparse.OptionParser(usage=usage, epilog=epilog)
|
||||
|
||||
parser.add_option("-b", "--build", dest="build",
|
||||
help="Build the package with the specified builder.",
|
||||
action="store_true", default=False)
|
||||
parser.add_option("-B", "--builder", dest="builder", default=None,
|
||||
help="Specify the package builder (default pbuilder)")
|
||||
parser.add_option("-e", "--edit",
|
||||
help="launch sub-shell to allow editing of the patch",
|
||||
dest="edit", action="store_true", default=False)
|
||||
parser.add_option("-k", "--key", dest="keyid", default=None,
|
||||
help="Specify the key ID to be used for signing.")
|
||||
parser.add_option("-l", "--lpinstance", dest="lpinstance", default=None,
|
||||
help="Launchpad instance to connect to "
|
||||
"(default: production)",
|
||||
metavar="INSTANCE")
|
||||
parser.add_option("--no-conf", dest="no_conf", default=False,
|
||||
help="Don't read config files or environment variables.",
|
||||
action="store_true")
|
||||
parser.add_option("-s", "--sponsor", help="sponsoring; equals -b -u ubuntu",
|
||||
dest="sponsoring", action="store_true", default=False)
|
||||
parser.add_option("-u", "--upload", dest="upload", default=None,
|
||||
help="Specify an upload destination (default none).")
|
||||
parser.add_option("-U", "--update", dest="update", default=False,
|
||||
action="store_true",
|
||||
help="Update the build environment before building.")
|
||||
parser.add_option("-v", "--verbose", help="print more information",
|
||||
dest="verbose", action="store_true", default=False)
|
||||
parser.add_option("-w", "--workdir", dest="workdir", default=None,
|
||||
help="Specify a working directory (default is a "
|
||||
"temporary directory, deleted afterwards).")
|
||||
parser.add_option(
|
||||
"-b",
|
||||
"--build",
|
||||
dest="build",
|
||||
help="Build the package with the specified builder.",
|
||||
action="store_true",
|
||||
default=False,
|
||||
)
|
||||
parser.add_option(
|
||||
"-B",
|
||||
"--builder",
|
||||
dest="builder",
|
||||
default=None,
|
||||
help="Specify the package builder (default pbuilder)",
|
||||
)
|
||||
parser.add_option(
|
||||
"-e",
|
||||
"--edit",
|
||||
help="launch sub-shell to allow editing of the patch",
|
||||
dest="edit",
|
||||
action="store_true",
|
||||
default=False,
|
||||
)
|
||||
parser.add_option(
|
||||
"-k",
|
||||
"--key",
|
||||
dest="keyid",
|
||||
default=None,
|
||||
help="Specify the key ID to be used for signing.",
|
||||
)
|
||||
parser.add_option(
|
||||
"-l",
|
||||
"--lpinstance",
|
||||
dest="lpinstance",
|
||||
default=None,
|
||||
help="Launchpad instance to connect to (default: production)",
|
||||
metavar="INSTANCE",
|
||||
)
|
||||
parser.add_option(
|
||||
"--no-conf",
|
||||
dest="no_conf",
|
||||
default=False,
|
||||
help="Don't read config files or environment variables.",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_option(
|
||||
"-s",
|
||||
"--sponsor",
|
||||
help="sponsoring; equals -b -u ubuntu",
|
||||
dest="sponsoring",
|
||||
action="store_true",
|
||||
default=False,
|
||||
)
|
||||
parser.add_option(
|
||||
"-u",
|
||||
"--upload",
|
||||
dest="upload",
|
||||
default=None,
|
||||
help="Specify an upload destination (default none).",
|
||||
)
|
||||
parser.add_option(
|
||||
"-U",
|
||||
"--update",
|
||||
dest="update",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Update the build environment before building.",
|
||||
)
|
||||
parser.add_option(
|
||||
"-v",
|
||||
"--verbose",
|
||||
help="print more information",
|
||||
dest="verbose",
|
||||
action="store_true",
|
||||
default=False,
|
||||
)
|
||||
parser.add_option(
|
||||
"-w",
|
||||
"--workdir",
|
||||
dest="workdir",
|
||||
default=None,
|
||||
help="Specify a working directory (default is a "
|
||||
"temporary directory, deleted afterwards).",
|
||||
)
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
if options.verbose:
|
||||
@ -113,19 +171,26 @@ def main():
|
||||
sys.exit(1)
|
||||
|
||||
if not options.upload and not options.workdir:
|
||||
Logger.error("Please specify either a working directory or an upload "
|
||||
"target!")
|
||||
Logger.error("Please specify either a working directory or an upload target!")
|
||||
sys.exit(1)
|
||||
|
||||
if options.workdir is None:
|
||||
workdir = tempfile.mkdtemp(prefix=script_name+"-")
|
||||
workdir = tempfile.mkdtemp(prefix=script_name + "-")
|
||||
else:
|
||||
workdir = options.workdir
|
||||
|
||||
try:
|
||||
sponsor_patch(bug_number, options.build, builder, options.edit,
|
||||
options.keyid, options.lpinstance, options.update,
|
||||
options.upload, workdir)
|
||||
sponsor_patch(
|
||||
bug_number,
|
||||
options.build,
|
||||
builder,
|
||||
options.edit,
|
||||
options.keyid,
|
||||
options.lpinstance,
|
||||
options.update,
|
||||
options.upload,
|
||||
workdir,
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
Logger.error("User abort.")
|
||||
sys.exit(2)
|
||||
|
153
submittodebian
153
submittodebian
@ -40,13 +40,14 @@ from ubuntutools.question import YesNoQuestion, EditFile
|
||||
from ubuntutools.update_maintainer import update_maintainer, restore_maintainer
|
||||
|
||||
from ubuntutools import getLogger
|
||||
|
||||
Logger = getLogger()
|
||||
|
||||
|
||||
def get_most_recent_debian_version(changelog):
|
||||
for block in changelog:
|
||||
version = block.version.full_version
|
||||
if not re.search('(ubuntu|build)', version):
|
||||
if not re.search("(ubuntu|build)", version):
|
||||
return version
|
||||
|
||||
|
||||
@ -65,19 +66,20 @@ In Ubuntu, the attached patch was applied to achieve the following:
|
||||
%s
|
||||
|
||||
Thanks for considering the patch.
|
||||
""" % ("\n".join([a for a in entry.changes()]))
|
||||
""" % (
|
||||
"\n".join([a for a in entry.changes()])
|
||||
)
|
||||
return msg
|
||||
|
||||
|
||||
def build_source_package():
|
||||
if os.path.isdir('.bzr'):
|
||||
cmd = ['bzr', 'bd', '--builder=dpkg-buildpackage', '-S',
|
||||
'--', '-uc', '-us', '-nc']
|
||||
if os.path.isdir(".bzr"):
|
||||
cmd = ["bzr", "bd", "--builder=dpkg-buildpackage", "-S", "--", "-uc", "-us", "-nc"]
|
||||
else:
|
||||
cmd = ['dpkg-buildpackage', '-S', '-uc', '-us', '-nc']
|
||||
cmd = ["dpkg-buildpackage", "-S", "-uc", "-us", "-nc"]
|
||||
env = os.environ.copy()
|
||||
# Unset DEBEMAIL in case there's an @ubuntu.com e-mail address
|
||||
env.pop('DEBEMAIL', None)
|
||||
env.pop("DEBEMAIL", None)
|
||||
check_call(cmd, env=env)
|
||||
|
||||
|
||||
@ -88,30 +90,34 @@ def gen_debdiff(tmpdir, changelog):
|
||||
newver = next(changelog_it).version
|
||||
oldver = next(changelog_it).version
|
||||
|
||||
debdiff = os.path.join(tmpdir, '%s_%s.debdiff' % (pkg, newver))
|
||||
debdiff = os.path.join(tmpdir, "%s_%s.debdiff" % (pkg, newver))
|
||||
|
||||
diff_cmd = ['bzr', 'diff', '-r', 'tag:' + str(oldver)]
|
||||
diff_cmd = ["bzr", "diff", "-r", "tag:" + str(oldver)]
|
||||
if call(diff_cmd, stdout=DEVNULL, stderr=DEVNULL) == 1:
|
||||
Logger.info("Extracting bzr diff between %s and %s" % (oldver, newver))
|
||||
else:
|
||||
if oldver.epoch is not None:
|
||||
oldver = str(oldver)[str(oldver).index(":") + 1:]
|
||||
oldver = str(oldver)[str(oldver).index(":") + 1 :]
|
||||
if newver.epoch is not None:
|
||||
newver = str(newver)[str(newver).index(":") + 1:]
|
||||
newver = str(newver)[str(newver).index(":") + 1 :]
|
||||
|
||||
olddsc = '../%s_%s.dsc' % (pkg, oldver)
|
||||
newdsc = '../%s_%s.dsc' % (pkg, newver)
|
||||
olddsc = "../%s_%s.dsc" % (pkg, oldver)
|
||||
newdsc = "../%s_%s.dsc" % (pkg, newver)
|
||||
|
||||
check_file(olddsc)
|
||||
check_file(newdsc)
|
||||
|
||||
Logger.info("Generating debdiff between %s and %s" % (oldver, newver))
|
||||
diff_cmd = ['debdiff', olddsc, newdsc]
|
||||
diff_cmd = ["debdiff", olddsc, newdsc]
|
||||
|
||||
with Popen(diff_cmd, stdout=PIPE, encoding='utf-8') as diff:
|
||||
with open(debdiff, 'w', encoding='utf-8') as debdiff_f:
|
||||
run(['filterdiff', '-x', '*changelog*'],
|
||||
stdin=diff.stdout, stdout=debdiff_f, encoding='utf-8')
|
||||
with Popen(diff_cmd, stdout=PIPE, encoding="utf-8") as diff:
|
||||
with open(debdiff, "w", encoding="utf-8") as debdiff_f:
|
||||
run(
|
||||
["filterdiff", "-x", "*changelog*"],
|
||||
stdin=diff.stdout,
|
||||
stdout=debdiff_f,
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
return debdiff
|
||||
|
||||
@ -131,57 +137,65 @@ def submit_bugreport(body, debdiff, deb_version, changelog):
|
||||
devel = UbuntuDistroInfo().devel()
|
||||
except DistroDataOutdated as e:
|
||||
Logger.info(str(e))
|
||||
devel = ''
|
||||
devel = ""
|
||||
|
||||
if os.path.dirname(sys.argv[0]).startswith('/usr/bin'):
|
||||
editor_path = '/usr/share/ubuntu-dev-tools'
|
||||
if os.path.dirname(sys.argv[0]).startswith("/usr/bin"):
|
||||
editor_path = "/usr/share/ubuntu-dev-tools"
|
||||
else:
|
||||
editor_path = os.path.dirname(sys.argv[0])
|
||||
env = dict(os.environ.items())
|
||||
if 'EDITOR' in env:
|
||||
env['UDT_EDIT_WRAPPER_EDITOR'] = env['EDITOR']
|
||||
if 'VISUAL' in env:
|
||||
env['UDT_EDIT_WRAPPER_VISUAL'] = env['VISUAL']
|
||||
env['EDITOR'] = os.path.join(editor_path, 'enforced-editing-wrapper')
|
||||
env['VISUAL'] = os.path.join(editor_path, 'enforced-editing-wrapper')
|
||||
env['UDT_EDIT_WRAPPER_TEMPLATE_RE'] = (
|
||||
'.*REPLACE THIS WITH ACTUAL INFORMATION.*')
|
||||
env['UDT_EDIT_WRAPPER_FILE_DESCRIPTION'] = 'bug report'
|
||||
if "EDITOR" in env:
|
||||
env["UDT_EDIT_WRAPPER_EDITOR"] = env["EDITOR"]
|
||||
if "VISUAL" in env:
|
||||
env["UDT_EDIT_WRAPPER_VISUAL"] = env["VISUAL"]
|
||||
env["EDITOR"] = os.path.join(editor_path, "enforced-editing-wrapper")
|
||||
env["VISUAL"] = os.path.join(editor_path, "enforced-editing-wrapper")
|
||||
env["UDT_EDIT_WRAPPER_TEMPLATE_RE"] = ".*REPLACE THIS WITH ACTUAL INFORMATION.*"
|
||||
env["UDT_EDIT_WRAPPER_FILE_DESCRIPTION"] = "bug report"
|
||||
|
||||
# In external mua mode, attachments are lost (Reportbug bug: #679907)
|
||||
internal_mua = True
|
||||
for cfgfile in ('/etc/reportbug.conf', '~/.reportbugrc'):
|
||||
for cfgfile in ("/etc/reportbug.conf", "~/.reportbugrc"):
|
||||
cfgfile = os.path.expanduser(cfgfile)
|
||||
if not os.path.exists(cfgfile):
|
||||
continue
|
||||
with open(cfgfile, 'r') as f:
|
||||
with open(cfgfile, "r") as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line in ('gnus', 'mutt', 'nmh') or line.startswith('mua '):
|
||||
if line in ("gnus", "mutt", "nmh") or line.startswith("mua "):
|
||||
internal_mua = False
|
||||
break
|
||||
|
||||
cmd = ('reportbug',
|
||||
'--no-check-available',
|
||||
'--no-check-installed',
|
||||
'--pseudo-header', 'User: ubuntu-devel@lists.ubuntu.com',
|
||||
'--pseudo-header', 'Usertags: origin-ubuntu %s ubuntu-patch'
|
||||
% devel,
|
||||
'--tag', 'patch',
|
||||
'--bts', 'debian',
|
||||
'--include', body,
|
||||
'--attach' if internal_mua else '--include', debdiff,
|
||||
'--package-version', deb_version,
|
||||
changelog.package)
|
||||
cmd = (
|
||||
"reportbug",
|
||||
"--no-check-available",
|
||||
"--no-check-installed",
|
||||
"--pseudo-header",
|
||||
"User: ubuntu-devel@lists.ubuntu.com",
|
||||
"--pseudo-header",
|
||||
"Usertags: origin-ubuntu %s ubuntu-patch" % devel,
|
||||
"--tag",
|
||||
"patch",
|
||||
"--bts",
|
||||
"debian",
|
||||
"--include",
|
||||
body,
|
||||
"--attach" if internal_mua else "--include",
|
||||
debdiff,
|
||||
"--package-version",
|
||||
deb_version,
|
||||
changelog.package,
|
||||
)
|
||||
check_call(cmd, env=env)
|
||||
|
||||
|
||||
def check_reportbug_config():
|
||||
fn = os.path.expanduser('~/.reportbugrc')
|
||||
fn = os.path.expanduser("~/.reportbugrc")
|
||||
if os.path.exists(fn):
|
||||
return
|
||||
email = ubu_email()[1]
|
||||
reportbugrc = """# Reportbug configuration generated by submittodebian(1)
|
||||
reportbugrc = (
|
||||
"""# Reportbug configuration generated by submittodebian(1)
|
||||
# See reportbug.conf(5) for the configuration file format.
|
||||
|
||||
# Use Debian's reportbug SMTP Server:
|
||||
@ -195,12 +209,15 @@ no-cc
|
||||
#smtphost smtp.googlemail.com:587
|
||||
#smtpuser "<your address>@gmail.com"
|
||||
#smtptls
|
||||
""" % email
|
||||
"""
|
||||
% email
|
||||
)
|
||||
|
||||
with open(fn, 'w') as f:
|
||||
with open(fn, "w") as f:
|
||||
f.write(reportbugrc)
|
||||
|
||||
Logger.info("""\
|
||||
Logger.info(
|
||||
"""\
|
||||
You have not configured reportbug. Assuming this is the first time you have
|
||||
used it. Writing a ~/.reportbugrc that will use Debian's mail server, and CC
|
||||
the bug to you at <%s>
|
||||
@ -211,26 +228,32 @@ the bug to you at <%s>
|
||||
|
||||
If this is not correct, please exit now and edit ~/.reportbugrc or run
|
||||
reportbug --configure for its configuration wizard.
|
||||
""" % (email, reportbugrc.strip()))
|
||||
"""
|
||||
% (email, reportbugrc.strip())
|
||||
)
|
||||
|
||||
if YesNoQuestion().ask("Continue submitting this bug", "yes") == "no":
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
description = 'Submit the Ubuntu changes in a package to Debian. ' + \
|
||||
'Run inside an unpacked Ubuntu source package.'
|
||||
description = (
|
||||
"Submit the Ubuntu changes in a package to Debian. "
|
||||
+ "Run inside an unpacked Ubuntu source package."
|
||||
)
|
||||
parser = optparse.OptionParser(description=description)
|
||||
parser.parse_args()
|
||||
|
||||
if not os.path.exists('/usr/bin/reportbug'):
|
||||
Logger.error("This utility requires the «reportbug» package, which isn't "
|
||||
"currently installed.")
|
||||
if not os.path.exists("/usr/bin/reportbug"):
|
||||
Logger.error(
|
||||
"This utility requires the «reportbug» package, which isn't currently installed."
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
check_reportbug_config()
|
||||
changelog_file = (check_file('debian/changelog', critical=False) or
|
||||
check_file('../debian/changelog'))
|
||||
changelog_file = check_file("debian/changelog", critical=False) or check_file(
|
||||
"../debian/changelog"
|
||||
)
|
||||
with open(changelog_file) as f:
|
||||
changelog = Changelog(f.read())
|
||||
|
||||
@ -238,13 +261,13 @@ def main():
|
||||
bug_body = get_bug_body(changelog)
|
||||
|
||||
tmpdir = mkdtemp()
|
||||
body = os.path.join(tmpdir, 'bug_body')
|
||||
with open(body, 'wb') as f:
|
||||
f.write(bug_body.encode('utf-8'))
|
||||
body = os.path.join(tmpdir, "bug_body")
|
||||
with open(body, "wb") as f:
|
||||
f.write(bug_body.encode("utf-8"))
|
||||
|
||||
restore_maintainer('debian')
|
||||
restore_maintainer("debian")
|
||||
build_source_package()
|
||||
update_maintainer('debian')
|
||||
update_maintainer("debian")
|
||||
|
||||
debdiff = gen_debdiff(tmpdir, changelog)
|
||||
|
||||
@ -252,7 +275,7 @@ def main():
|
||||
# reverted in the most recent build
|
||||
build_source_package()
|
||||
|
||||
EditFile(debdiff, 'debdiff').edit(optional=True)
|
||||
EditFile(debdiff, "debdiff").edit(optional=True)
|
||||
|
||||
submit_bugreport(body, debdiff, deb_version, changelog)
|
||||
os.unlink(body)
|
||||
@ -260,5 +283,5 @@ def main():
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
615
syncpackage
615
syncpackage
@ -32,27 +32,30 @@ import urllib.request
|
||||
|
||||
from lazr.restfulclient.errors import HTTPError
|
||||
|
||||
from ubuntutools.archive import (DebianSourcePackage, UbuntuSourcePackage,
|
||||
DownloadError)
|
||||
from ubuntutools.archive import DebianSourcePackage, UbuntuSourcePackage, DownloadError
|
||||
from ubuntutools.config import UDTConfig, ubu_email
|
||||
from ubuntutools.lp import udtexceptions
|
||||
from ubuntutools.lp.lpapicache import (Distribution, Launchpad, PersonTeam,
|
||||
SourcePackagePublishingHistory)
|
||||
from ubuntutools.lp.lpapicache import (
|
||||
Distribution,
|
||||
Launchpad,
|
||||
PersonTeam,
|
||||
SourcePackagePublishingHistory,
|
||||
)
|
||||
from ubuntutools.misc import split_release_pocket
|
||||
from ubuntutools.question import YesNoQuestion
|
||||
from ubuntutools.requestsync.mail import (
|
||||
get_debian_srcpkg as requestsync_mail_get_debian_srcpkg)
|
||||
from ubuntutools.requestsync.mail import get_debian_srcpkg as requestsync_mail_get_debian_srcpkg
|
||||
from ubuntutools.requestsync.lp import get_debian_srcpkg, get_ubuntu_srcpkg
|
||||
from ubuntutools.version import Version
|
||||
|
||||
from ubuntutools import getLogger
|
||||
|
||||
Logger = getLogger()
|
||||
|
||||
|
||||
def remove_signature(dscname):
|
||||
'''Removes the signature from a .dsc file if the .dsc file is signed.'''
|
||||
"""Removes the signature from a .dsc file if the .dsc file is signed."""
|
||||
|
||||
dsc_file = open(dscname, encoding='utf-8')
|
||||
dsc_file = open(dscname, encoding="utf-8")
|
||||
if dsc_file.readline().strip() == "-----BEGIN PGP SIGNED MESSAGE-----":
|
||||
unsigned_file = []
|
||||
# search until begin of body found
|
||||
@ -67,14 +70,14 @@ def remove_signature(dscname):
|
||||
unsigned_file.append(line)
|
||||
|
||||
dsc_file.close()
|
||||
dsc_file = open(dscname, "w", encoding='utf-8')
|
||||
dsc_file = open(dscname, "w", encoding="utf-8")
|
||||
dsc_file.writelines(unsigned_file)
|
||||
dsc_file.close()
|
||||
|
||||
|
||||
def add_fixed_bugs(changes, bugs):
|
||||
'''Add additional Launchpad bugs to the list of fixed bugs in changes
|
||||
file.'''
|
||||
"""Add additional Launchpad bugs to the list of fixed bugs in changes
|
||||
file."""
|
||||
|
||||
changes = [line for line in changes.split("\n") if line.strip() != ""]
|
||||
# Remove duplicates
|
||||
@ -93,12 +96,23 @@ def add_fixed_bugs(changes, bugs):
|
||||
return "\n".join(changes + [""])
|
||||
|
||||
|
||||
def sync_dsc(src_pkg, debian_dist, release, name, email, bugs, ubuntu_mirror,
|
||||
keyid=None, simulate=False, force=False, fakesync=False):
|
||||
'''Local sync, trying to emulate sync-source.py
|
||||
def sync_dsc(
|
||||
src_pkg,
|
||||
debian_dist,
|
||||
release,
|
||||
name,
|
||||
email,
|
||||
bugs,
|
||||
ubuntu_mirror,
|
||||
keyid=None,
|
||||
simulate=False,
|
||||
force=False,
|
||||
fakesync=False,
|
||||
):
|
||||
"""Local sync, trying to emulate sync-source.py
|
||||
Grabs a source package, replaces the .orig.tar with the one from Ubuntu,
|
||||
if necessary, writes a sync-appropriate .changes file, and signs it.
|
||||
'''
|
||||
"""
|
||||
|
||||
uploader = name + " <" + email + ">"
|
||||
|
||||
@ -106,52 +120,59 @@ def sync_dsc(src_pkg, debian_dist, release, name, email, bugs, ubuntu_mirror,
|
||||
|
||||
try:
|
||||
ubuntu_series, ubuntu_pocket = split_release_pocket(release)
|
||||
ubuntu_source = get_ubuntu_srcpkg(src_pkg.source, ubuntu_series,
|
||||
ubuntu_pocket)
|
||||
ubuntu_source = get_ubuntu_srcpkg(src_pkg.source, ubuntu_series, ubuntu_pocket)
|
||||
ubuntu_ver = Version(ubuntu_source.getVersion())
|
||||
ubu_pkg = UbuntuSourcePackage(src_pkg.source, ubuntu_ver.full_version,
|
||||
ubuntu_source.getComponent(),
|
||||
mirrors=[ubuntu_mirror])
|
||||
ubu_pkg = UbuntuSourcePackage(
|
||||
src_pkg.source,
|
||||
ubuntu_ver.full_version,
|
||||
ubuntu_source.getComponent(),
|
||||
mirrors=[ubuntu_mirror],
|
||||
)
|
||||
need_orig = ubuntu_ver.upstream_version != new_ver.upstream_version
|
||||
except udtexceptions.PackageNotFoundException:
|
||||
ubuntu_ver = Version('~')
|
||||
ubuntu_ver = Version("~")
|
||||
ubu_pkg = None
|
||||
need_orig = True
|
||||
Logger.info('%s does not exist in Ubuntu.', name)
|
||||
Logger.info("%s does not exist in Ubuntu.", name)
|
||||
|
||||
Logger.debug('Source %s: current version %s, new version %s',
|
||||
src_pkg.source, ubuntu_ver, new_ver)
|
||||
Logger.debug('Needs source tarball: %s', str(need_orig))
|
||||
Logger.debug(
|
||||
"Source %s: current version %s, new version %s", src_pkg.source, ubuntu_ver, new_ver
|
||||
)
|
||||
Logger.debug("Needs source tarball: %s", str(need_orig))
|
||||
|
||||
cur_ver = ubuntu_ver.get_related_debian_version()
|
||||
if ubuntu_ver.is_modified_in_ubuntu():
|
||||
if not force:
|
||||
Logger.error('--force is required to discard Ubuntu changes.')
|
||||
Logger.error("--force is required to discard Ubuntu changes.")
|
||||
sys.exit(1)
|
||||
|
||||
Logger.warning('Overwriting modified Ubuntu version %s, '
|
||||
'setting current version to %s',
|
||||
ubuntu_ver.full_version, cur_ver.full_version)
|
||||
Logger.warning(
|
||||
"Overwriting modified Ubuntu version %s, setting current version to %s",
|
||||
ubuntu_ver.full_version,
|
||||
cur_ver.full_version,
|
||||
)
|
||||
if simulate:
|
||||
return
|
||||
|
||||
try:
|
||||
src_pkg.pull()
|
||||
except DownloadError as e:
|
||||
Logger.error('Failed to download: %s', str(e))
|
||||
Logger.error("Failed to download: %s", str(e))
|
||||
sys.exit(1)
|
||||
src_pkg.unpack()
|
||||
|
||||
needs_fakesync = not (need_orig or ubu_pkg.verify_orig())
|
||||
|
||||
if needs_fakesync and fakesync:
|
||||
Logger.warning('Performing a fakesync')
|
||||
Logger.warning("Performing a fakesync")
|
||||
elif not needs_fakesync and fakesync:
|
||||
Logger.error('Fakesync not required, aborting.')
|
||||
Logger.error("Fakesync not required, aborting.")
|
||||
sys.exit(1)
|
||||
elif needs_fakesync and not fakesync:
|
||||
Logger.error('The checksums of the Debian and Ubuntu packages '
|
||||
'mismatch. A fake sync using --fakesync is required.')
|
||||
Logger.error(
|
||||
"The checksums of the Debian and Ubuntu packages "
|
||||
"mismatch. A fake sync using --fakesync is required."
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
if fakesync:
|
||||
@ -159,47 +180,50 @@ def sync_dsc(src_pkg, debian_dist, release, name, email, bugs, ubuntu_mirror,
|
||||
try:
|
||||
ubu_pkg.pull()
|
||||
except DownloadError as e:
|
||||
Logger.error('Failed to download: %s', str(e))
|
||||
Logger.error("Failed to download: %s", str(e))
|
||||
sys.exit(1)
|
||||
|
||||
# change into package directory
|
||||
directory = src_pkg.source + '-' + new_ver.upstream_version
|
||||
Logger.debug('cd' + directory)
|
||||
directory = src_pkg.source + "-" + new_ver.upstream_version
|
||||
Logger.debug("cd" + directory)
|
||||
os.chdir(directory)
|
||||
|
||||
# read Debian distribution from debian/changelog if not specified
|
||||
if debian_dist is None:
|
||||
line = open("debian/changelog", encoding='utf-8').readline()
|
||||
line = open("debian/changelog", encoding="utf-8").readline()
|
||||
debian_dist = line.split(" ")[2].strip(";")
|
||||
|
||||
if not fakesync:
|
||||
# create the changes file
|
||||
changes_filename = "%s_%s_source.changes" % \
|
||||
(src_pkg.source, new_ver.strip_epoch())
|
||||
cmd = ["dpkg-genchanges", "-S", "-v" + cur_ver.full_version,
|
||||
"-DDistribution=" + release,
|
||||
"-DOrigin=debian/" + debian_dist,
|
||||
"-e" + uploader]
|
||||
changes_filename = "%s_%s_source.changes" % (src_pkg.source, new_ver.strip_epoch())
|
||||
cmd = [
|
||||
"dpkg-genchanges",
|
||||
"-S",
|
||||
"-v" + cur_ver.full_version,
|
||||
"-DDistribution=" + release,
|
||||
"-DOrigin=debian/" + debian_dist,
|
||||
"-e" + uploader,
|
||||
]
|
||||
if need_orig:
|
||||
cmd.append("-sa")
|
||||
else:
|
||||
cmd.append("-sd")
|
||||
if not Logger.isEnabledFor(logging.DEBUG):
|
||||
cmd += ["-q"]
|
||||
Logger.debug(' '.join(cmd) + '> ../' + changes_filename)
|
||||
changes = subprocess.check_output(cmd, encoding='utf-8')
|
||||
Logger.debug(" ".join(cmd) + "> ../" + changes_filename)
|
||||
changes = subprocess.check_output(cmd, encoding="utf-8")
|
||||
|
||||
# Add additional bug numbers
|
||||
if len(bugs) > 0:
|
||||
changes = add_fixed_bugs(changes, bugs)
|
||||
|
||||
# remove extracted (temporary) files
|
||||
Logger.debug('cd ..')
|
||||
os.chdir('..')
|
||||
Logger.debug("cd ..")
|
||||
os.chdir("..")
|
||||
shutil.rmtree(directory, True)
|
||||
|
||||
# write changes file
|
||||
changes_file = open(changes_filename, "w", encoding='utf-8')
|
||||
changes_file = open(changes_filename, "w", encoding="utf-8")
|
||||
changes_file.writelines(changes)
|
||||
changes_file.close()
|
||||
|
||||
@ -209,48 +233,44 @@ def sync_dsc(src_pkg, debian_dist, release, name, email, bugs, ubuntu_mirror,
|
||||
cmd = ["debsign", changes_filename]
|
||||
if keyid is not None:
|
||||
cmd.insert(1, "-k" + keyid)
|
||||
Logger.debug(' '.join(cmd))
|
||||
Logger.debug(" ".join(cmd))
|
||||
subprocess.check_call(cmd)
|
||||
else:
|
||||
# Create fakesync changelog entry
|
||||
new_ver = Version(new_ver.full_version + "fakesync1")
|
||||
changes_filename = "%s_%s_source.changes" % \
|
||||
(src_pkg.source, new_ver.strip_epoch())
|
||||
changes_filename = "%s_%s_source.changes" % (src_pkg.source, new_ver.strip_epoch())
|
||||
if len(bugs) > 0:
|
||||
message = "Fake sync due to mismatching orig tarball (LP: %s)." % \
|
||||
(", ".join(["#" + str(b) for b in bugs]))
|
||||
message = "Fake sync due to mismatching orig tarball (LP: %s)." % (
|
||||
", ".join(["#" + str(b) for b in bugs])
|
||||
)
|
||||
else:
|
||||
message = "Fake sync due to mismatching orig tarball."
|
||||
cmd = ['dch', '-v', new_ver.full_version, '--force-distribution',
|
||||
'-D', release, message]
|
||||
env = {'DEBFULLNAME': name, 'DEBEMAIL': email}
|
||||
Logger.debug(' '.join(cmd))
|
||||
cmd = ["dch", "-v", new_ver.full_version, "--force-distribution", "-D", release, message]
|
||||
env = {"DEBFULLNAME": name, "DEBEMAIL": email}
|
||||
Logger.debug(" ".join(cmd))
|
||||
subprocess.check_call(cmd, env=env)
|
||||
|
||||
# update the Maintainer field
|
||||
cmd = ["update-maintainer"]
|
||||
if not Logger.isEnabledFor(logging.DEBUG):
|
||||
cmd.append("-q")
|
||||
Logger.debug(' '.join(cmd))
|
||||
Logger.debug(" ".join(cmd))
|
||||
subprocess.check_call(cmd)
|
||||
|
||||
# Build source package
|
||||
cmd = ["debuild", "--no-lintian", "-nc", "-S",
|
||||
"-v" + cur_ver.full_version]
|
||||
cmd = ["debuild", "--no-lintian", "-nc", "-S", "-v" + cur_ver.full_version]
|
||||
if need_orig:
|
||||
cmd += ['-sa']
|
||||
cmd += ["-sa"]
|
||||
if keyid:
|
||||
cmd += ["-k" + keyid]
|
||||
Logger.debug(' '.join(cmd))
|
||||
Logger.debug(" ".join(cmd))
|
||||
returncode = subprocess.call(cmd)
|
||||
if returncode != 0:
|
||||
Logger.error('Source-only build with debuild failed. '
|
||||
'Please check build log above.')
|
||||
Logger.error("Source-only build with debuild failed. Please check build log above.")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def fetch_source_pkg(package, dist, version, component, ubuntu_release,
|
||||
mirror):
|
||||
def fetch_source_pkg(package, dist, version, component, ubuntu_release, mirror):
|
||||
"""Download the specified source package.
|
||||
dist, version, component, mirror can all be None.
|
||||
"""
|
||||
@ -259,11 +279,11 @@ def fetch_source_pkg(package, dist, version, component, ubuntu_release,
|
||||
else:
|
||||
mirrors = [mirror]
|
||||
|
||||
if package.endswith('.dsc'):
|
||||
if package.endswith(".dsc"):
|
||||
return DebianSourcePackage(dscfile=package, mirrors=mirrors)
|
||||
|
||||
if dist is None:
|
||||
dist = 'unstable'
|
||||
dist = "unstable"
|
||||
|
||||
requested_version = version
|
||||
if type(version) == str:
|
||||
@ -272,19 +292,20 @@ def fetch_source_pkg(package, dist, version, component, ubuntu_release,
|
||||
if version is None or component is None:
|
||||
try:
|
||||
debian_srcpkg = get_debian_srcpkg(package, dist)
|
||||
except (udtexceptions.PackageNotFoundException,
|
||||
udtexceptions.SeriesNotFoundException) as e:
|
||||
except (
|
||||
udtexceptions.PackageNotFoundException,
|
||||
udtexceptions.SeriesNotFoundException,
|
||||
) as e:
|
||||
Logger.error(str(e))
|
||||
sys.exit(1)
|
||||
if version is None:
|
||||
version = Version(debian_srcpkg.getVersion())
|
||||
try:
|
||||
ubuntu_series, ubuntu_pocket = split_release_pocket(ubuntu_release)
|
||||
ubuntu_srcpkg = get_ubuntu_srcpkg(package, ubuntu_series,
|
||||
ubuntu_pocket)
|
||||
ubuntu_srcpkg = get_ubuntu_srcpkg(package, ubuntu_series, ubuntu_pocket)
|
||||
ubuntu_version = Version(ubuntu_srcpkg.getVersion())
|
||||
except udtexceptions.PackageNotFoundException:
|
||||
ubuntu_version = Version('~')
|
||||
ubuntu_version = Version("~")
|
||||
except udtexceptions.SeriesNotFoundException as e:
|
||||
Logger.error(str(e))
|
||||
sys.exit(1)
|
||||
@ -294,72 +315,84 @@ def fetch_source_pkg(package, dist, version, component, ubuntu_release,
|
||||
if requested_version is None:
|
||||
version = Version(debian_srcpkg.getVersion())
|
||||
if ubuntu_version >= version:
|
||||
Logger.error("Version in Debian %s (%s) isn't newer than "
|
||||
"Ubuntu %s (%s)",
|
||||
version, dist, ubuntu_version, ubuntu_release)
|
||||
Logger.error(
|
||||
"Version in Debian %s (%s) isn't newer than Ubuntu %s (%s)",
|
||||
version,
|
||||
dist,
|
||||
ubuntu_version,
|
||||
ubuntu_release,
|
||||
)
|
||||
sys.exit(1)
|
||||
if component is None:
|
||||
component = debian_srcpkg.getComponent()
|
||||
|
||||
assert component in ('main', 'contrib', 'non-free')
|
||||
assert component in ("main", "contrib", "non-free")
|
||||
|
||||
return DebianSourcePackage(package, version.full_version, component,
|
||||
mirrors=mirrors)
|
||||
return DebianSourcePackage(package, version.full_version, component, mirrors=mirrors)
|
||||
|
||||
|
||||
def copy(src_pkg, release, bugs, sponsoree=None, simulate=False, force=False):
|
||||
"""Copy a source package from Debian to Ubuntu using the Launchpad API."""
|
||||
ubuntu = Distribution('ubuntu')
|
||||
debian_archive = Distribution('debian').getArchive()
|
||||
ubuntu = Distribution("ubuntu")
|
||||
debian_archive = Distribution("debian").getArchive()
|
||||
ubuntu_archive = ubuntu.getArchive()
|
||||
if release is None:
|
||||
ubuntu_series = ubuntu.getDevelopmentSeries().name
|
||||
ubuntu_pocket = 'Release'
|
||||
ubuntu_pocket = "Release"
|
||||
else:
|
||||
ubuntu_series, ubuntu_pocket = split_release_pocket(release)
|
||||
|
||||
# Ensure that the provided Debian version actually exists.
|
||||
try:
|
||||
debian_spph = SourcePackagePublishingHistory(
|
||||
debian_archive.getPublishedSources(
|
||||
source_name=src_pkg.source,
|
||||
version=src_pkg.version.full_version,
|
||||
exact_match=True)[0]
|
||||
)
|
||||
debian_archive.getPublishedSources(
|
||||
source_name=src_pkg.source, version=src_pkg.version.full_version, exact_match=True
|
||||
)[0]
|
||||
)
|
||||
except IndexError:
|
||||
Logger.error('Debian version %s has not been picked up by LP yet. '
|
||||
'Please try again later.',
|
||||
src_pkg.version)
|
||||
Logger.error(
|
||||
"Debian version %s has not been picked up by LP yet. Please try again later.",
|
||||
src_pkg.version,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
ubuntu_spph = get_ubuntu_srcpkg(src_pkg.source,
|
||||
ubuntu_series, ubuntu_pocket)
|
||||
ubuntu_pkg = UbuntuSourcePackage(src_pkg.source,
|
||||
ubuntu_spph.getVersion(),
|
||||
ubuntu_spph.getComponent(),
|
||||
mirrors=[])
|
||||
ubuntu_spph = get_ubuntu_srcpkg(src_pkg.source, ubuntu_series, ubuntu_pocket)
|
||||
ubuntu_pkg = UbuntuSourcePackage(
|
||||
src_pkg.source, ubuntu_spph.getVersion(), ubuntu_spph.getComponent(), mirrors=[]
|
||||
)
|
||||
|
||||
Logger.info('Source %s -> %s/%s: current version %s, new version %s',
|
||||
src_pkg.source, ubuntu_series, ubuntu_pocket,
|
||||
ubuntu_pkg.version, src_pkg.version)
|
||||
Logger.info(
|
||||
"Source %s -> %s/%s: current version %s, new version %s",
|
||||
src_pkg.source,
|
||||
ubuntu_series,
|
||||
ubuntu_pocket,
|
||||
ubuntu_pkg.version,
|
||||
src_pkg.version,
|
||||
)
|
||||
|
||||
ubuntu_version = Version(ubuntu_pkg.version.full_version)
|
||||
base_version = ubuntu_version.get_related_debian_version()
|
||||
if not force and ubuntu_version.is_modified_in_ubuntu():
|
||||
Logger.error('--force is required to discard Ubuntu changes.')
|
||||
Logger.error("--force is required to discard Ubuntu changes.")
|
||||
sys.exit(1)
|
||||
|
||||
# Check whether a fakesync would be required.
|
||||
if not src_pkg.dsc.compare_dsc(ubuntu_pkg.dsc):
|
||||
Logger.error('The checksums of the Debian and Ubuntu packages '
|
||||
'mismatch. A fake sync using --fakesync is required.')
|
||||
Logger.error(
|
||||
"The checksums of the Debian and Ubuntu packages "
|
||||
"mismatch. A fake sync using --fakesync is required."
|
||||
)
|
||||
sys.exit(1)
|
||||
except udtexceptions.PackageNotFoundException:
|
||||
base_version = Version('~')
|
||||
Logger.info('Source %s -> %s/%s: not in Ubuntu, new version %s',
|
||||
src_pkg.source, ubuntu_series, ubuntu_pocket,
|
||||
src_pkg.version)
|
||||
base_version = Version("~")
|
||||
Logger.info(
|
||||
"Source %s -> %s/%s: not in Ubuntu, new version %s",
|
||||
src_pkg.source,
|
||||
ubuntu_series,
|
||||
ubuntu_pocket,
|
||||
src_pkg.version,
|
||||
)
|
||||
|
||||
changes = debian_spph.getChangelog(since_version=base_version)
|
||||
if changes:
|
||||
@ -370,8 +403,7 @@ def copy(src_pkg, release, bugs, sponsoree=None, simulate=False, force=False):
|
||||
return
|
||||
|
||||
if sponsoree:
|
||||
Logger.info("Sponsoring this sync for %s (%s)",
|
||||
sponsoree.display_name, sponsoree.name)
|
||||
Logger.info("Sponsoring this sync for %s (%s)", sponsoree.display_name, sponsoree.name)
|
||||
answer = YesNoQuestion().ask("Sync this package", "no")
|
||||
if answer != "yes":
|
||||
return
|
||||
@ -384,65 +416,65 @@ def copy(src_pkg, release, bugs, sponsoree=None, simulate=False, force=False):
|
||||
to_series=ubuntu_series,
|
||||
to_pocket=ubuntu_pocket,
|
||||
include_binaries=False,
|
||||
sponsored=sponsoree)
|
||||
sponsored=sponsoree,
|
||||
)
|
||||
except HTTPError as error:
|
||||
Logger.error("HTTP Error %s: %s", error.response.status,
|
||||
error.response.reason)
|
||||
Logger.error("HTTP Error %s: %s", error.response.status, error.response.reason)
|
||||
Logger.error(error.content)
|
||||
sys.exit(1)
|
||||
|
||||
Logger.info('Request succeeded; you should get an e-mail once it is '
|
||||
'processed.')
|
||||
Logger.info("Request succeeded; you should get an e-mail once it is processed.")
|
||||
bugs = sorted(set(bugs))
|
||||
if bugs:
|
||||
Logger.info("Launchpad bugs to be closed: %s",
|
||||
', '.join(str(bug) for bug in bugs))
|
||||
Logger.info('Please wait for the sync to be successful before '
|
||||
'closing bugs.')
|
||||
Logger.info("Launchpad bugs to be closed: %s", ", ".join(str(bug) for bug in bugs))
|
||||
Logger.info("Please wait for the sync to be successful before closing bugs.")
|
||||
answer = YesNoQuestion().ask("Close bugs", "yes")
|
||||
if answer == "yes":
|
||||
close_bugs(bugs, src_pkg.source, src_pkg.version.full_version,
|
||||
changes, sponsoree)
|
||||
close_bugs(bugs, src_pkg.source, src_pkg.version.full_version, changes, sponsoree)
|
||||
|
||||
|
||||
def is_blacklisted(query):
|
||||
""""Determine if package "query" is in the sync blacklist
|
||||
"""Determine if package "query" is in the sync blacklist
|
||||
Returns tuple of (blacklisted, comments)
|
||||
blacklisted is one of False, 'CURRENT', 'ALWAYS'
|
||||
"""
|
||||
series = Launchpad.distributions['ubuntu'].current_series
|
||||
series = Launchpad.distributions["ubuntu"].current_series
|
||||
lp_comments = series.getDifferenceComments(source_package_name=query)
|
||||
blacklisted = False
|
||||
comments = ['%s\n -- %s %s'
|
||||
% (c.body_text, c.comment_author.name,
|
||||
c.comment_date.strftime('%a, %d %b %Y %H:%M:%S +0000'))
|
||||
for c in lp_comments]
|
||||
comments = [
|
||||
"%s\n -- %s %s"
|
||||
% (
|
||||
c.body_text,
|
||||
c.comment_author.name,
|
||||
c.comment_date.strftime("%a, %d %b %Y %H:%M:%S +0000"),
|
||||
)
|
||||
for c in lp_comments
|
||||
]
|
||||
|
||||
for diff in series.getDifferencesTo(source_package_name_filter=query):
|
||||
if (diff.status == 'Blacklisted current version'
|
||||
and blacklisted != 'ALWAYS'):
|
||||
blacklisted = 'CURRENT'
|
||||
if diff.status == 'Blacklisted always':
|
||||
blacklisted = 'ALWAYS'
|
||||
if diff.status == "Blacklisted current version" and blacklisted != "ALWAYS":
|
||||
blacklisted = "CURRENT"
|
||||
if diff.status == "Blacklisted always":
|
||||
blacklisted = "ALWAYS"
|
||||
|
||||
# Old blacklist:
|
||||
url = 'http://people.canonical.com/~ubuntu-archive/sync-blacklist.txt'
|
||||
url = "http://people.canonical.com/~ubuntu-archive/sync-blacklist.txt"
|
||||
with urllib.request.urlopen(url) as f:
|
||||
applicable_lines = []
|
||||
for line in f:
|
||||
line = line.decode('utf-8')
|
||||
line = line.decode("utf-8")
|
||||
if not line.strip():
|
||||
applicable_lines = []
|
||||
continue
|
||||
applicable_lines.append(line)
|
||||
try:
|
||||
line = line[:line.index('#')]
|
||||
line = line[: line.index("#")]
|
||||
except ValueError:
|
||||
pass
|
||||
source = line.strip()
|
||||
if source and fnmatch.fnmatch(query, source):
|
||||
comments += ["From sync-blacklist.txt:"] + applicable_lines
|
||||
blacklisted = 'ALWAYS'
|
||||
blacklisted = "ALWAYS"
|
||||
break
|
||||
|
||||
return (blacklisted, comments)
|
||||
@ -450,12 +482,10 @@ def is_blacklisted(query):
|
||||
|
||||
def close_bugs(bugs, package, version, changes, sponsoree):
|
||||
"""Close the correct task on all bugs, with changes"""
|
||||
ubuntu = Launchpad.distributions['ubuntu']
|
||||
message = ("This bug was fixed in the package %s - %s"
|
||||
% (package, version))
|
||||
ubuntu = Launchpad.distributions["ubuntu"]
|
||||
message = "This bug was fixed in the package %s - %s" % (package, version)
|
||||
if sponsoree:
|
||||
message += '\nSponsored for %s (%s)' % (sponsoree.display_name,
|
||||
sponsoree.name)
|
||||
message += "\nSponsored for %s (%s)" % (sponsoree.display_name, sponsoree.name)
|
||||
if changes:
|
||||
message += "\n\n---------------\n" + changes
|
||||
for bug in bugs:
|
||||
@ -464,11 +494,12 @@ def close_bugs(bugs, package, version, changes, sponsoree):
|
||||
bug = bug.duplicate_of
|
||||
for task in bug.bug_tasks:
|
||||
target = task.target
|
||||
if target == ubuntu or (target.name == package and
|
||||
getattr(target, 'distribution', None) == ubuntu):
|
||||
if task.status != 'Fix Released':
|
||||
if target == ubuntu or (
|
||||
target.name == package and getattr(target, "distribution", None) == ubuntu
|
||||
):
|
||||
if task.status != "Fix Released":
|
||||
Logger.info("Closed bug %s", task.web_link)
|
||||
task.status = 'Fix Released'
|
||||
task.status = "Fix Released"
|
||||
task.lp_save()
|
||||
bug.newMessage(content=message)
|
||||
break
|
||||
@ -483,77 +514,116 @@ def parse():
|
||||
epilog = "See %s(1) for more info." % os.path.basename(sys.argv[0])
|
||||
parser = optparse.OptionParser(usage=usage, epilog=epilog)
|
||||
|
||||
parser.add_option("-d", "--distribution",
|
||||
help="Debian distribution to sync from.")
|
||||
parser.add_option("-r", "--release",
|
||||
help="Specify target Ubuntu release.")
|
||||
parser.add_option("-V", "--debian-version",
|
||||
help="Specify the version to sync from.")
|
||||
parser.add_option("-c", "--component",
|
||||
help="Specify the Debian component to sync from.")
|
||||
parser.add_option("-b", "--bug", metavar="BUG",
|
||||
dest="bugs", action="append", default=list(),
|
||||
help="Mark Launchpad bug BUG as being fixed by this "
|
||||
"upload.")
|
||||
parser.add_option("-s", "--sponsor", metavar="USERNAME",
|
||||
dest="sponsoree", default=None,
|
||||
help="Sponsor the sync for USERNAME (a Launchpad "
|
||||
"username).")
|
||||
parser.add_option("-v", "--verbose",
|
||||
action="store_true", default=False,
|
||||
help="Display more progress information.")
|
||||
parser.add_option("-F", "--fakesync",
|
||||
action="store_true", default=False,
|
||||
help="Perform a fakesync (a sync where Debian and "
|
||||
"Ubuntu have a .orig.tar mismatch). "
|
||||
"This implies --no-lp and will leave a signed "
|
||||
".changes file for you to upload.")
|
||||
parser.add_option("-f", "--force",
|
||||
action="store_true", default=False,
|
||||
help="Force sync over the top of Ubuntu changes.")
|
||||
parser.add_option('--no-conf',
|
||||
default=False, action='store_true',
|
||||
help="Don't read config files or environment variables.")
|
||||
parser.add_option('-l', '--lpinstance', metavar='INSTANCE',
|
||||
help='Launchpad instance to connect to '
|
||||
'(default: production).')
|
||||
parser.add_option('--simulate',
|
||||
default=False, action='store_true',
|
||||
help="Show what would be done, but don't actually do "
|
||||
"it.")
|
||||
parser.add_option("-d", "--distribution", help="Debian distribution to sync from.")
|
||||
parser.add_option("-r", "--release", help="Specify target Ubuntu release.")
|
||||
parser.add_option("-V", "--debian-version", help="Specify the version to sync from.")
|
||||
parser.add_option("-c", "--component", help="Specify the Debian component to sync from.")
|
||||
parser.add_option(
|
||||
"-b",
|
||||
"--bug",
|
||||
metavar="BUG",
|
||||
dest="bugs",
|
||||
action="append",
|
||||
default=list(),
|
||||
help="Mark Launchpad bug BUG as being fixed by this upload.",
|
||||
)
|
||||
parser.add_option(
|
||||
"-s",
|
||||
"--sponsor",
|
||||
metavar="USERNAME",
|
||||
dest="sponsoree",
|
||||
default=None,
|
||||
help="Sponsor the sync for USERNAME (a Launchpad username).",
|
||||
)
|
||||
parser.add_option(
|
||||
"-v",
|
||||
"--verbose",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Display more progress information.",
|
||||
)
|
||||
parser.add_option(
|
||||
"-F",
|
||||
"--fakesync",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Perform a fakesync (a sync where Debian and "
|
||||
"Ubuntu have a .orig.tar mismatch). "
|
||||
"This implies --no-lp and will leave a signed "
|
||||
".changes file for you to upload.",
|
||||
)
|
||||
parser.add_option(
|
||||
"-f",
|
||||
"--force",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Force sync over the top of Ubuntu changes.",
|
||||
)
|
||||
parser.add_option(
|
||||
"--no-conf",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Don't read config files or environment variables.",
|
||||
)
|
||||
parser.add_option(
|
||||
"-l",
|
||||
"--lpinstance",
|
||||
metavar="INSTANCE",
|
||||
help="Launchpad instance to connect to (default: production).",
|
||||
)
|
||||
parser.add_option(
|
||||
"--simulate",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Show what would be done, but don't actually do it.",
|
||||
)
|
||||
|
||||
no_lp = optparse.OptionGroup(
|
||||
parser, "Local sync preparation options",
|
||||
parser,
|
||||
"Local sync preparation options",
|
||||
"Options that only apply when using --no-lp. "
|
||||
"WARNING: The use of --no-lp is not recommended for uploads "
|
||||
"targeted at Ubuntu. "
|
||||
"The archive-admins discourage its use, except for fakesyncs.")
|
||||
no_lp.add_option("--no-lp",
|
||||
dest="lp", action="store_false", default=True,
|
||||
help="Construct sync locally, rather than letting "
|
||||
"Launchpad copy the package directly. "
|
||||
"It will leave a signed .changes file for you to "
|
||||
"upload.")
|
||||
no_lp.add_option("-n", "--uploader-name",
|
||||
help="Use UPLOADER_NAME as the name of the maintainer "
|
||||
"for this upload.")
|
||||
no_lp.add_option("-e", "--uploader-email",
|
||||
help="Use UPLOADER_EMAIL as email address of the "
|
||||
"maintainer for this upload.")
|
||||
no_lp.add_option("-k", "--key",
|
||||
dest="keyid",
|
||||
help="Specify the key ID to be used for signing.")
|
||||
no_lp.add_option('--dont-sign',
|
||||
dest='keyid', action='store_false',
|
||||
help='Do not sign the upload.')
|
||||
no_lp.add_option('-D', '--debian-mirror', metavar='DEBIAN_MIRROR',
|
||||
help='Preferred Debian mirror '
|
||||
'(default: %s)'
|
||||
% UDTConfig.defaults['DEBIAN_MIRROR'])
|
||||
no_lp.add_option('-U', '--ubuntu-mirror', metavar='UBUNTU_MIRROR',
|
||||
help='Preferred Ubuntu mirror '
|
||||
'(default: %s)'
|
||||
% UDTConfig.defaults['UBUNTU_MIRROR'])
|
||||
"The archive-admins discourage its use, except for fakesyncs.",
|
||||
)
|
||||
no_lp.add_option(
|
||||
"--no-lp",
|
||||
dest="lp",
|
||||
action="store_false",
|
||||
default=True,
|
||||
help="Construct sync locally, rather than letting "
|
||||
"Launchpad copy the package directly. "
|
||||
"It will leave a signed .changes file for you to "
|
||||
"upload.",
|
||||
)
|
||||
no_lp.add_option(
|
||||
"-n",
|
||||
"--uploader-name",
|
||||
help="Use UPLOADER_NAME as the name of the maintainer for this upload.",
|
||||
)
|
||||
no_lp.add_option(
|
||||
"-e",
|
||||
"--uploader-email",
|
||||
help="Use UPLOADER_EMAIL as email address of the maintainer for this upload.",
|
||||
)
|
||||
no_lp.add_option(
|
||||
"-k", "--key", dest="keyid", help="Specify the key ID to be used for signing."
|
||||
)
|
||||
no_lp.add_option(
|
||||
"--dont-sign", dest="keyid", action="store_false", help="Do not sign the upload."
|
||||
)
|
||||
no_lp.add_option(
|
||||
"-D",
|
||||
"--debian-mirror",
|
||||
metavar="DEBIAN_MIRROR",
|
||||
help="Preferred Debian mirror (default: %s)" % UDTConfig.defaults["DEBIAN_MIRROR"],
|
||||
)
|
||||
no_lp.add_option(
|
||||
"-U",
|
||||
"--ubuntu-mirror",
|
||||
metavar="UBUNTU_MIRROR",
|
||||
help="Preferred Ubuntu mirror (default: %s)" % UDTConfig.defaults["UBUNTU_MIRROR"],
|
||||
)
|
||||
parser.add_option_group(no_lp)
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
@ -562,58 +632,56 @@ def parse():
|
||||
options.lp = False
|
||||
|
||||
if len(args) == 0:
|
||||
parser.error('No .dsc URL/path or package name specified.')
|
||||
parser.error("No .dsc URL/path or package name specified.")
|
||||
if len(args) > 1:
|
||||
parser.error('Multiple .dsc URLs/paths or package names specified: '
|
||||
+ ', '.join(args))
|
||||
parser.error("Multiple .dsc URLs/paths or package names specified: " + ", ".join(args))
|
||||
|
||||
try:
|
||||
options.bugs = [int(b) for b in options.bugs]
|
||||
except TypeError:
|
||||
parser.error('Invalid bug number(s) specified.')
|
||||
parser.error("Invalid bug number(s) specified.")
|
||||
|
||||
if options.component not in (None, "main", "contrib", "non-free"):
|
||||
parser.error('%s is not a valid Debian component. '
|
||||
'It should be one of main, contrib, or non-free.'
|
||||
% options.component)
|
||||
parser.error(
|
||||
"%s is not a valid Debian component. "
|
||||
"It should be one of main, contrib, or non-free." % options.component
|
||||
)
|
||||
|
||||
if options.lp and options.uploader_name:
|
||||
parser.error('Uploader name can only be overridden using --no-lp.')
|
||||
parser.error("Uploader name can only be overridden using --no-lp.")
|
||||
if options.lp and options.uploader_email:
|
||||
parser.error('Uploader email address can only be overridden using '
|
||||
'--no-lp.')
|
||||
parser.error("Uploader email address can only be overridden using --no-lp.")
|
||||
# --key, --dont-sign, --debian-mirror, and --ubuntu-mirror are just
|
||||
# ignored with options.lp, and do not require warnings.
|
||||
|
||||
if options.lp:
|
||||
if args[0].endswith('.dsc'):
|
||||
parser.error('.dsc files can only be synced using --no-lp.')
|
||||
if args[0].endswith(".dsc"):
|
||||
parser.error(".dsc files can only be synced using --no-lp.")
|
||||
|
||||
return (options, args[0])
|
||||
|
||||
|
||||
def main():
|
||||
'''Handle parameters and get the ball rolling'''
|
||||
"""Handle parameters and get the ball rolling"""
|
||||
(options, package) = parse()
|
||||
|
||||
if options.verbose:
|
||||
Logger.setLevel('DEBUG')
|
||||
Logger.setLevel("DEBUG")
|
||||
config = UDTConfig(options.no_conf)
|
||||
if options.debian_mirror is None:
|
||||
options.debian_mirror = config.get_value('DEBIAN_MIRROR')
|
||||
options.debian_mirror = config.get_value("DEBIAN_MIRROR")
|
||||
if options.ubuntu_mirror is None:
|
||||
options.ubuntu_mirror = config.get_value('UBUNTU_MIRROR')
|
||||
options.ubuntu_mirror = config.get_value("UBUNTU_MIRROR")
|
||||
|
||||
if options.keyid is None:
|
||||
options.keyid = config.get_value('KEYID')
|
||||
options.keyid = config.get_value("KEYID")
|
||||
|
||||
if options.lpinstance is None:
|
||||
options.lpinstance = config.get_value('LPINSTANCE')
|
||||
options.lpinstance = config.get_value("LPINSTANCE")
|
||||
|
||||
try:
|
||||
# devel for copyPackage and changelogUrl
|
||||
kwargs = {'service': options.lpinstance,
|
||||
'api_version': 'devel'}
|
||||
kwargs = {"service": options.lpinstance, "api_version": "devel"}
|
||||
if options.lp and not options.simulate:
|
||||
Launchpad.login(**kwargs)
|
||||
else:
|
||||
@ -626,18 +694,19 @@ def main():
|
||||
options.release = "%s-proposed" % ubuntu.current_series.name
|
||||
|
||||
if not options.fakesync and not options.lp:
|
||||
Logger.warning("The use of --no-lp is not recommended for uploads "
|
||||
"targeted at Ubuntu. "
|
||||
"The archive-admins discourage its use, except for "
|
||||
"fakesyncs.")
|
||||
Logger.warning(
|
||||
"The use of --no-lp is not recommended for uploads "
|
||||
"targeted at Ubuntu. "
|
||||
"The archive-admins discourage its use, except for "
|
||||
"fakesyncs."
|
||||
)
|
||||
|
||||
sponsoree = None
|
||||
if options.sponsoree:
|
||||
try:
|
||||
sponsoree = PersonTeam(options.sponsoree)
|
||||
except KeyError:
|
||||
Logger.error('Cannot find the username "%s" in Launchpad.',
|
||||
options.sponsoree)
|
||||
Logger.error('Cannot find the username "%s" in Launchpad.', options.sponsoree)
|
||||
sys.exit(1)
|
||||
|
||||
if sponsoree and options.uploader_name is None:
|
||||
@ -650,44 +719,54 @@ def main():
|
||||
options.uploader_email = sponsoree.preferred_email_address.email
|
||||
except ValueError:
|
||||
if not options.lp:
|
||||
Logger.error("%s doesn't have a publicly visible e-mail "
|
||||
"address in LP, please provide one "
|
||||
"--uploader-email option", sponsoree.display_name)
|
||||
Logger.error(
|
||||
"%s doesn't have a publicly visible e-mail "
|
||||
"address in LP, please provide one "
|
||||
"--uploader-email option",
|
||||
sponsoree.display_name,
|
||||
)
|
||||
sys.exit(1)
|
||||
elif options.uploader_email is None:
|
||||
options.uploader_email = ubu_email(export=False)[1]
|
||||
|
||||
src_pkg = fetch_source_pkg(package, options.distribution,
|
||||
options.debian_version,
|
||||
options.component,
|
||||
options.release,
|
||||
options.debian_mirror)
|
||||
src_pkg = fetch_source_pkg(
|
||||
package,
|
||||
options.distribution,
|
||||
options.debian_version,
|
||||
options.component,
|
||||
options.release,
|
||||
options.debian_mirror,
|
||||
)
|
||||
|
||||
blacklisted, comments = is_blacklisted(src_pkg.source)
|
||||
blacklist_fail = False
|
||||
if blacklisted:
|
||||
messages = []
|
||||
|
||||
if blacklisted == 'CURRENT':
|
||||
Logger.debug("Source package %s is temporarily blacklisted "
|
||||
"(blacklisted_current). "
|
||||
"Ubuntu ignores these for now. "
|
||||
"See also LP: #841372", src_pkg.source)
|
||||
if blacklisted == "CURRENT":
|
||||
Logger.debug(
|
||||
"Source package %s is temporarily blacklisted "
|
||||
"(blacklisted_current). "
|
||||
"Ubuntu ignores these for now. "
|
||||
"See also LP: #841372",
|
||||
src_pkg.source,
|
||||
)
|
||||
else:
|
||||
if options.fakesync:
|
||||
messages += ["Doing a fakesync, overriding blacklist."]
|
||||
else:
|
||||
blacklist_fail = True
|
||||
messages += ["If this package needs a fakesync, "
|
||||
"use --fakesync",
|
||||
"If you think this package shouldn't be "
|
||||
"blacklisted, please file a bug explaining your "
|
||||
"reasoning and subscribe ~ubuntu-archive."]
|
||||
messages += [
|
||||
"If this package needs a fakesync, use --fakesync",
|
||||
"If you think this package shouldn't be "
|
||||
"blacklisted, please file a bug explaining your "
|
||||
"reasoning and subscribe ~ubuntu-archive.",
|
||||
]
|
||||
|
||||
if blacklist_fail:
|
||||
Logger.error("Source package %s is blacklisted.", src_pkg.source)
|
||||
elif blacklisted == 'ALWAYS':
|
||||
Logger.info(u"Source package %s is blacklisted.", src_pkg.source)
|
||||
elif blacklisted == "ALWAYS":
|
||||
Logger.info("Source package %s is blacklisted.", src_pkg.source)
|
||||
if messages:
|
||||
for message in messages:
|
||||
for line in textwrap.wrap(message):
|
||||
@ -703,18 +782,26 @@ def main():
|
||||
sys.exit(1)
|
||||
|
||||
if options.lp:
|
||||
copy(src_pkg, options.release, options.bugs, sponsoree,
|
||||
options.simulate, options.force)
|
||||
copy(src_pkg, options.release, options.bugs, sponsoree, options.simulate, options.force)
|
||||
else:
|
||||
os.environ['DEB_VENDOR'] = 'Ubuntu'
|
||||
sync_dsc(src_pkg, options.distribution, options.release,
|
||||
options.uploader_name, options.uploader_email, options.bugs,
|
||||
options.ubuntu_mirror, options.keyid, options.simulate,
|
||||
options.force, options.fakesync)
|
||||
os.environ["DEB_VENDOR"] = "Ubuntu"
|
||||
sync_dsc(
|
||||
src_pkg,
|
||||
options.distribution,
|
||||
options.release,
|
||||
options.uploader_name,
|
||||
options.uploader_email,
|
||||
options.bugs,
|
||||
options.ubuntu_mirror,
|
||||
options.keyid,
|
||||
options.simulate,
|
||||
options.force,
|
||||
options.fakesync,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
Logger.info('User abort.')
|
||||
Logger.info("User abort.")
|
||||
|
203
ubuntu-build
203
ubuntu-build
@ -26,14 +26,17 @@
|
||||
import sys
|
||||
from optparse import OptionGroup
|
||||
from optparse import OptionParser
|
||||
from ubuntutools.lp.udtexceptions import (SeriesNotFoundException,
|
||||
PackageNotFoundException,
|
||||
PocketDoesNotExistError,)
|
||||
from ubuntutools.lp.udtexceptions import (
|
||||
SeriesNotFoundException,
|
||||
PackageNotFoundException,
|
||||
PocketDoesNotExistError,
|
||||
)
|
||||
from ubuntutools.lp.lpapicache import Distribution, Launchpad, PersonTeam
|
||||
from launchpadlib.credentials import TokenAuthorizationException
|
||||
from ubuntutools.misc import split_release_pocket
|
||||
|
||||
from ubuntutools import getLogger
|
||||
|
||||
Logger = getLogger()
|
||||
|
||||
|
||||
@ -44,48 +47,84 @@ def main():
|
||||
usage += "Only Launchpad Buildd Admins may rescore package builds."
|
||||
|
||||
# Valid architectures.
|
||||
valid_archs = set([
|
||||
"armel", "armhf", "arm64", "amd64", "hppa", "i386", "ia64",
|
||||
"lpia", "powerpc", "ppc64el", "riscv64", "s390x", "sparc",
|
||||
])
|
||||
valid_archs = set(
|
||||
[
|
||||
"armel",
|
||||
"armhf",
|
||||
"arm64",
|
||||
"amd64",
|
||||
"hppa",
|
||||
"i386",
|
||||
"ia64",
|
||||
"lpia",
|
||||
"powerpc",
|
||||
"ppc64el",
|
||||
"riscv64",
|
||||
"s390x",
|
||||
"sparc",
|
||||
]
|
||||
)
|
||||
|
||||
# Prepare our option parser.
|
||||
opt_parser = OptionParser(usage)
|
||||
|
||||
# Retry options
|
||||
retry_rescore_options = OptionGroup(opt_parser, "Retry and rescore options",
|
||||
"These options may only be used with "
|
||||
"the 'retry' and 'rescore' operations.")
|
||||
retry_rescore_options.add_option("-a", "--arch", type="string",
|
||||
action="append", dest="architecture",
|
||||
help="Rebuild or rescore a specific "
|
||||
"architecture. Valid architectures "
|
||||
"include: %s." %
|
||||
", ".join(valid_archs))
|
||||
retry_rescore_options = OptionGroup(
|
||||
opt_parser,
|
||||
"Retry and rescore options",
|
||||
"These options may only be used with the 'retry' and 'rescore' operations.",
|
||||
)
|
||||
retry_rescore_options.add_option(
|
||||
"-a",
|
||||
"--arch",
|
||||
type="string",
|
||||
action="append",
|
||||
dest="architecture",
|
||||
help="Rebuild or rescore a specific "
|
||||
"architecture. Valid architectures "
|
||||
"include: %s." % ", ".join(valid_archs),
|
||||
)
|
||||
|
||||
# Batch processing options
|
||||
batch_options = OptionGroup(opt_parser, "Batch processing",
|
||||
"These options and parameter ordering is only "
|
||||
"available in --batch mode.\nUsage: "
|
||||
"ubuntu-build --batch [options] <package>...")
|
||||
batch_options.add_option('--batch',
|
||||
action='store_true', dest='batch', default=False,
|
||||
help='Enable batch mode')
|
||||
batch_options.add_option('--series',
|
||||
action='store', dest='series', type='string',
|
||||
help='Selects the Ubuntu series to operate on '
|
||||
'(default: current development series)')
|
||||
batch_options.add_option('--retry',
|
||||
action='store_true', dest='retry', default=False,
|
||||
help='Retry builds (give-back).')
|
||||
batch_options.add_option('--rescore',
|
||||
action='store', dest='priority', type='int',
|
||||
help='Rescore builds to <priority>.')
|
||||
batch_options.add_option('--arch2', action='append', dest='architecture',
|
||||
type='string',
|
||||
help="Affect only 'architecture' (can be used "
|
||||
"several times). Valid architectures are: %s."
|
||||
% ', '.join(valid_archs))
|
||||
batch_options = OptionGroup(
|
||||
opt_parser,
|
||||
"Batch processing",
|
||||
"These options and parameter ordering is only "
|
||||
"available in --batch mode.\nUsage: "
|
||||
"ubuntu-build --batch [options] <package>...",
|
||||
)
|
||||
batch_options.add_option(
|
||||
"--batch", action="store_true", dest="batch", default=False, help="Enable batch mode"
|
||||
)
|
||||
batch_options.add_option(
|
||||
"--series",
|
||||
action="store",
|
||||
dest="series",
|
||||
type="string",
|
||||
help="Selects the Ubuntu series to operate on (default: current development series)",
|
||||
)
|
||||
batch_options.add_option(
|
||||
"--retry",
|
||||
action="store_true",
|
||||
dest="retry",
|
||||
default=False,
|
||||
help="Retry builds (give-back).",
|
||||
)
|
||||
batch_options.add_option(
|
||||
"--rescore",
|
||||
action="store",
|
||||
dest="priority",
|
||||
type="int",
|
||||
help="Rescore builds to <priority>.",
|
||||
)
|
||||
batch_options.add_option(
|
||||
"--arch2",
|
||||
action="append",
|
||||
dest="architecture",
|
||||
type="string",
|
||||
help="Affect only 'architecture' (can be used "
|
||||
"several times). Valid architectures are: %s." % ", ".join(valid_archs),
|
||||
)
|
||||
|
||||
# Add the retry options to the main group.
|
||||
opt_parser.add_option_group(retry_rescore_options)
|
||||
@ -121,8 +160,7 @@ def main():
|
||||
# rebuild it and nothing else.
|
||||
if options.architecture:
|
||||
if options.architecture[0] not in valid_archs:
|
||||
Logger.error("Invalid architecture specified: %s."
|
||||
% options.architecture[0])
|
||||
Logger.error("Invalid architecture specified: %s." % options.architecture[0])
|
||||
sys.exit(1)
|
||||
else:
|
||||
one_arch = True
|
||||
@ -143,11 +181,11 @@ def main():
|
||||
sys.exit(1)
|
||||
|
||||
# Get the ubuntu archive
|
||||
ubuntu_archive = Distribution('ubuntu').getArchive()
|
||||
ubuntu_archive = Distribution("ubuntu").getArchive()
|
||||
# Get list of published sources for package in question.
|
||||
try:
|
||||
sources = ubuntu_archive.getSourcePackage(package, release, pocket)
|
||||
distroseries = Distribution('ubuntu').getSeries(release)
|
||||
distroseries = Distribution("ubuntu").getSeries(release)
|
||||
except (SeriesNotFoundException, PackageNotFoundException) as error:
|
||||
Logger.error(error)
|
||||
sys.exit(1)
|
||||
@ -163,22 +201,29 @@ def main():
|
||||
# are in place.
|
||||
me = PersonTeam.me
|
||||
if op == "rescore":
|
||||
necessary_privs = me.isLpTeamMember('launchpad-buildd-admins')
|
||||
necessary_privs = me.isLpTeamMember("launchpad-buildd-admins")
|
||||
if op == "retry":
|
||||
necessary_privs = me.canUploadPackage(ubuntu_archive, distroseries,
|
||||
sources.getPackageName(),
|
||||
sources.getComponent(),
|
||||
pocket=pocket)
|
||||
necessary_privs = me.canUploadPackage(
|
||||
ubuntu_archive,
|
||||
distroseries,
|
||||
sources.getPackageName(),
|
||||
sources.getComponent(),
|
||||
pocket=pocket,
|
||||
)
|
||||
|
||||
if op in ('rescore', 'retry') and not necessary_privs:
|
||||
Logger.error("You cannot perform the %s operation on a %s "
|
||||
"package as you do not have the permissions "
|
||||
"to do this action." % (op, component))
|
||||
if op in ("rescore", "retry") and not necessary_privs:
|
||||
Logger.error(
|
||||
"You cannot perform the %s operation on a %s "
|
||||
"package as you do not have the permissions "
|
||||
"to do this action." % (op, component)
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# Output details.
|
||||
Logger.info("The source version for '%s' in %s (%s) is at %s." %
|
||||
(package, release.capitalize(), component, version))
|
||||
Logger.info(
|
||||
"The source version for '%s' in %s (%s) is at %s."
|
||||
% (package, release.capitalize(), component, version)
|
||||
)
|
||||
|
||||
Logger.info("Current build status for this package:")
|
||||
|
||||
@ -191,20 +236,20 @@ def main():
|
||||
|
||||
done = True
|
||||
Logger.info("%s: %s." % (build.arch_tag, build.buildstate))
|
||||
if op == 'rescore':
|
||||
if op == "rescore":
|
||||
if build.can_be_rescored:
|
||||
# FIXME: make priority an option
|
||||
priority = 5000
|
||||
Logger.info('Rescoring build %s to %d...' % (build.arch_tag, priority))
|
||||
Logger.info("Rescoring build %s to %d..." % (build.arch_tag, priority))
|
||||
build.rescore(score=priority)
|
||||
else:
|
||||
Logger.info('Cannot rescore build on %s.' % build.arch_tag)
|
||||
if op == 'retry':
|
||||
Logger.info("Cannot rescore build on %s." % build.arch_tag)
|
||||
if op == "retry":
|
||||
if build.can_be_retried:
|
||||
Logger.info('Retrying build on %s...' % build.arch_tag)
|
||||
Logger.info("Retrying build on %s..." % build.arch_tag)
|
||||
build.retry()
|
||||
else:
|
||||
Logger.info('Cannot retry build on %s.' % build.arch_tag)
|
||||
Logger.info("Cannot retry build on %s." % build.arch_tag)
|
||||
|
||||
# We are done
|
||||
if done:
|
||||
@ -227,29 +272,27 @@ def main():
|
||||
|
||||
release = options.series
|
||||
if not release:
|
||||
release = (Distribution('ubuntu').getDevelopmentSeries().name
|
||||
+ '-proposed')
|
||||
release = Distribution("ubuntu").getDevelopmentSeries().name + "-proposed"
|
||||
try:
|
||||
(release, pocket) = split_release_pocket(release)
|
||||
except PocketDoesNotExistError as error:
|
||||
Logger.error(error)
|
||||
sys.exit(1)
|
||||
|
||||
ubuntu_archive = Distribution('ubuntu').getArchive()
|
||||
ubuntu_archive = Distribution("ubuntu").getArchive()
|
||||
try:
|
||||
distroseries = Distribution('ubuntu').getSeries(release)
|
||||
distroseries = Distribution("ubuntu").getSeries(release)
|
||||
except SeriesNotFoundException as error:
|
||||
Logger.error(error)
|
||||
sys.exit(1)
|
||||
me = PersonTeam.me
|
||||
|
||||
# Check permisions (part 1): Rescoring can only be done by buildd admins
|
||||
can_rescore = ((options.priority
|
||||
and me.isLpTeamMember('launchpad-buildd-admins'))
|
||||
or False)
|
||||
can_rescore = (options.priority and me.isLpTeamMember("launchpad-buildd-admins")) or False
|
||||
if options.priority and not can_rescore:
|
||||
Logger.error("You don't have the permissions to rescore "
|
||||
"builds. Ignoring your rescore request.")
|
||||
Logger.error(
|
||||
"You don't have the permissions to rescore builds. Ignoring your rescore request."
|
||||
)
|
||||
|
||||
for pkg in args:
|
||||
try:
|
||||
@ -260,17 +303,19 @@ def main():
|
||||
|
||||
# Check permissions (part 2): check upload permissions for the source
|
||||
# package
|
||||
can_retry = options.retry and me.canUploadPackage(ubuntu_archive,
|
||||
distroseries,
|
||||
pkg.getPackageName(),
|
||||
pkg.getComponent())
|
||||
can_retry = options.retry and me.canUploadPackage(
|
||||
ubuntu_archive, distroseries, pkg.getPackageName(), pkg.getComponent()
|
||||
)
|
||||
if options.retry and not can_retry:
|
||||
Logger.error("You don't have the permissions to retry the "
|
||||
"build of '%s'. Ignoring your request."
|
||||
% pkg.getPackageName())
|
||||
Logger.error(
|
||||
"You don't have the permissions to retry the "
|
||||
"build of '%s'. Ignoring your request." % pkg.getPackageName()
|
||||
)
|
||||
|
||||
Logger.info("The source version for '%s' in '%s' (%s) is: %s" %
|
||||
(pkg.getPackageName(), release, pocket, pkg.getVersion()))
|
||||
Logger.info(
|
||||
"The source version for '%s' in '%s' (%s) is: %s"
|
||||
% (pkg.getPackageName(), release, pocket, pkg.getVersion())
|
||||
)
|
||||
|
||||
Logger.info(pkg.getBuildStates(archs))
|
||||
if can_retry:
|
||||
@ -278,8 +323,8 @@ def main():
|
||||
if options.priority and can_rescore:
|
||||
Logger.info(pkg.rescoreBuilds(archs, options.priority))
|
||||
|
||||
Logger.info('')
|
||||
Logger.info("")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
24
ubuntu-iso
24
ubuntu-iso
@ -25,14 +25,15 @@ import subprocess
|
||||
import sys
|
||||
|
||||
from ubuntutools import getLogger
|
||||
|
||||
Logger = getLogger()
|
||||
|
||||
|
||||
def extract(iso, path):
|
||||
command = ['isoinfo', '-R', '-i', iso, '-x', path]
|
||||
pipe = subprocess.run(command, encoding='utf-8',
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
command = ["isoinfo", "-R", "-i", iso, "-x", path]
|
||||
pipe = subprocess.run(
|
||||
command, encoding="utf-8", stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
|
||||
if pipe.returncode != 0:
|
||||
sys.stderr.write(pipe.stderr)
|
||||
@ -42,22 +43,21 @@ def extract(iso, path):
|
||||
|
||||
|
||||
def main():
|
||||
desc = 'Given an ISO, %prog will display the Ubuntu version information'
|
||||
parser = optparse.OptionParser(usage='%prog [options] iso...',
|
||||
description=desc)
|
||||
desc = "Given an ISO, %prog will display the Ubuntu version information"
|
||||
parser = optparse.OptionParser(usage="%prog [options] iso...", description=desc)
|
||||
isos = parser.parse_args()[1]
|
||||
err = False
|
||||
|
||||
for iso in isos:
|
||||
if len(isos) > 1:
|
||||
prefix = '%s:' % iso
|
||||
prefix = "%s:" % iso
|
||||
else:
|
||||
prefix = ''
|
||||
prefix = ""
|
||||
|
||||
version = extract(iso, '/.disk/info')
|
||||
version = extract(iso, "/.disk/info")
|
||||
|
||||
if len(version) == 0:
|
||||
Logger.error('%s does not appear to be an Ubuntu ISO' % iso)
|
||||
Logger.error("%s does not appear to be an Ubuntu ISO" % iso)
|
||||
err = True
|
||||
continue
|
||||
|
||||
@ -67,6 +67,6 @@ def main():
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
sys.exit(0)
|
||||
|
@ -17,28 +17,45 @@
|
||||
import optparse
|
||||
import sys
|
||||
|
||||
from ubuntutools.lp.lpapicache import (Launchpad, Distribution, PersonTeam,
|
||||
Packageset, PackageNotFoundException,
|
||||
SeriesNotFoundException)
|
||||
from ubuntutools.lp.lpapicache import (
|
||||
Launchpad,
|
||||
Distribution,
|
||||
PersonTeam,
|
||||
Packageset,
|
||||
PackageNotFoundException,
|
||||
SeriesNotFoundException,
|
||||
)
|
||||
from ubuntutools.misc import split_release_pocket
|
||||
|
||||
from ubuntutools import getLogger
|
||||
|
||||
Logger = getLogger()
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
'''Parse arguments and return (options, package)'''
|
||||
parser = optparse.OptionParser('%prog [options] package')
|
||||
parser.add_option('-r', '--release', default=None, metavar='RELEASE',
|
||||
help='Use RELEASE, rather than the current development '
|
||||
'release')
|
||||
parser.add_option('-a', '--list-uploaders',
|
||||
default=False, action='store_true',
|
||||
help='List all the people/teams with upload rights')
|
||||
parser.add_option('-t', '--list-team-members',
|
||||
default=False, action='store_true',
|
||||
help='List all team members of teams with upload rights '
|
||||
'(implies --list-uploaders)')
|
||||
"""Parse arguments and return (options, package)"""
|
||||
parser = optparse.OptionParser("%prog [options] package")
|
||||
parser.add_option(
|
||||
"-r",
|
||||
"--release",
|
||||
default=None,
|
||||
metavar="RELEASE",
|
||||
help="Use RELEASE, rather than the current development release",
|
||||
)
|
||||
parser.add_option(
|
||||
"-a",
|
||||
"--list-uploaders",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="List all the people/teams with upload rights",
|
||||
)
|
||||
parser.add_option(
|
||||
"-t",
|
||||
"--list-team-members",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="List all team members of teams with upload rights (implies --list-uploaders)",
|
||||
)
|
||||
options, args = parser.parse_args()
|
||||
|
||||
if len(args) != 1:
|
||||
@ -52,12 +69,12 @@ def parse_arguments():
|
||||
|
||||
|
||||
def main():
|
||||
'''Query upload permissions'''
|
||||
"""Query upload permissions"""
|
||||
options, package = parse_arguments()
|
||||
# Need to be logged in to see uploaders:
|
||||
Launchpad.login()
|
||||
|
||||
ubuntu = Distribution('ubuntu')
|
||||
ubuntu = Distribution("ubuntu")
|
||||
archive = ubuntu.getArchive()
|
||||
if options.release is None:
|
||||
options.release = ubuntu.getDevelopmentSeries().name
|
||||
@ -74,20 +91,22 @@ def main():
|
||||
Logger.error(str(e))
|
||||
sys.exit(2)
|
||||
component = spph.getComponent()
|
||||
if (options.list_uploaders and (pocket != 'Release' or series.status in
|
||||
('Experimental', 'Active Development', 'Pre-release Freeze'))):
|
||||
if options.list_uploaders and (
|
||||
pocket != "Release"
|
||||
or series.status in ("Experimental", "Active Development", "Pre-release Freeze")
|
||||
):
|
||||
|
||||
component_uploader = archive.getUploadersForComponent(
|
||||
component_name=component)[0]
|
||||
component_uploader = archive.getUploadersForComponent(component_name=component)[0]
|
||||
Logger.info("All upload permissions for %s:" % package)
|
||||
Logger.info("")
|
||||
Logger.info("Component (%s)" % component)
|
||||
Logger.info("============" + ("=" * len(component)))
|
||||
print_uploaders([component_uploader], options.list_team_members)
|
||||
|
||||
packagesets = sorted(Packageset.setsIncludingSource(
|
||||
distroseries=series,
|
||||
sourcepackagename=package), key=lambda p: p.name)
|
||||
packagesets = sorted(
|
||||
Packageset.setsIncludingSource(distroseries=series, sourcepackagename=package),
|
||||
key=lambda p: p.name,
|
||||
)
|
||||
if packagesets:
|
||||
Logger.info("")
|
||||
Logger.info("Packagesets")
|
||||
@ -95,11 +114,12 @@ def main():
|
||||
for packageset in packagesets:
|
||||
Logger.info("")
|
||||
Logger.info("%s:" % packageset.name)
|
||||
print_uploaders(archive.getUploadersForPackageset(
|
||||
packageset=packageset), options.list_team_members)
|
||||
print_uploaders(
|
||||
archive.getUploadersForPackageset(packageset=packageset),
|
||||
options.list_team_members,
|
||||
)
|
||||
|
||||
ppu_uploaders = archive.getUploadersForPackage(
|
||||
source_package_name=package)
|
||||
ppu_uploaders = archive.getUploadersForPackage(source_package_name=package)
|
||||
if ppu_uploaders:
|
||||
Logger.info("")
|
||||
Logger.info("Per-Package-Uploaders")
|
||||
@ -108,37 +128,45 @@ def main():
|
||||
print_uploaders(ppu_uploaders, options.list_team_members)
|
||||
Logger.info("")
|
||||
|
||||
if PersonTeam.me.canUploadPackage(archive, series, package, component,
|
||||
pocket):
|
||||
if PersonTeam.me.canUploadPackage(archive, series, package, component, pocket):
|
||||
Logger.info("You can upload %s to %s." % (package, options.release))
|
||||
else:
|
||||
Logger.info("You can not upload %s to %s, yourself." % (package, options.release))
|
||||
if (series.status in ('Current Stable Release', 'Supported', 'Obsolete')
|
||||
and pocket == 'Release'):
|
||||
Logger.info("%s is in the '%s' state. You may want to query the %s-proposed pocket." %
|
||||
(release, series.status, release))
|
||||
if (
|
||||
series.status in ("Current Stable Release", "Supported", "Obsolete")
|
||||
and pocket == "Release"
|
||||
):
|
||||
Logger.info(
|
||||
"%s is in the '%s' state. You may want to query the %s-proposed pocket."
|
||||
% (release, series.status, release)
|
||||
)
|
||||
else:
|
||||
Logger.info("But you can still contribute to it via the sponsorship "
|
||||
"process: https://wiki.ubuntu.com/SponsorshipProcess")
|
||||
Logger.info(
|
||||
"But you can still contribute to it via the sponsorship "
|
||||
"process: https://wiki.ubuntu.com/SponsorshipProcess"
|
||||
)
|
||||
if not options.list_uploaders:
|
||||
Logger.info("To see who has the necessary upload rights, "
|
||||
"use the --list-uploaders option.")
|
||||
Logger.info(
|
||||
"To see who has the necessary upload rights, "
|
||||
"use the --list-uploaders option."
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def print_uploaders(uploaders, expand_teams=False, prefix=''):
|
||||
def print_uploaders(uploaders, expand_teams=False, prefix=""):
|
||||
"""Given a list of uploaders, pretty-print them all
|
||||
Each line is prefixed with prefix.
|
||||
If expand_teams is set, recurse, adding more spaces to prefix on each
|
||||
recursion.
|
||||
"""
|
||||
for uploader in sorted(uploaders, key=lambda p: p.display_name):
|
||||
Logger.info("%s* %s (%s)%s" %
|
||||
(prefix, uploader.display_name, uploader.name,
|
||||
' [team]' if uploader.is_team else ''))
|
||||
Logger.info(
|
||||
"%s* %s (%s)%s"
|
||||
% (prefix, uploader.display_name, uploader.name, " [team]" if uploader.is_team else "")
|
||||
)
|
||||
if expand_teams and uploader.is_team:
|
||||
print_uploaders(uploader.participants, True, prefix=prefix + ' ')
|
||||
print_uploaders(uploader.participants, True, prefix=prefix + " ")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
@ -8,7 +8,7 @@ import sys
|
||||
|
||||
|
||||
def getLogger():
|
||||
''' Get the logger instance for this module
|
||||
"""Get the logger instance for this module
|
||||
|
||||
Quick guide for using this or not: if you want to call ubuntutools
|
||||
module code and have its output print to stdout/stderr ONLY, you can
|
||||
@ -33,12 +33,12 @@ def getLogger():
|
||||
This should only be used by runnable scripts provided by the
|
||||
ubuntu-dev-tools package, or other runnable scripts that want the behavior
|
||||
described above.
|
||||
'''
|
||||
"""
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.INFO)
|
||||
logger.propagate = False
|
||||
|
||||
fmt = logging.Formatter('%(message)s')
|
||||
fmt = logging.Formatter("%(message)s")
|
||||
|
||||
stdout_handler = logging.StreamHandler(stream=sys.stdout)
|
||||
stdout_handler.setFormatter(fmt)
|
||||
@ -47,7 +47,7 @@ def getLogger():
|
||||
|
||||
stderr_handler = logging.StreamHandler(stream=sys.stderr)
|
||||
stderr_handler.setFormatter(fmt)
|
||||
stderr_handler.setLevel(logging.INFO+1)
|
||||
stderr_handler.setLevel(logging.INFO + 1)
|
||||
logger.addHandler(stderr_handler)
|
||||
|
||||
return logger
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -22,6 +22,7 @@ import os
|
||||
import subprocess
|
||||
|
||||
import logging
|
||||
|
||||
Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -35,16 +36,17 @@ class Builder(object):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
cmd = ["dpkg-architecture", "-qDEB_BUILD_ARCH_CPU"]
|
||||
self.architecture = subprocess.check_output(cmd, encoding='utf-8').strip()
|
||||
self.architecture = subprocess.check_output(cmd, encoding="utf-8").strip()
|
||||
|
||||
def _build_failure(self, returncode, dsc_file):
|
||||
if returncode != 0:
|
||||
Logger.error("Failed to build %s from source with %s." %
|
||||
(os.path.basename(dsc_file), self.name))
|
||||
Logger.error(
|
||||
"Failed to build %s from source with %s." % (os.path.basename(dsc_file), self.name)
|
||||
)
|
||||
return returncode
|
||||
|
||||
def exists_in_path(self):
|
||||
for path in os.environ.get('PATH', os.defpath).split(os.pathsep):
|
||||
for path in os.environ.get("PATH", os.defpath).split(os.pathsep):
|
||||
if os.path.isfile(os.path.join(path, self.name)):
|
||||
return True
|
||||
return False
|
||||
@ -57,8 +59,7 @@ class Builder(object):
|
||||
|
||||
def _update_failure(self, returncode, dist):
|
||||
if returncode != 0:
|
||||
Logger.error("Failed to update %s chroot for %s." %
|
||||
(dist, self.name))
|
||||
Logger.error("Failed to update %s chroot for %s." % (dist, self.name))
|
||||
return returncode
|
||||
|
||||
|
||||
@ -68,19 +69,39 @@ class Pbuilder(Builder):
|
||||
|
||||
def build(self, dsc_file, dist, result_directory):
|
||||
_build_preparation(result_directory)
|
||||
cmd = ["sudo", "-E", "ARCH=" + self.architecture, "DIST=" + dist,
|
||||
self.name, "--build",
|
||||
"--architecture", self.architecture, "--distribution", dist,
|
||||
"--buildresult", result_directory, dsc_file]
|
||||
Logger.debug(' '.join(cmd))
|
||||
cmd = [
|
||||
"sudo",
|
||||
"-E",
|
||||
"ARCH=" + self.architecture,
|
||||
"DIST=" + dist,
|
||||
self.name,
|
||||
"--build",
|
||||
"--architecture",
|
||||
self.architecture,
|
||||
"--distribution",
|
||||
dist,
|
||||
"--buildresult",
|
||||
result_directory,
|
||||
dsc_file,
|
||||
]
|
||||
Logger.debug(" ".join(cmd))
|
||||
returncode = subprocess.call(cmd)
|
||||
return self._build_failure(returncode, dsc_file)
|
||||
|
||||
def update(self, dist):
|
||||
cmd = ["sudo", "-E", "ARCH=" + self.architecture, "DIST=" + dist,
|
||||
self.name, "--update",
|
||||
"--architecture", self.architecture, "--distribution", dist]
|
||||
Logger.debug(' '.join(cmd))
|
||||
cmd = [
|
||||
"sudo",
|
||||
"-E",
|
||||
"ARCH=" + self.architecture,
|
||||
"DIST=" + dist,
|
||||
self.name,
|
||||
"--update",
|
||||
"--architecture",
|
||||
self.architecture,
|
||||
"--distribution",
|
||||
dist,
|
||||
]
|
||||
Logger.debug(" ".join(cmd))
|
||||
returncode = subprocess.call(cmd)
|
||||
return self._update_failure(returncode, dist)
|
||||
|
||||
@ -91,15 +112,22 @@ class Pbuilderdist(Builder):
|
||||
|
||||
def build(self, dsc_file, dist, result_directory):
|
||||
_build_preparation(result_directory)
|
||||
cmd = [self.name, dist, self.architecture,
|
||||
"build", dsc_file, "--buildresult", result_directory]
|
||||
Logger.debug(' '.join(cmd))
|
||||
cmd = [
|
||||
self.name,
|
||||
dist,
|
||||
self.architecture,
|
||||
"build",
|
||||
dsc_file,
|
||||
"--buildresult",
|
||||
result_directory,
|
||||
]
|
||||
Logger.debug(" ".join(cmd))
|
||||
returncode = subprocess.call(cmd)
|
||||
return self._build_failure(returncode, dsc_file)
|
||||
|
||||
def update(self, dist):
|
||||
cmd = [self.name, dist, self.architecture, "update"]
|
||||
Logger.debug(' '.join(cmd))
|
||||
Logger.debug(" ".join(cmd))
|
||||
returncode = subprocess.call(cmd)
|
||||
return self._update_failure(returncode, dist)
|
||||
|
||||
@ -113,9 +141,8 @@ class Sbuild(Builder):
|
||||
workdir = os.getcwd()
|
||||
Logger.debug("cd " + result_directory)
|
||||
os.chdir(result_directory)
|
||||
cmd = ["sbuild", "--arch-all", "--dist=" + dist,
|
||||
"--arch=" + self.architecture, dsc_file]
|
||||
Logger.debug(' '.join(cmd))
|
||||
cmd = ["sbuild", "--arch-all", "--dist=" + dist, "--arch=" + self.architecture, dsc_file]
|
||||
Logger.debug(" ".join(cmd))
|
||||
returncode = subprocess.call(cmd)
|
||||
Logger.debug("cd " + workdir)
|
||||
os.chdir(workdir)
|
||||
@ -123,29 +150,29 @@ class Sbuild(Builder):
|
||||
|
||||
def update(self, dist):
|
||||
cmd = ["schroot", "--list"]
|
||||
Logger.debug(' '.join(cmd))
|
||||
process = subprocess.run(cmd, stdout=subprocess.PIPE, encoding='utf-8')
|
||||
Logger.debug(" ".join(cmd))
|
||||
process = subprocess.run(cmd, stdout=subprocess.PIPE, encoding="utf-8")
|
||||
chroots, _ = process.stdout.strip().split()
|
||||
if process.returncode != 0:
|
||||
return process.returncode
|
||||
|
||||
params = {"dist": dist, "arch": self.architecture}
|
||||
for chroot in ("%(dist)s-%(arch)s-sbuild-source",
|
||||
"%(dist)s-sbuild-source",
|
||||
"%(dist)s-%(arch)s-source",
|
||||
"%(dist)s-source"):
|
||||
for chroot in (
|
||||
"%(dist)s-%(arch)s-sbuild-source",
|
||||
"%(dist)s-sbuild-source",
|
||||
"%(dist)s-%(arch)s-source",
|
||||
"%(dist)s-source",
|
||||
):
|
||||
chroot = chroot % params
|
||||
if chroot in chroots:
|
||||
break
|
||||
else:
|
||||
return 1
|
||||
|
||||
commands = [["sbuild-update"],
|
||||
["sbuild-distupgrade"],
|
||||
["sbuild-clean", "-a", "-c"]]
|
||||
commands = [["sbuild-update"], ["sbuild-distupgrade"], ["sbuild-clean", "-a", "-c"]]
|
||||
for cmd in commands:
|
||||
# pylint: disable=W0631
|
||||
Logger.debug(' '.join(cmd) + " " + chroot)
|
||||
Logger.debug(" ".join(cmd) + " " + chroot)
|
||||
ret = subprocess.call(cmd + [chroot])
|
||||
# pylint: enable=W0631
|
||||
if ret != 0:
|
||||
@ -170,5 +197,4 @@ def get_builder(name):
|
||||
Logger.error("Builder doesn't appear to be installed: %s", name)
|
||||
else:
|
||||
Logger.error("Unsupported builder specified: %s.", name)
|
||||
Logger.error("Supported builders: %s",
|
||||
", ".join(sorted(_SUPPORTED_BUILDERS.keys())))
|
||||
Logger.error("Supported builders: %s", ", ".join(sorted(_SUPPORTED_BUILDERS.keys())))
|
||||
|
@ -24,6 +24,7 @@ import sys
|
||||
import locale
|
||||
|
||||
import logging
|
||||
|
||||
Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -31,23 +32,24 @@ class UDTConfig(object):
|
||||
"""Ubuntu Dev Tools configuration file (devscripts config file) and
|
||||
environment variable parsing.
|
||||
"""
|
||||
|
||||
no_conf = False
|
||||
# Package wide configuration variables.
|
||||
# These are reqired to be used by at least two scripts.
|
||||
defaults = {
|
||||
'BUILDER': 'pbuilder',
|
||||
'DEBIAN_MIRROR': 'http://deb.debian.org/debian',
|
||||
'DEBSEC_MIRROR': 'http://security.debian.org',
|
||||
'DEBIAN_DDEBS_MIRROR': 'http://debug.mirrors.debian.org/debian-debug',
|
||||
'LPINSTANCE': 'production',
|
||||
'MIRROR_FALLBACK': True,
|
||||
'UBUNTU_MIRROR': 'http://archive.ubuntu.com/ubuntu',
|
||||
'UBUNTU_PORTS_MIRROR': 'http://ports.ubuntu.com',
|
||||
'UBUNTU_INTERNAL_MIRROR': 'http://ftpmaster.internal/ubuntu',
|
||||
'UBUNTU_DDEBS_MIRROR': 'http://ddebs.ubuntu.com',
|
||||
'UPDATE_BUILDER': False,
|
||||
'WORKDIR': None,
|
||||
'KEYID': None,
|
||||
"BUILDER": "pbuilder",
|
||||
"DEBIAN_MIRROR": "http://deb.debian.org/debian",
|
||||
"DEBSEC_MIRROR": "http://security.debian.org",
|
||||
"DEBIAN_DDEBS_MIRROR": "http://debug.mirrors.debian.org/debian-debug",
|
||||
"LPINSTANCE": "production",
|
||||
"MIRROR_FALLBACK": True,
|
||||
"UBUNTU_MIRROR": "http://archive.ubuntu.com/ubuntu",
|
||||
"UBUNTU_PORTS_MIRROR": "http://ports.ubuntu.com",
|
||||
"UBUNTU_INTERNAL_MIRROR": "http://ftpmaster.internal/ubuntu",
|
||||
"UBUNTU_DDEBS_MIRROR": "http://ddebs.ubuntu.com",
|
||||
"UPDATE_BUILDER": False,
|
||||
"WORKDIR": None,
|
||||
"KEYID": None,
|
||||
}
|
||||
# Populated from the configuration files:
|
||||
config = {}
|
||||
@ -55,7 +57,7 @@ class UDTConfig(object):
|
||||
def __init__(self, no_conf=False, prefix=None):
|
||||
self.no_conf = no_conf
|
||||
if prefix is None:
|
||||
prefix = os.path.basename(sys.argv[0]).upper().replace('-', '_')
|
||||
prefix = os.path.basename(sys.argv[0]).upper().replace("-", "_")
|
||||
self.prefix = prefix
|
||||
if not no_conf:
|
||||
self.config = self.parse_devscripts_config()
|
||||
@ -65,18 +67,21 @@ class UDTConfig(object):
|
||||
dictionary
|
||||
"""
|
||||
config = {}
|
||||
for filename in ('/etc/devscripts.conf', '~/.devscripts'):
|
||||
for filename in ("/etc/devscripts.conf", "~/.devscripts"):
|
||||
try:
|
||||
f = open(os.path.expanduser(filename), 'r')
|
||||
f = open(os.path.expanduser(filename), "r")
|
||||
except IOError:
|
||||
continue
|
||||
for line in f:
|
||||
parsed = shlex.split(line, comments=True)
|
||||
if len(parsed) > 1:
|
||||
Logger.warning('Cannot parse variable assignment in %s: %s',
|
||||
getattr(f, 'name', '<config>'), line)
|
||||
if len(parsed) >= 1 and '=' in parsed[0]:
|
||||
key, value = parsed[0].split('=', 1)
|
||||
Logger.warning(
|
||||
"Cannot parse variable assignment in %s: %s",
|
||||
getattr(f, "name", "<config>"),
|
||||
line,
|
||||
)
|
||||
if len(parsed) >= 1 and "=" in parsed[0]:
|
||||
key, value = parsed[0].split("=", 1)
|
||||
config[key] = value
|
||||
f.close()
|
||||
return config
|
||||
@ -95,9 +100,9 @@ class UDTConfig(object):
|
||||
if default is None and key in self.defaults:
|
||||
default = self.defaults[key]
|
||||
|
||||
keys = [self.prefix + '_' + key]
|
||||
keys = [self.prefix + "_" + key]
|
||||
if key in self.defaults:
|
||||
keys.append('UBUNTUTOOLS_' + key)
|
||||
keys.append("UBUNTUTOOLS_" + key)
|
||||
keys += compat_keys
|
||||
|
||||
for k in keys:
|
||||
@ -105,16 +110,19 @@ class UDTConfig(object):
|
||||
if k in store:
|
||||
value = store[k]
|
||||
if boolean:
|
||||
if value in ('yes', 'no'):
|
||||
value = value == 'yes'
|
||||
if value in ("yes", "no"):
|
||||
value = value == "yes"
|
||||
else:
|
||||
continue
|
||||
if k in compat_keys:
|
||||
replacements = self.prefix + '_' + key
|
||||
replacements = self.prefix + "_" + key
|
||||
if key in self.defaults:
|
||||
replacements += 'or UBUNTUTOOLS_' + key
|
||||
Logger.warning('Using deprecated configuration variable %s. '
|
||||
'You should use %s.', k, replacements)
|
||||
replacements += "or UBUNTUTOOLS_" + key
|
||||
Logger.warning(
|
||||
"Using deprecated configuration variable %s. You should use %s.",
|
||||
k,
|
||||
replacements,
|
||||
)
|
||||
return value
|
||||
return default
|
||||
|
||||
@ -132,7 +140,7 @@ def ubu_email(name=None, email=None, export=True):
|
||||
|
||||
Return name, email.
|
||||
"""
|
||||
name_email_re = re.compile(r'^\s*(.+?)\s*<(.+@.+)>\s*$')
|
||||
name_email_re = re.compile(r"^\s*(.+?)\s*<(.+@.+)>\s*$")
|
||||
|
||||
if email:
|
||||
match = name_email_re.match(email)
|
||||
@ -140,11 +148,16 @@ def ubu_email(name=None, email=None, export=True):
|
||||
name = match.group(1)
|
||||
email = match.group(2)
|
||||
|
||||
if export and not name and not email and 'UBUMAIL' not in os.environ:
|
||||
if export and not name and not email and "UBUMAIL" not in os.environ:
|
||||
export = False
|
||||
|
||||
for var, target in (('UBUMAIL', 'email'), ('DEBFULLNAME', 'name'), ('DEBEMAIL', 'email'),
|
||||
('EMAIL', 'email'), ('NAME', 'name')):
|
||||
for var, target in (
|
||||
("UBUMAIL", "email"),
|
||||
("DEBFULLNAME", "name"),
|
||||
("DEBEMAIL", "email"),
|
||||
("EMAIL", "email"),
|
||||
("NAME", "name"),
|
||||
):
|
||||
if name and email:
|
||||
break
|
||||
if var in os.environ:
|
||||
@ -154,30 +167,30 @@ def ubu_email(name=None, email=None, export=True):
|
||||
name = match.group(1)
|
||||
if not email:
|
||||
email = match.group(2)
|
||||
elif target == 'name' and not name:
|
||||
elif target == "name" and not name:
|
||||
name = os.environ[var].strip()
|
||||
elif target == 'email' and not email:
|
||||
elif target == "email" and not email:
|
||||
email = os.environ[var].strip()
|
||||
|
||||
if not name:
|
||||
gecos_name = pwd.getpwuid(os.getuid()).pw_gecos.split(',')[0].strip()
|
||||
gecos_name = pwd.getpwuid(os.getuid()).pw_gecos.split(",")[0].strip()
|
||||
if gecos_name:
|
||||
name = gecos_name
|
||||
|
||||
if not email:
|
||||
mailname = socket.getfqdn()
|
||||
if os.path.isfile('/etc/mailname'):
|
||||
mailname = open('/etc/mailname', 'r').read().strip()
|
||||
email = pwd.getpwuid(os.getuid()).pw_name + '@' + mailname
|
||||
if os.path.isfile("/etc/mailname"):
|
||||
mailname = open("/etc/mailname", "r").read().strip()
|
||||
email = pwd.getpwuid(os.getuid()).pw_name + "@" + mailname
|
||||
|
||||
if export:
|
||||
os.environ['DEBFULLNAME'] = name
|
||||
os.environ['DEBEMAIL'] = email
|
||||
os.environ["DEBFULLNAME"] = name
|
||||
os.environ["DEBEMAIL"] = email
|
||||
|
||||
# decode env var or gecos raw string with the current locale's encoding
|
||||
encoding = locale.getdefaultlocale()[1]
|
||||
if not encoding:
|
||||
encoding = 'utf-8'
|
||||
encoding = "utf-8"
|
||||
if name and isinstance(name, bytes):
|
||||
name = name.decode(encoding)
|
||||
return name, email
|
||||
|
@ -2,5 +2,5 @@
|
||||
# ubuntu-dev-tools Launchpad Python modules.
|
||||
#
|
||||
|
||||
service = 'production'
|
||||
api_version = 'devel'
|
||||
service = "production"
|
||||
api_version = "devel"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,33 +1,40 @@
|
||||
class PackageNotFoundException(BaseException):
|
||||
""" Thrown when a package is not found """
|
||||
"""Thrown when a package is not found"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class SeriesNotFoundException(BaseException):
|
||||
""" Thrown when a distroseries is not found """
|
||||
"""Thrown when a distroseries is not found"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class PocketDoesNotExistError(Exception):
|
||||
'''Raised when a invalid pocket is used.'''
|
||||
"""Raised when a invalid pocket is used."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ArchiveNotFoundException(BaseException):
|
||||
""" Thrown when an archive for a distibution is not found """
|
||||
"""Thrown when an archive for a distibution is not found"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class AlreadyLoggedInError(Exception):
|
||||
'''Raised when a second login is attempted.'''
|
||||
"""Raised when a second login is attempted."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ArchSeriesNotFoundException(BaseException):
|
||||
"""Thrown when a distroarchseries is not found."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class InvalidDistroValueError(ValueError):
|
||||
""" Thrown when distro value is invalid """
|
||||
"""Thrown when distro value is invalid"""
|
||||
|
||||
pass
|
||||
|
@ -39,16 +39,17 @@ from urllib.parse import urlparse
|
||||
from ubuntutools.lp.udtexceptions import PocketDoesNotExistError
|
||||
|
||||
import logging
|
||||
|
||||
Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
DEFAULT_POCKETS = ('Release', 'Security', 'Updates', 'Proposed')
|
||||
POCKETS = DEFAULT_POCKETS + ('Backports',)
|
||||
DEFAULT_POCKETS = ("Release", "Security", "Updates", "Proposed")
|
||||
POCKETS = DEFAULT_POCKETS + ("Backports",)
|
||||
|
||||
DEFAULT_STATUSES = ('Pending', 'Published')
|
||||
STATUSES = DEFAULT_STATUSES + ('Superseded', 'Deleted', 'Obsolete')
|
||||
DEFAULT_STATUSES = ("Pending", "Published")
|
||||
STATUSES = DEFAULT_STATUSES + ("Superseded", "Deleted", "Obsolete")
|
||||
|
||||
UPLOAD_QUEUE_STATUSES = ('New', 'Unapproved', 'Accepted', 'Done', 'Rejected')
|
||||
UPLOAD_QUEUE_STATUSES = ("New", "Unapproved", "Accepted", "Done", "Rejected")
|
||||
|
||||
DOWNLOAD_BLOCKSIZE_DEFAULT = 8192
|
||||
|
||||
@ -66,7 +67,7 @@ class NotFoundError(DownloadError):
|
||||
|
||||
|
||||
def system_distribution_chain():
|
||||
""" system_distribution_chain() -> [string]
|
||||
"""system_distribution_chain() -> [string]
|
||||
|
||||
Detect the system's distribution as well as all of its parent
|
||||
distributions and return them as a list of strings, with the
|
||||
@ -77,18 +78,24 @@ def system_distribution_chain():
|
||||
global _system_distribution_chain
|
||||
if len(_system_distribution_chain) == 0:
|
||||
try:
|
||||
vendor = check_output(('dpkg-vendor', '--query', 'Vendor'),
|
||||
encoding='utf-8').strip()
|
||||
vendor = check_output(("dpkg-vendor", "--query", "Vendor"), encoding="utf-8").strip()
|
||||
_system_distribution_chain.append(vendor)
|
||||
except CalledProcessError:
|
||||
Logger.error('Could not determine what distribution you are running.')
|
||||
Logger.error("Could not determine what distribution you are running.")
|
||||
return []
|
||||
|
||||
while True:
|
||||
try:
|
||||
parent = check_output((
|
||||
'dpkg-vendor', '--vendor', _system_distribution_chain[-1],
|
||||
'--query', 'Parent'), encoding='utf-8').strip()
|
||||
parent = check_output(
|
||||
(
|
||||
"dpkg-vendor",
|
||||
"--vendor",
|
||||
_system_distribution_chain[-1],
|
||||
"--query",
|
||||
"Parent",
|
||||
),
|
||||
encoding="utf-8",
|
||||
).strip()
|
||||
except CalledProcessError:
|
||||
# Vendor has no parent
|
||||
break
|
||||
@ -98,7 +105,7 @@ def system_distribution_chain():
|
||||
|
||||
|
||||
def system_distribution():
|
||||
""" system_distro() -> string
|
||||
"""system_distro() -> string
|
||||
|
||||
Detect the system's distribution and return it as a string. If the
|
||||
name of the distribution can't be determined, print an error message
|
||||
@ -108,28 +115,26 @@ def system_distribution():
|
||||
|
||||
|
||||
def host_architecture():
|
||||
""" host_architecture -> string
|
||||
"""host_architecture -> string
|
||||
|
||||
Detect the host's architecture and return it as a string. If the
|
||||
architecture can't be determined, print an error message and return None.
|
||||
"""
|
||||
|
||||
try:
|
||||
arch = check_output(('dpkg', '--print-architecture'),
|
||||
encoding='utf-8').strip()
|
||||
arch = check_output(("dpkg", "--print-architecture"), encoding="utf-8").strip()
|
||||
except CalledProcessError:
|
||||
arch = None
|
||||
|
||||
if not arch or 'not found' in arch:
|
||||
Logger.error('Not running on a Debian based system; '
|
||||
'could not detect its architecture.')
|
||||
if not arch or "not found" in arch:
|
||||
Logger.error("Not running on a Debian based system; could not detect its architecture.")
|
||||
return None
|
||||
|
||||
return arch
|
||||
|
||||
|
||||
def readlist(filename, uniq=True):
|
||||
""" readlist(filename, uniq) -> list
|
||||
"""readlist(filename, uniq) -> list
|
||||
|
||||
Read a list of words from the indicated file. If 'uniq' is True, filter
|
||||
out duplicated words.
|
||||
@ -137,13 +142,13 @@ def readlist(filename, uniq=True):
|
||||
p = Path(filename)
|
||||
|
||||
if not p.is_file():
|
||||
Logger.error(f'File {p} does not exist.')
|
||||
Logger.error(f"File {p} does not exist.")
|
||||
return False
|
||||
|
||||
content = p.read_text().replace('\n', ' ').replace(',', ' ')
|
||||
content = p.read_text().replace("\n", " ").replace(",", " ")
|
||||
|
||||
if not content.strip():
|
||||
Logger.error(f'File {p} is empty.')
|
||||
Logger.error(f"File {p} is empty.")
|
||||
return False
|
||||
|
||||
items = [item for item in content.split() if item]
|
||||
@ -154,21 +159,21 @@ def readlist(filename, uniq=True):
|
||||
return items
|
||||
|
||||
|
||||
def split_release_pocket(release, default='Release'):
|
||||
'''Splits the release and pocket name.
|
||||
def split_release_pocket(release, default="Release"):
|
||||
"""Splits the release and pocket name.
|
||||
|
||||
If the argument doesn't contain a pocket name then the 'Release' pocket
|
||||
is assumed.
|
||||
|
||||
Returns the release and pocket name.
|
||||
'''
|
||||
"""
|
||||
pocket = default
|
||||
|
||||
if release is None:
|
||||
raise ValueError('No release name specified')
|
||||
raise ValueError("No release name specified")
|
||||
|
||||
if '-' in release:
|
||||
(release, pocket) = release.rsplit('-', 1)
|
||||
if "-" in release:
|
||||
(release, pocket) = release.rsplit("-", 1)
|
||||
pocket = pocket.capitalize()
|
||||
|
||||
if pocket not in POCKETS:
|
||||
@ -178,18 +183,20 @@ def split_release_pocket(release, default='Release'):
|
||||
|
||||
|
||||
def require_utf8():
|
||||
'''Can be called by programs that only function in UTF-8 locales'''
|
||||
if locale.getpreferredencoding() != 'UTF-8':
|
||||
"""Can be called by programs that only function in UTF-8 locales"""
|
||||
if locale.getpreferredencoding() != "UTF-8":
|
||||
Logger.error("This program only functions in a UTF-8 locale. Aborting.")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
_vendor_to_distroinfo = {"Debian": distro_info.DebianDistroInfo,
|
||||
"Ubuntu": distro_info.UbuntuDistroInfo}
|
||||
_vendor_to_distroinfo = {
|
||||
"Debian": distro_info.DebianDistroInfo,
|
||||
"Ubuntu": distro_info.UbuntuDistroInfo,
|
||||
}
|
||||
|
||||
|
||||
def vendor_to_distroinfo(vendor):
|
||||
""" vendor_to_distroinfo(string) -> DistroInfo class
|
||||
"""vendor_to_distroinfo(string) -> DistroInfo class
|
||||
|
||||
Convert a string name of a distribution into a DistroInfo subclass
|
||||
representing that distribution, or None if the distribution is
|
||||
@ -199,7 +206,7 @@ def vendor_to_distroinfo(vendor):
|
||||
|
||||
|
||||
def codename_to_distribution(codename):
|
||||
""" codename_to_distribution(string) -> string
|
||||
"""codename_to_distribution(string) -> string
|
||||
|
||||
Finds a given release codename in your distribution's genaology
|
||||
(i.e. looking at the current distribution and its parents), or
|
||||
@ -215,7 +222,7 @@ def codename_to_distribution(codename):
|
||||
|
||||
|
||||
def verify_file_checksums(pathname, checksums={}, size=0):
|
||||
""" verify checksums of file
|
||||
"""verify checksums of file
|
||||
|
||||
Any failure will log an error.
|
||||
|
||||
@ -231,16 +238,16 @@ def verify_file_checksums(pathname, checksums={}, size=0):
|
||||
p = Path(pathname)
|
||||
|
||||
if not p.is_file():
|
||||
Logger.error(f'File {p} not found')
|
||||
Logger.error(f"File {p} not found")
|
||||
return False
|
||||
filesize = p.stat().st_size
|
||||
if size and size != filesize:
|
||||
Logger.error(f'File {p} incorrect size, got {filesize} expected {size}')
|
||||
Logger.error(f"File {p} incorrect size, got {filesize} expected {size}")
|
||||
return False
|
||||
|
||||
for (alg, checksum) in checksums.items():
|
||||
h = hashlib.new(alg)
|
||||
with p.open('rb') as f:
|
||||
with p.open("rb") as f:
|
||||
while True:
|
||||
block = f.read(h.block_size)
|
||||
if len(block) == 0:
|
||||
@ -248,15 +255,15 @@ def verify_file_checksums(pathname, checksums={}, size=0):
|
||||
h.update(block)
|
||||
digest = h.hexdigest()
|
||||
if digest == checksum:
|
||||
Logger.debug(f'File {p} checksum ({alg}) verified: {checksum}')
|
||||
Logger.debug(f"File {p} checksum ({alg}) verified: {checksum}")
|
||||
else:
|
||||
Logger.error(f'File {p} checksum ({alg}) mismatch: got {digest} expected {checksum}')
|
||||
Logger.error(f"File {p} checksum ({alg}) mismatch: got {digest} expected {checksum}")
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def verify_file_checksum(pathname, alg, checksum, size=0):
|
||||
""" verify checksum of file
|
||||
"""verify checksum of file
|
||||
|
||||
pathname: str or Path
|
||||
full path to file
|
||||
@ -273,7 +280,7 @@ def verify_file_checksum(pathname, alg, checksum, size=0):
|
||||
|
||||
|
||||
def extract_authentication(url):
|
||||
""" Remove plaintext authentication data from a URL
|
||||
"""Remove plaintext authentication data from a URL
|
||||
|
||||
If the URL has a username:password in its netloc, this removes it
|
||||
and returns the remaining URL, along with the username and password
|
||||
@ -289,7 +296,7 @@ def extract_authentication(url):
|
||||
|
||||
|
||||
def download(src, dst, size=0, *, blocksize=DOWNLOAD_BLOCKSIZE_DEFAULT):
|
||||
""" download/copy a file/url to local file
|
||||
"""download/copy a file/url to local file
|
||||
|
||||
src: str or Path
|
||||
Source to copy from (file path or url)
|
||||
@ -315,18 +322,18 @@ def download(src, dst, size=0, *, blocksize=DOWNLOAD_BLOCKSIZE_DEFAULT):
|
||||
dst = dst / Path(parsedsrc.path).name
|
||||
|
||||
# Copy if src is a local file
|
||||
if parsedsrc.scheme in ['', 'file']:
|
||||
if parsedsrc.scheme in ["", "file"]:
|
||||
src = Path(parsedsrc.path).expanduser().resolve()
|
||||
if src != parsedsrc.path:
|
||||
Logger.info(f'Parsed {parsedsrc.path} as {src}')
|
||||
Logger.info(f"Parsed {parsedsrc.path} as {src}")
|
||||
if not src.exists():
|
||||
raise NotFoundError(f'Source file {src} not found')
|
||||
raise NotFoundError(f"Source file {src} not found")
|
||||
if dst.exists():
|
||||
if src.samefile(dst):
|
||||
Logger.info(f'Using existing file {dst}')
|
||||
Logger.info(f"Using existing file {dst}")
|
||||
return dst
|
||||
Logger.info(f'Replacing existing file {dst}')
|
||||
Logger.info(f'Copying file {src} to {dst}')
|
||||
Logger.info(f"Replacing existing file {dst}")
|
||||
Logger.info(f"Copying file {src} to {dst}")
|
||||
shutil.copyfile(src, dst)
|
||||
return dst
|
||||
|
||||
@ -334,18 +341,18 @@ def download(src, dst, size=0, *, blocksize=DOWNLOAD_BLOCKSIZE_DEFAULT):
|
||||
auth = (username, password) if username or password else None
|
||||
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
tmpdst = Path(d) / 'dst'
|
||||
tmpdst = Path(d) / "dst"
|
||||
try:
|
||||
with requests.get(src, stream=True, auth=auth) as fsrc, tmpdst.open('wb') as fdst:
|
||||
with requests.get(src, stream=True, auth=auth) as fsrc, tmpdst.open("wb") as fdst:
|
||||
fsrc.raise_for_status()
|
||||
_download(fsrc, fdst, size, blocksize=blocksize)
|
||||
except requests.exceptions.HTTPError as e:
|
||||
if e.response is not None and e.response.status_code == 404:
|
||||
raise NotFoundError(f'URL {src} not found: {e}')
|
||||
raise NotFoundError(f"URL {src} not found: {e}")
|
||||
raise DownloadError(e)
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
# This is likely a archive hostname that doesn't resolve, like 'ftpmaster.internal'
|
||||
raise NotFoundError(f'URL {src} not found: {e}')
|
||||
raise NotFoundError(f"URL {src} not found: {e}")
|
||||
except requests.exceptions.RequestException as e:
|
||||
raise DownloadError(e)
|
||||
shutil.move(tmpdst, dst)
|
||||
@ -358,60 +365,64 @@ class _StderrProgressBar(object):
|
||||
|
||||
def __init__(self, max_width):
|
||||
self.full_width = min(max_width, self.BAR_WIDTH_DEFAULT)
|
||||
self.width = self.full_width - len('[] 99%')
|
||||
self.width = self.full_width - len("[] 99%")
|
||||
self.show_progress = self.full_width >= self.BAR_WIDTH_MIN
|
||||
|
||||
def update(self, progress, total):
|
||||
if not self.show_progress:
|
||||
return
|
||||
pct = progress * 100 // total
|
||||
pctstr = f'{pct:>3}%'
|
||||
pctstr = f"{pct:>3}%"
|
||||
barlen = self.width * pct // 100
|
||||
barstr = '=' * barlen
|
||||
barstr = barstr[:-1] + '>'
|
||||
barstr = "=" * barlen
|
||||
barstr = barstr[:-1] + ">"
|
||||
barstr = barstr.ljust(self.width)
|
||||
fullstr = f'\r[{barstr}]{pctstr}'
|
||||
fullstr = f"\r[{barstr}]{pctstr}"
|
||||
sys.stderr.write(fullstr)
|
||||
sys.stderr.flush()
|
||||
|
||||
def finish(self):
|
||||
if not self.show_progress:
|
||||
return
|
||||
sys.stderr.write('\n')
|
||||
sys.stderr.write("\n")
|
||||
sys.stderr.flush()
|
||||
|
||||
|
||||
def _download(fsrc, fdst, size, *, blocksize):
|
||||
""" helper method to download src to dst using requests library. """
|
||||
"""helper method to download src to dst using requests library."""
|
||||
url = fsrc.url
|
||||
Logger.debug(f'Using URL: {url}')
|
||||
Logger.debug(f"Using URL: {url}")
|
||||
|
||||
if not size:
|
||||
with suppress(AttributeError, TypeError, ValueError):
|
||||
size = int(fsrc.headers.get('Content-Length'))
|
||||
size = int(fsrc.headers.get("Content-Length"))
|
||||
|
||||
parsed = urlparse(url)
|
||||
filename = Path(parsed.path).name
|
||||
hostname = parsed.hostname
|
||||
sizemb = ' (%0.3f MiB)' % (size / 1024.0 / 1024) if size else ''
|
||||
Logger.info(f'Downloading {filename} from {hostname}{sizemb}')
|
||||
sizemb = " (%0.3f MiB)" % (size / 1024.0 / 1024) if size else ""
|
||||
Logger.info(f"Downloading {filename} from {hostname}{sizemb}")
|
||||
|
||||
# Don't show progress if:
|
||||
# logging INFO is suppressed
|
||||
# stderr isn't a tty
|
||||
# we don't know the total file size
|
||||
# the file is content-encoded (i.e. compressed)
|
||||
show_progress = all((Logger.isEnabledFor(logging.INFO),
|
||||
sys.stderr.isatty(),
|
||||
size > 0,
|
||||
'Content-Encoding' not in fsrc.headers))
|
||||
show_progress = all(
|
||||
(
|
||||
Logger.isEnabledFor(logging.INFO),
|
||||
sys.stderr.isatty(),
|
||||
size > 0,
|
||||
"Content-Encoding" not in fsrc.headers,
|
||||
)
|
||||
)
|
||||
|
||||
terminal_width = 0
|
||||
if show_progress:
|
||||
try:
|
||||
terminal_width = os.get_terminal_size(sys.stderr.fileno()).columns
|
||||
except Exception as e:
|
||||
Logger.error(f'Error finding stderr width, suppressing progress bar: {e}')
|
||||
Logger.error(f"Error finding stderr width, suppressing progress bar: {e}")
|
||||
progress_bar = _StderrProgressBar(max_width=terminal_width)
|
||||
|
||||
downloaded = 0
|
||||
@ -423,20 +434,21 @@ def _download(fsrc, fdst, size, *, blocksize):
|
||||
finally:
|
||||
progress_bar.finish()
|
||||
if size and size > downloaded:
|
||||
Logger.error('Partial download: %0.3f MiB of %0.3f MiB' %
|
||||
(downloaded / 1024.0 / 1024,
|
||||
size / 1024.0 / 1024))
|
||||
Logger.error(
|
||||
"Partial download: %0.3f MiB of %0.3f MiB"
|
||||
% (downloaded / 1024.0 / 1024, size / 1024.0 / 1024)
|
||||
)
|
||||
|
||||
|
||||
def _download_text(src, binary, *, blocksize):
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
dst = Path(d) / 'dst'
|
||||
dst = Path(d) / "dst"
|
||||
download(src, dst, blocksize=blocksize)
|
||||
return dst.read_bytes() if binary else dst.read_text()
|
||||
|
||||
|
||||
def download_text(src, mode=None, *, blocksize=DOWNLOAD_BLOCKSIZE_DEFAULT):
|
||||
""" Return the text content of a downloaded file
|
||||
"""Return the text content of a downloaded file
|
||||
|
||||
src: str or Path
|
||||
Source to copy from (file path or url)
|
||||
@ -449,9 +461,9 @@ def download_text(src, mode=None, *, blocksize=DOWNLOAD_BLOCKSIZE_DEFAULT):
|
||||
|
||||
Returns text content of downloaded file
|
||||
"""
|
||||
return _download_text(src, binary='b' in (mode or ''), blocksize=blocksize)
|
||||
return _download_text(src, binary="b" in (mode or ""), blocksize=blocksize)
|
||||
|
||||
|
||||
def download_bytes(src, *, blocksize=DOWNLOAD_BLOCKSIZE_DEFAULT):
|
||||
""" Same as download_text() but returns bytes """
|
||||
"""Same as download_text() but returns bytes"""
|
||||
return _download_text(src, binary=True, blocksize=blocksize)
|
||||
|
@ -34,42 +34,50 @@ from distro_info import DebianDistroInfo
|
||||
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from ubuntutools.archive import (UbuntuSourcePackage, DebianSourcePackage,
|
||||
UbuntuCloudArchiveSourcePackage,
|
||||
PersonalPackageArchiveSourcePackage)
|
||||
from ubuntutools.archive import (
|
||||
UbuntuSourcePackage,
|
||||
DebianSourcePackage,
|
||||
UbuntuCloudArchiveSourcePackage,
|
||||
PersonalPackageArchiveSourcePackage,
|
||||
)
|
||||
from ubuntutools.config import UDTConfig
|
||||
from ubuntutools.lp.lpapicache import (Distribution, Launchpad)
|
||||
from ubuntutools.lp.udtexceptions import (AlreadyLoggedInError,
|
||||
SeriesNotFoundException,
|
||||
PackageNotFoundException,
|
||||
PocketDoesNotExistError,
|
||||
InvalidDistroValueError)
|
||||
from ubuntutools.misc import (split_release_pocket,
|
||||
host_architecture,
|
||||
download,
|
||||
UPLOAD_QUEUE_STATUSES,
|
||||
STATUSES)
|
||||
from ubuntutools.lp.lpapicache import Distribution, Launchpad
|
||||
from ubuntutools.lp.udtexceptions import (
|
||||
AlreadyLoggedInError,
|
||||
SeriesNotFoundException,
|
||||
PackageNotFoundException,
|
||||
PocketDoesNotExistError,
|
||||
InvalidDistroValueError,
|
||||
)
|
||||
from ubuntutools.misc import (
|
||||
split_release_pocket,
|
||||
host_architecture,
|
||||
download,
|
||||
UPLOAD_QUEUE_STATUSES,
|
||||
STATUSES,
|
||||
)
|
||||
|
||||
|
||||
# by default we use standard logging.getLogger() and only use
|
||||
# ubuntutools.getLogger() in PullPkg().main()
|
||||
from ubuntutools import getLogger as ubuntutools_getLogger
|
||||
import logging
|
||||
|
||||
Logger = logging.getLogger(__name__)
|
||||
|
||||
PULL_SOURCE = 'source'
|
||||
PULL_DEBS = 'debs'
|
||||
PULL_DDEBS = 'ddebs'
|
||||
PULL_UDEBS = 'udebs'
|
||||
PULL_LIST = 'list'
|
||||
PULL_SOURCE = "source"
|
||||
PULL_DEBS = "debs"
|
||||
PULL_DDEBS = "ddebs"
|
||||
PULL_UDEBS = "udebs"
|
||||
PULL_LIST = "list"
|
||||
|
||||
VALID_PULLS = [PULL_SOURCE, PULL_DEBS, PULL_DDEBS, PULL_UDEBS, PULL_LIST]
|
||||
VALID_BINARY_PULLS = [PULL_DEBS, PULL_DDEBS, PULL_UDEBS]
|
||||
|
||||
DISTRO_DEBIAN = 'debian'
|
||||
DISTRO_UBUNTU = 'ubuntu'
|
||||
DISTRO_UCA = 'uca'
|
||||
DISTRO_PPA = 'ppa'
|
||||
DISTRO_DEBIAN = "debian"
|
||||
DISTRO_UBUNTU = "ubuntu"
|
||||
DISTRO_UCA = "uca"
|
||||
DISTRO_PPA = "ppa"
|
||||
|
||||
DISTRO_PKG_CLASS = {
|
||||
DISTRO_DEBIAN: DebianSourcePackage,
|
||||
@ -81,12 +89,14 @@ VALID_DISTROS = DISTRO_PKG_CLASS.keys()
|
||||
|
||||
|
||||
class InvalidPullValueError(ValueError):
|
||||
""" Thrown when --pull value is invalid """
|
||||
"""Thrown when --pull value is invalid"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class PullPkg(object):
|
||||
"""Class used to pull file(s) associated with a specific package"""
|
||||
|
||||
@classmethod
|
||||
def main(cls, *args, **kwargs):
|
||||
"""For use by stand-alone cmdline scripts.
|
||||
@ -107,53 +117,67 @@ class PullPkg(object):
|
||||
cls(*args, **kwargs).pull()
|
||||
return
|
||||
except KeyboardInterrupt:
|
||||
Logger.info('User abort.')
|
||||
except (PackageNotFoundException, SeriesNotFoundException,
|
||||
PocketDoesNotExistError, InvalidDistroValueError,
|
||||
InvalidPullValueError) as e:
|
||||
Logger.info("User abort.")
|
||||
except (
|
||||
PackageNotFoundException,
|
||||
SeriesNotFoundException,
|
||||
PocketDoesNotExistError,
|
||||
InvalidDistroValueError,
|
||||
InvalidPullValueError,
|
||||
) as e:
|
||||
Logger.error(str(e))
|
||||
sys.exit(errno.ENOENT)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._default_pull = kwargs.get('pull')
|
||||
self._default_distro = kwargs.get('distro')
|
||||
self._default_arch = kwargs.get('arch', host_architecture())
|
||||
self._default_pull = kwargs.get("pull")
|
||||
self._default_distro = kwargs.get("distro")
|
||||
self._default_arch = kwargs.get("arch", host_architecture())
|
||||
|
||||
def parse_args(self, args):
|
||||
args = args[:]
|
||||
|
||||
help_default_pull = "What to pull: " + ", ".join(VALID_PULLS)
|
||||
if self._default_pull:
|
||||
help_default_pull += (" (default: %s)" % self._default_pull)
|
||||
help_default_pull += " (default: %s)" % self._default_pull
|
||||
help_default_distro = "Pull from: " + ", ".join(VALID_DISTROS)
|
||||
if self._default_distro:
|
||||
help_default_distro += (" (default: %s)" % self._default_distro)
|
||||
help_default_arch = ("Get binary packages for arch")
|
||||
help_default_arch += ("(default: %s)" % self._default_arch)
|
||||
help_default_distro += " (default: %s)" % self._default_distro
|
||||
help_default_arch = "Get binary packages for arch"
|
||||
help_default_arch += "(default: %s)" % self._default_arch
|
||||
|
||||
# use add_help=False because we do parse_known_args() below, and if
|
||||
# that sees --help then it exits immediately
|
||||
parser = ArgumentParser(add_help=False)
|
||||
parser.add_argument('-L', '--login', action='store_true',
|
||||
help="Login to Launchpad")
|
||||
parser.add_argument('-v', '--verbose', action='count', default=0,
|
||||
help="Increase verbosity/debug")
|
||||
parser.add_argument('-d', '--download-only', action='store_true',
|
||||
help="Do not extract the source package")
|
||||
parser.add_argument('-m', '--mirror', action='append',
|
||||
help='Preferred mirror(s)')
|
||||
parser.add_argument('--no-conf', action='store_true',
|
||||
help="Don't read config files or environment variables")
|
||||
parser.add_argument('--no-verify-signature', action='store_true',
|
||||
help="Don't fail if dsc signature can't be verified")
|
||||
parser.add_argument('-s', '--status', action='append', default=[],
|
||||
help="Search for packages with specific status(es)")
|
||||
parser.add_argument('-a', '--arch', default=self._default_arch,
|
||||
help=help_default_arch)
|
||||
parser.add_argument('-p', '--pull', default=self._default_pull,
|
||||
help=help_default_pull)
|
||||
parser.add_argument('-D', '--distro', default=self._default_distro,
|
||||
help=help_default_distro)
|
||||
parser.add_argument("-L", "--login", action="store_true", help="Login to Launchpad")
|
||||
parser.add_argument(
|
||||
"-v", "--verbose", action="count", default=0, help="Increase verbosity/debug"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d", "--download-only", action="store_true", help="Do not extract the source package"
|
||||
)
|
||||
parser.add_argument("-m", "--mirror", action="append", help="Preferred mirror(s)")
|
||||
parser.add_argument(
|
||||
"--no-conf",
|
||||
action="store_true",
|
||||
help="Don't read config files or environment variables",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-verify-signature",
|
||||
action="store_true",
|
||||
help="Don't fail if dsc signature can't be verified",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--status",
|
||||
action="append",
|
||||
default=[],
|
||||
help="Search for packages with specific status(es)",
|
||||
)
|
||||
parser.add_argument("-a", "--arch", default=self._default_arch, help=help_default_arch)
|
||||
parser.add_argument("-p", "--pull", default=self._default_pull, help=help_default_pull)
|
||||
parser.add_argument(
|
||||
"-D", "--distro", default=self._default_distro, help=help_default_distro
|
||||
)
|
||||
|
||||
# add distro-specific params
|
||||
try:
|
||||
@ -163,30 +187,36 @@ class PullPkg(object):
|
||||
distro = None
|
||||
|
||||
if distro == DISTRO_UBUNTU:
|
||||
parser.add_argument('--security', action='store_true',
|
||||
help='Pull from the Ubuntu Security Team (proposed) PPA')
|
||||
parser.add_argument('--upload-queue', action='store_true',
|
||||
help='Pull from the Ubuntu upload queue')
|
||||
parser.add_argument(
|
||||
"--security",
|
||||
action="store_true",
|
||||
help="Pull from the Ubuntu Security Team (proposed) PPA",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--upload-queue", action="store_true", help="Pull from the Ubuntu upload queue"
|
||||
)
|
||||
if distro == DISTRO_PPA:
|
||||
parser.add_argument('--ppa', help='PPA to pull from')
|
||||
parser.add_argument("--ppa", help="PPA to pull from")
|
||||
if parser.parse_known_args(args)[0].ppa is None:
|
||||
# check for any param starting with "ppa:"
|
||||
# if found, move it to a --ppa param
|
||||
for param in args:
|
||||
if param.startswith('ppa:'):
|
||||
if param.startswith("ppa:"):
|
||||
args.remove(param)
|
||||
args.insert(0, param)
|
||||
args.insert(0, '--ppa')
|
||||
args.insert(0, "--ppa")
|
||||
break
|
||||
|
||||
# add the positional params
|
||||
parser.add_argument('package', help="Package name to pull")
|
||||
parser.add_argument('release', nargs='?', help="Release to pull from")
|
||||
parser.add_argument('version', nargs='?', help="Package version to pull")
|
||||
parser.add_argument("package", help="Package name to pull")
|
||||
parser.add_argument("release", nargs="?", help="Release to pull from")
|
||||
parser.add_argument("version", nargs="?", help="Package version to pull")
|
||||
|
||||
epilog = ("Note on --status: if a version is provided, all status types "
|
||||
"will be searched; if no version is provided, by default only "
|
||||
"'Pending' and 'Published' status will be searched.")
|
||||
epilog = (
|
||||
"Note on --status: if a version is provided, all status types "
|
||||
"will be searched; if no version is provided, by default only "
|
||||
"'Pending' and 'Published' status will be searched."
|
||||
)
|
||||
|
||||
# since parser has no --help handler, create a new parser that does
|
||||
newparser = ArgumentParser(parents=[parser], epilog=epilog)
|
||||
@ -198,11 +228,11 @@ class PullPkg(object):
|
||||
raise InvalidPullValueError("Must specify --pull")
|
||||
|
||||
# allow 'dbgsym' as alias for 'ddebs'
|
||||
if pull == 'dbgsym':
|
||||
if pull == "dbgsym":
|
||||
Logger.debug("Pulling '%s' for '%s'", PULL_DDEBS, pull)
|
||||
pull = PULL_DDEBS
|
||||
# assume anything starting with 'bin' means 'debs'
|
||||
if str(pull).startswith('bin'):
|
||||
if str(pull).startswith("bin"):
|
||||
Logger.debug("Pulling '%s' for '%s'", PULL_DEBS, pull)
|
||||
pull = PULL_DEBS
|
||||
# verify pull action is valid
|
||||
@ -218,11 +248,11 @@ class PullPkg(object):
|
||||
distro = distro.lower()
|
||||
|
||||
# allow 'lp' for 'ubuntu'
|
||||
if distro == 'lp':
|
||||
if distro == "lp":
|
||||
Logger.debug("Using distro '%s' for '%s'", DISTRO_UBUNTU, distro)
|
||||
distro = DISTRO_UBUNTU
|
||||
# assume anything with 'cloud' is UCA
|
||||
if re.match(r'.*cloud.*', distro):
|
||||
if re.match(r".*cloud.*", distro):
|
||||
Logger.debug("Using distro '%s' for '%s'", DISTRO_UCA, distro)
|
||||
distro = DISTRO_UCA
|
||||
# verify distro is valid
|
||||
@ -256,8 +286,7 @@ class PullPkg(object):
|
||||
# let SeriesNotFoundException flow up
|
||||
d.getSeries(release)
|
||||
|
||||
Logger.debug("Using distro '%s' release '%s' pocket '%s'",
|
||||
distro, release, pocket)
|
||||
Logger.debug("Using distro '%s' release '%s' pocket '%s'", distro, release, pocket)
|
||||
return (release, pocket)
|
||||
|
||||
def parse_release_and_version(self, distro, release, version, try_swap=True):
|
||||
@ -281,95 +310,99 @@ class PullPkg(object):
|
||||
# they should all be provided, though the optional ones may be None
|
||||
|
||||
# type bool
|
||||
assert 'verbose' in options
|
||||
assert 'download_only' in options
|
||||
assert 'no_conf' in options
|
||||
assert 'no_verify_signature' in options
|
||||
assert 'status' in options
|
||||
assert "verbose" in options
|
||||
assert "download_only" in options
|
||||
assert "no_conf" in options
|
||||
assert "no_verify_signature" in options
|
||||
assert "status" in options
|
||||
# type string
|
||||
assert 'pull' in options
|
||||
assert 'distro' in options
|
||||
assert 'arch' in options
|
||||
assert 'package' in options
|
||||
assert "pull" in options
|
||||
assert "distro" in options
|
||||
assert "arch" in options
|
||||
assert "package" in options
|
||||
# type string, optional
|
||||
assert 'release' in options
|
||||
assert 'version' in options
|
||||
assert "release" in options
|
||||
assert "version" in options
|
||||
# type list of strings, optional
|
||||
assert 'mirror' in options
|
||||
assert "mirror" in options
|
||||
|
||||
options['pull'] = self.parse_pull(options['pull'])
|
||||
options['distro'] = self.parse_distro(options['distro'])
|
||||
options["pull"] = self.parse_pull(options["pull"])
|
||||
options["distro"] = self.parse_distro(options["distro"])
|
||||
|
||||
# ensure these are always included so we can just check for None/False later
|
||||
options['ppa'] = options.get('ppa', None)
|
||||
options['security'] = options.get('security', False)
|
||||
options['upload_queue'] = options.get('upload_queue', False)
|
||||
options["ppa"] = options.get("ppa", None)
|
||||
options["security"] = options.get("security", False)
|
||||
options["upload_queue"] = options.get("upload_queue", False)
|
||||
|
||||
return options
|
||||
|
||||
def _get_params(self, options):
|
||||
distro = options['distro']
|
||||
pull = options['pull']
|
||||
distro = options["distro"]
|
||||
pull = options["pull"]
|
||||
|
||||
params = {}
|
||||
params['package'] = options['package']
|
||||
params["package"] = options["package"]
|
||||
|
||||
if options['release']:
|
||||
(r, v, p) = self.parse_release_and_version(distro, options['release'],
|
||||
options['version'])
|
||||
params['series'] = r
|
||||
params['version'] = v
|
||||
params['pocket'] = p
|
||||
if options["release"]:
|
||||
(r, v, p) = self.parse_release_and_version(
|
||||
distro, options["release"], options["version"]
|
||||
)
|
||||
params["series"] = r
|
||||
params["version"] = v
|
||||
params["pocket"] = p
|
||||
|
||||
if (params['package'].endswith('.dsc') and not params['series'] and not params['version']):
|
||||
params['dscfile'] = params['package']
|
||||
params.pop('package')
|
||||
if params["package"].endswith(".dsc") and not params["series"] and not params["version"]:
|
||||
params["dscfile"] = params["package"]
|
||||
params.pop("package")
|
||||
|
||||
if options['security']:
|
||||
if options['ppa']:
|
||||
Logger.warning('Both --security and --ppa specified, ignoring --ppa')
|
||||
Logger.debug('Checking Ubuntu Security PPA')
|
||||
if options["security"]:
|
||||
if options["ppa"]:
|
||||
Logger.warning("Both --security and --ppa specified, ignoring --ppa")
|
||||
Logger.debug("Checking Ubuntu Security PPA")
|
||||
# --security is just a shortcut for --ppa ppa:ubuntu-security-proposed/ppa
|
||||
options['ppa'] = 'ubuntu-security-proposed/ppa'
|
||||
options["ppa"] = "ubuntu-security-proposed/ppa"
|
||||
|
||||
if options['ppa']:
|
||||
if options['ppa'].startswith('ppa:'):
|
||||
params['ppa'] = options['ppa'][4:]
|
||||
if options["ppa"]:
|
||||
if options["ppa"].startswith("ppa:"):
|
||||
params["ppa"] = options["ppa"][4:]
|
||||
else:
|
||||
params['ppa'] = options['ppa']
|
||||
params["ppa"] = options["ppa"]
|
||||
elif distro == DISTRO_PPA:
|
||||
raise ValueError('Must specify PPA to pull from')
|
||||
raise ValueError("Must specify PPA to pull from")
|
||||
|
||||
mirrors = []
|
||||
if options['mirror']:
|
||||
mirrors.extend(options['mirror'])
|
||||
if options["mirror"]:
|
||||
mirrors.extend(options["mirror"])
|
||||
if pull == PULL_DDEBS:
|
||||
config = UDTConfig(options['no_conf'])
|
||||
ddebs_mirror = config.get_value(distro.upper() + '_DDEBS_MIRROR')
|
||||
config = UDTConfig(options["no_conf"])
|
||||
ddebs_mirror = config.get_value(distro.upper() + "_DDEBS_MIRROR")
|
||||
if ddebs_mirror:
|
||||
mirrors.append(ddebs_mirror)
|
||||
if mirrors:
|
||||
Logger.debug("using mirrors %s", ", ".join(mirrors))
|
||||
params['mirrors'] = mirrors
|
||||
params["mirrors"] = mirrors
|
||||
|
||||
params['verify_signature'] = not options['no_verify_signature']
|
||||
params["verify_signature"] = not options["no_verify_signature"]
|
||||
|
||||
params['status'] = STATUSES if 'all' in options['status'] else options['status']
|
||||
params["status"] = STATUSES if "all" in options["status"] else options["status"]
|
||||
|
||||
# special handling for upload queue
|
||||
if options['upload_queue']:
|
||||
if len(options['status']) > 1:
|
||||
raise ValueError("Too many --status provided, "
|
||||
"can only search for a single status or 'all'")
|
||||
if not options['status']:
|
||||
params['status'] = None
|
||||
elif options['status'][0].lower() == 'all':
|
||||
params['status'] = 'all'
|
||||
elif options['status'][0].capitalize() in UPLOAD_QUEUE_STATUSES:
|
||||
params['status'] = options['status'][0].capitalize()
|
||||
if options["upload_queue"]:
|
||||
if len(options["status"]) > 1:
|
||||
raise ValueError(
|
||||
"Too many --status provided, can only search for a single status or 'all'"
|
||||
)
|
||||
if not options["status"]:
|
||||
params["status"] = None
|
||||
elif options["status"][0].lower() == "all":
|
||||
params["status"] = "all"
|
||||
elif options["status"][0].capitalize() in UPLOAD_QUEUE_STATUSES:
|
||||
params["status"] = options["status"][0].capitalize()
|
||||
else:
|
||||
msg = ("Invalid upload queue status '%s': valid values are %s" %
|
||||
(options['status'][0], ', '.join(UPLOAD_QUEUE_STATUSES)))
|
||||
msg = "Invalid upload queue status '%s': valid values are %s" % (
|
||||
options["status"][0],
|
||||
", ".join(UPLOAD_QUEUE_STATUSES),
|
||||
)
|
||||
raise ValueError(msg)
|
||||
|
||||
return params
|
||||
@ -378,56 +411,58 @@ class PullPkg(object):
|
||||
"""Pull (download) specified package file(s)"""
|
||||
options = self.parse_args(args)
|
||||
|
||||
if options['verbose']:
|
||||
if options["verbose"]:
|
||||
Logger.setLevel(logging.DEBUG)
|
||||
if options['verbose'] > 1:
|
||||
if options["verbose"] > 1:
|
||||
logging.getLogger(__package__).setLevel(logging.DEBUG)
|
||||
|
||||
Logger.debug("pullpkg options: %s", options)
|
||||
|
||||
pull = options['pull']
|
||||
distro = options['distro']
|
||||
pull = options["pull"]
|
||||
distro = options["distro"]
|
||||
|
||||
if options['login']:
|
||||
if options["login"]:
|
||||
Logger.debug("Logging in to Launchpad:")
|
||||
try:
|
||||
Launchpad.login()
|
||||
except AlreadyLoggedInError:
|
||||
Logger.error("Launchpad singleton has already performed a login, "
|
||||
"and its design prevents another login")
|
||||
Logger.error(
|
||||
"Launchpad singleton has already performed a login, "
|
||||
"and its design prevents another login"
|
||||
)
|
||||
Logger.warning("Continuing anyway, with existing Launchpad instance")
|
||||
|
||||
params = self._get_params(options)
|
||||
package = params['package']
|
||||
package = params["package"]
|
||||
|
||||
if options['upload_queue']:
|
||||
if options["upload_queue"]:
|
||||
# upload queue API is different/simpler
|
||||
self.pull_upload_queue(pull, arch=options['arch'],
|
||||
download_only=options['download_only'],
|
||||
**params)
|
||||
self.pull_upload_queue(
|
||||
pull, arch=options["arch"], download_only=options["download_only"], **params
|
||||
)
|
||||
return
|
||||
|
||||
# call implementation, and allow exceptions to flow up to caller
|
||||
srcpkg = DISTRO_PKG_CLASS[distro](**params)
|
||||
spph = srcpkg.lp_spph
|
||||
|
||||
Logger.info('Found %s', spph.display_name)
|
||||
Logger.info("Found %s", spph.display_name)
|
||||
|
||||
if pull == PULL_LIST:
|
||||
Logger.info("Source files:")
|
||||
for f in srcpkg.dsc['Files']:
|
||||
Logger.info(" %s", f['name'])
|
||||
for f in srcpkg.dsc["Files"]:
|
||||
Logger.info(" %s", f["name"])
|
||||
Logger.info("Binary files:")
|
||||
for f in spph.getBinaries(options['arch']):
|
||||
archtext = ''
|
||||
for f in spph.getBinaries(options["arch"]):
|
||||
archtext = ""
|
||||
name = f.getFileName()
|
||||
if name.rpartition('.')[0].endswith('all'):
|
||||
if name.rpartition(".")[0].endswith("all"):
|
||||
archtext = f" ({f.arch})"
|
||||
Logger.info(f" {name}{archtext}")
|
||||
elif pull == PULL_SOURCE:
|
||||
# allow DownloadError to flow up to caller
|
||||
srcpkg.pull()
|
||||
if options['download_only']:
|
||||
if options["download_only"]:
|
||||
Logger.debug("--download-only specified, not extracting")
|
||||
else:
|
||||
srcpkg.unpack()
|
||||
@ -435,70 +470,86 @@ class PullPkg(object):
|
||||
name = None
|
||||
if package != spph.getPackageName():
|
||||
Logger.info("Pulling only binary package '%s'", package)
|
||||
Logger.info("Use package name '%s' to pull all binary packages",
|
||||
spph.getPackageName())
|
||||
Logger.info(
|
||||
"Use package name '%s' to pull all binary packages", spph.getPackageName()
|
||||
)
|
||||
name = package
|
||||
|
||||
# e.g. 'debs' -> 'deb'
|
||||
ext = pull.rstrip('s')
|
||||
ext = pull.rstrip("s")
|
||||
|
||||
if distro == DISTRO_DEBIAN:
|
||||
# Debian ddebs don't use .ddeb extension, unfortunately :(
|
||||
if pull in [PULL_DEBS, PULL_DDEBS]:
|
||||
name = name or '.*'
|
||||
ext = 'deb'
|
||||
name = name or ".*"
|
||||
ext = "deb"
|
||||
if pull == PULL_DEBS:
|
||||
name += r'(?<!-dbgsym)$'
|
||||
name += r"(?<!-dbgsym)$"
|
||||
if pull == PULL_DDEBS:
|
||||
name += r'-dbgsym$'
|
||||
name += r"-dbgsym$"
|
||||
|
||||
# allow DownloadError to flow up to caller
|
||||
total = srcpkg.pull_binaries(name=name, ext=ext, arch=options['arch'])
|
||||
total = srcpkg.pull_binaries(name=name, ext=ext, arch=options["arch"])
|
||||
if total < 1:
|
||||
Logger.error("No %s found for %s %s", pull,
|
||||
package, spph.getVersion())
|
||||
Logger.error("No %s found for %s %s", pull, package, spph.getVersion())
|
||||
else:
|
||||
Logger.error("Internal error: invalid pull value after parse_pull()")
|
||||
raise InvalidPullValueError("Invalid pull value '%s'" % pull)
|
||||
|
||||
def pull_upload_queue(self, pull, *,
|
||||
package, version=None, arch=None, series=None, pocket=None,
|
||||
status=None, download_only=None, **kwargs):
|
||||
def pull_upload_queue(
|
||||
self,
|
||||
pull,
|
||||
*,
|
||||
package,
|
||||
version=None,
|
||||
arch=None,
|
||||
series=None,
|
||||
pocket=None,
|
||||
status=None,
|
||||
download_only=None,
|
||||
**kwargs,
|
||||
):
|
||||
if not series:
|
||||
Logger.error("Using --upload-queue requires specifying series")
|
||||
return
|
||||
|
||||
series = Distribution('ubuntu').getSeries(series)
|
||||
series = Distribution("ubuntu").getSeries(series)
|
||||
|
||||
queueparams = {'name': package}
|
||||
queueparams = {"name": package}
|
||||
if pocket:
|
||||
queueparams['pocket'] = pocket
|
||||
queueparams["pocket"] = pocket
|
||||
|
||||
if status == 'all':
|
||||
queueparams['status'] = None
|
||||
queuetype = 'any'
|
||||
if status == "all":
|
||||
queueparams["status"] = None
|
||||
queuetype = "any"
|
||||
elif status:
|
||||
queueparams['status'] = status
|
||||
queueparams["status"] = status
|
||||
queuetype = status
|
||||
else:
|
||||
queuetype = 'Unapproved'
|
||||
queuetype = "Unapproved"
|
||||
|
||||
packages = [p for p in series.getPackageUploads(**queueparams) if
|
||||
p.package_version == version or
|
||||
str(p.id) == version or
|
||||
not version]
|
||||
packages = [
|
||||
p
|
||||
for p in series.getPackageUploads(**queueparams)
|
||||
if p.package_version == version or str(p.id) == version or not version
|
||||
]
|
||||
|
||||
if pull == PULL_SOURCE:
|
||||
packages = [p for p in packages if p.contains_source]
|
||||
elif pull in VALID_BINARY_PULLS:
|
||||
packages = [p for p in packages if
|
||||
p.contains_build and
|
||||
(arch in ['all', 'any'] or
|
||||
arch in p.display_arches.replace(',', '').split())]
|
||||
packages = [
|
||||
p
|
||||
for p in packages
|
||||
if p.contains_build
|
||||
and (arch in ["all", "any"] or arch in p.display_arches.replace(",", "").split())
|
||||
]
|
||||
|
||||
if not packages:
|
||||
msg = ("Package %s not found in %s upload queue for %s" %
|
||||
(package, queuetype, series.name))
|
||||
msg = "Package %s not found in %s upload queue for %s" % (
|
||||
package,
|
||||
queuetype,
|
||||
series.name,
|
||||
)
|
||||
if version:
|
||||
msg += " with version/id %s" % version
|
||||
if pull in VALID_BINARY_PULLS:
|
||||
@ -563,28 +614,29 @@ class PullPkg(object):
|
||||
dscfile = None
|
||||
for url in urls:
|
||||
dst = download(url, os.getcwd())
|
||||
if dst.name.endswith('.dsc'):
|
||||
if dst.name.endswith(".dsc"):
|
||||
dscfile = dst
|
||||
if download_only:
|
||||
Logger.debug("--download-only specified, not extracting")
|
||||
elif not dscfile:
|
||||
Logger.error("No source dsc file found, cannot extract")
|
||||
else:
|
||||
cmd = ['dpkg-source', '-x', dscfile.name]
|
||||
Logger.debug(' '.join(cmd))
|
||||
result = subprocess.run(cmd, encoding='utf-8',
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
cmd = ["dpkg-source", "-x", dscfile.name]
|
||||
Logger.debug(" ".join(cmd))
|
||||
result = subprocess.run(
|
||||
cmd, encoding="utf-8", stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
||||
)
|
||||
if result.returncode != 0:
|
||||
Logger.error('Source unpack failed.')
|
||||
Logger.error("Source unpack failed.")
|
||||
Logger.debug(result.stdout)
|
||||
else:
|
||||
name = '.*'
|
||||
name = ".*"
|
||||
if pull == PULL_DEBS:
|
||||
name = r'{}(?<!-di)(?<!-dbgsym)$'.format(name)
|
||||
name = r"{}(?<!-di)(?<!-dbgsym)$".format(name)
|
||||
elif pull == PULL_DDEBS:
|
||||
name += '-dbgsym$'
|
||||
name += "-dbgsym$"
|
||||
elif pull == PULL_UDEBS:
|
||||
name += '-di$'
|
||||
name += "-di$"
|
||||
else:
|
||||
raise InvalidPullValueError("Invalid pull value %s" % pull)
|
||||
|
||||
|
@ -57,7 +57,7 @@ class Question(object):
|
||||
try:
|
||||
selected = input(question).strip().lower()
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
print('\nAborting as requested.')
|
||||
print("\nAborting as requested.")
|
||||
sys.exit(1)
|
||||
if selected == "":
|
||||
selected = default
|
||||
@ -86,7 +86,7 @@ def input_number(question, min_number, max_number, default=None):
|
||||
try:
|
||||
selected = input(question).strip()
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
print('\nAborting as requested.')
|
||||
print("\nAborting as requested.")
|
||||
sys.exit(1)
|
||||
if default and selected == "":
|
||||
selected = default
|
||||
@ -102,17 +102,17 @@ def input_number(question, min_number, max_number, default=None):
|
||||
|
||||
|
||||
def confirmation_prompt(message=None, action=None):
|
||||
'''Display message, or a stock message including action, and wait for the
|
||||
user to press Enter
|
||||
'''
|
||||
"""Display message, or a stock message including action, and wait for the
|
||||
user to press Enter
|
||||
"""
|
||||
if message is None:
|
||||
if action is None:
|
||||
action = 'continue'
|
||||
message = 'Press [Enter] to %s. Press [Ctrl-C] to abort now.' % action
|
||||
action = "continue"
|
||||
message = "Press [Enter] to %s. Press [Ctrl-C] to abort now." % action
|
||||
try:
|
||||
input(message)
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
print('\nAborting as requested.')
|
||||
print("\nAborting as requested.")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@ -121,13 +121,13 @@ class EditFile(object):
|
||||
self.filename = filename
|
||||
self.description = description
|
||||
if placeholders is None:
|
||||
placeholders = (re.compile(r'^>>>.*<<<$', re.UNICODE),)
|
||||
placeholders = (re.compile(r"^>>>.*<<<$", re.UNICODE),)
|
||||
self.placeholders = placeholders
|
||||
|
||||
def edit(self, optional=False):
|
||||
if optional:
|
||||
print("\n\nCurrently the %s looks like:" % self.description)
|
||||
with open(self.filename, 'r', encoding='utf-8') as f:
|
||||
with open(self.filename, "r", encoding="utf-8") as f:
|
||||
print(f.read())
|
||||
if YesNoQuestion().ask("Edit", "no") == "no":
|
||||
return
|
||||
@ -135,21 +135,22 @@ class EditFile(object):
|
||||
done = False
|
||||
while not done:
|
||||
old_mtime = os.stat(self.filename).st_mtime
|
||||
subprocess.check_call(['sensible-editor', self.filename])
|
||||
subprocess.check_call(["sensible-editor", self.filename])
|
||||
modified = old_mtime != os.stat(self.filename).st_mtime
|
||||
placeholders_present = False
|
||||
if self.placeholders:
|
||||
with open(self.filename, 'r', encoding='utf-8') as f:
|
||||
with open(self.filename, "r", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
for placeholder in self.placeholders:
|
||||
if placeholder.search(line.strip()):
|
||||
placeholders_present = True
|
||||
|
||||
if placeholders_present:
|
||||
print("Placeholders still present in the %s. "
|
||||
"Please replace them with useful information."
|
||||
% self.description)
|
||||
confirmation_prompt(action='edit again')
|
||||
print(
|
||||
"Placeholders still present in the %s. "
|
||||
"Please replace them with useful information." % self.description
|
||||
)
|
||||
confirmation_prompt(action="edit again")
|
||||
elif not modified:
|
||||
print("The %s was not modified" % self.description)
|
||||
if YesNoQuestion().ask("Edit again", "yes") == "no":
|
||||
@ -158,45 +159,44 @@ class EditFile(object):
|
||||
done = True
|
||||
|
||||
def check_edit(self):
|
||||
'''Override this to implement extra checks on the edited report.
|
||||
"""Override this to implement extra checks on the edited report.
|
||||
Should return False if another round of editing is needed,
|
||||
and should prompt the user to confirm that, if necessary.
|
||||
'''
|
||||
"""
|
||||
return True
|
||||
|
||||
|
||||
class EditBugReport(EditFile):
|
||||
split_re = re.compile(r'^Summary.*?:\s+(.*?)\s+'
|
||||
r'Description:\s+(.*)$',
|
||||
re.DOTALL | re.UNICODE)
|
||||
split_re = re.compile(r"^Summary.*?:\s+(.*?)\s+Description:\s+(.*)$", re.DOTALL | re.UNICODE)
|
||||
|
||||
def __init__(self, subject, body, placeholders=None):
|
||||
prefix = os.path.basename(sys.argv[0]) + '_'
|
||||
tmpfile = tempfile.NamedTemporaryFile(prefix=prefix, suffix='.txt',
|
||||
delete=False)
|
||||
tmpfile.write((u'Summary (one line):\n%s\n\nDescription:\n%s'
|
||||
% (subject, body)).encode('utf-8'))
|
||||
prefix = os.path.basename(sys.argv[0]) + "_"
|
||||
tmpfile = tempfile.NamedTemporaryFile(prefix=prefix, suffix=".txt", delete=False)
|
||||
tmpfile.write(
|
||||
("Summary (one line):\n%s\n\nDescription:\n%s" % (subject, body)).encode("utf-8")
|
||||
)
|
||||
tmpfile.close()
|
||||
super(EditBugReport, self).__init__(tmpfile.name, 'bug report',
|
||||
placeholders)
|
||||
super(EditBugReport, self).__init__(tmpfile.name, "bug report", placeholders)
|
||||
|
||||
def check_edit(self):
|
||||
with open(self.filename, 'r', encoding='utf-8') as f:
|
||||
with open(self.filename, "r", encoding="utf-8") as f:
|
||||
report = f.read()
|
||||
|
||||
if self.split_re.match(report) is None:
|
||||
print("The %s doesn't start with 'Summary:' and 'Description:' "
|
||||
"blocks" % self.description)
|
||||
confirmation_prompt('edit again')
|
||||
print(
|
||||
"The %s doesn't start with 'Summary:' and 'Description:' "
|
||||
"blocks" % self.description
|
||||
)
|
||||
confirmation_prompt("edit again")
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_report(self):
|
||||
with open(self.filename, 'r', encoding='utf-8') as f:
|
||||
with open(self.filename, "r", encoding="utf-8") as f:
|
||||
report = f.read()
|
||||
|
||||
match = self.split_re.match(report)
|
||||
title = match.group(1).replace(u'\n', u' ')
|
||||
title = match.group(1).replace("\n", " ")
|
||||
report = (title, match.group(2))
|
||||
os.unlink(self.filename)
|
||||
return report
|
||||
|
@ -22,13 +22,12 @@ class RDependsException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def query_rdepends(package, release, arch,
|
||||
server='http://qa.ubuntuwire.org/rdepends'):
|
||||
def query_rdepends(package, release, arch, server="http://qa.ubuntuwire.org/rdepends"):
|
||||
"""Look up a packages reverse-dependencies on the Ubuntuwire
|
||||
Reverse- webservice
|
||||
"""
|
||||
|
||||
url = os.path.join(server, 'v1', release, arch, package)
|
||||
url = os.path.join(server, "v1", release, arch, package)
|
||||
|
||||
response, data = httplib2.Http().request(url)
|
||||
if response.status != 200:
|
||||
|
@ -27,16 +27,21 @@ from distro_info import DebianDistroInfo, DistroDataOutdated
|
||||
from httplib2 import Http, HttpLib2Error
|
||||
|
||||
from ubuntutools.lp import udtexceptions
|
||||
from ubuntutools.lp.lpapicache import (Launchpad, Distribution, PersonTeam,
|
||||
DistributionSourcePackage)
|
||||
from ubuntutools.lp.lpapicache import (
|
||||
Launchpad,
|
||||
Distribution,
|
||||
PersonTeam,
|
||||
DistributionSourcePackage,
|
||||
)
|
||||
from ubuntutools.question import confirmation_prompt
|
||||
|
||||
import logging
|
||||
|
||||
Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_debian_srcpkg(name, release):
|
||||
debian = Distribution('debian')
|
||||
debian = Distribution("debian")
|
||||
debian_archive = debian.getArchive()
|
||||
|
||||
try:
|
||||
@ -47,82 +52,86 @@ def get_debian_srcpkg(name, release):
|
||||
return debian_archive.getSourcePackage(name, release)
|
||||
|
||||
|
||||
def get_ubuntu_srcpkg(name, release, pocket='Release'):
|
||||
ubuntu = Distribution('ubuntu')
|
||||
def get_ubuntu_srcpkg(name, release, pocket="Release"):
|
||||
ubuntu = Distribution("ubuntu")
|
||||
ubuntu_archive = ubuntu.getArchive()
|
||||
|
||||
try:
|
||||
return ubuntu_archive.getSourcePackage(name, release, pocket)
|
||||
except udtexceptions.PackageNotFoundException:
|
||||
if pocket != 'Release':
|
||||
parent_pocket = 'Release'
|
||||
if pocket == 'Updates':
|
||||
parent_pocket = 'Proposed'
|
||||
if pocket != "Release":
|
||||
parent_pocket = "Release"
|
||||
if pocket == "Updates":
|
||||
parent_pocket = "Proposed"
|
||||
return get_ubuntu_srcpkg(name, release, parent_pocket)
|
||||
raise
|
||||
|
||||
|
||||
def need_sponsorship(name, component, release):
|
||||
'''
|
||||
"""
|
||||
Check if the user has upload permissions for either the package
|
||||
itself or the component
|
||||
'''
|
||||
archive = Distribution('ubuntu').getArchive()
|
||||
distroseries = Distribution('ubuntu').getSeries(release)
|
||||
"""
|
||||
archive = Distribution("ubuntu").getArchive()
|
||||
distroseries = Distribution("ubuntu").getSeries(release)
|
||||
|
||||
need_sponsor = not PersonTeam.me.canUploadPackage(archive, distroseries,
|
||||
name, component)
|
||||
need_sponsor = not PersonTeam.me.canUploadPackage(archive, distroseries, name, component)
|
||||
if need_sponsor:
|
||||
print('''You are not able to upload this package directly to Ubuntu.
|
||||
print(
|
||||
"""You are not able to upload this package directly to Ubuntu.
|
||||
Your sync request shall require an approval by a member of the appropriate
|
||||
sponsorship team, who shall be subscribed to this bug report.
|
||||
This must be done before it can be processed by a member of the Ubuntu Archive
|
||||
team.''')
|
||||
team."""
|
||||
)
|
||||
confirmation_prompt()
|
||||
|
||||
return need_sponsor
|
||||
|
||||
|
||||
def check_existing_reports(srcpkg):
|
||||
'''
|
||||
"""
|
||||
Check existing bug reports on Launchpad for a possible sync request.
|
||||
|
||||
If found ask for confirmation on filing a request.
|
||||
'''
|
||||
"""
|
||||
|
||||
# Fetch the package's bug list from Launchpad
|
||||
pkg = Distribution('ubuntu').getSourcePackage(name=srcpkg)
|
||||
pkg_bug_list = pkg.searchTasks(status=["Incomplete", "New", "Confirmed",
|
||||
"Triaged", "In Progress",
|
||||
"Fix Committed"],
|
||||
omit_duplicates=True)
|
||||
pkg = Distribution("ubuntu").getSourcePackage(name=srcpkg)
|
||||
pkg_bug_list = pkg.searchTasks(
|
||||
status=["Incomplete", "New", "Confirmed", "Triaged", "In Progress", "Fix Committed"],
|
||||
omit_duplicates=True,
|
||||
)
|
||||
|
||||
# Search bug list for other sync requests.
|
||||
for bug in pkg_bug_list:
|
||||
# check for Sync or sync and the package name
|
||||
if not bug.is_complete and 'ync %s' % srcpkg in bug.title:
|
||||
print('The following bug could be a possible duplicate sync bug '
|
||||
'on Launchpad:\n'
|
||||
' * %s (%s)\n'
|
||||
'Please check the above URL to verify this before '
|
||||
'continuing.'
|
||||
% (bug.title, bug.web_link))
|
||||
if not bug.is_complete and "ync %s" % srcpkg in bug.title:
|
||||
print(
|
||||
"The following bug could be a possible duplicate sync bug "
|
||||
"on Launchpad:\n"
|
||||
" * %s (%s)\n"
|
||||
"Please check the above URL to verify this before "
|
||||
"continuing." % (bug.title, bug.web_link)
|
||||
)
|
||||
confirmation_prompt()
|
||||
|
||||
|
||||
def get_ubuntu_delta_changelog(srcpkg):
|
||||
'''
|
||||
"""
|
||||
Download the Ubuntu changelog and extract the entries since the last sync
|
||||
from Debian.
|
||||
'''
|
||||
archive = Distribution('ubuntu').getArchive()
|
||||
spph = archive.getPublishedSources(source_name=srcpkg.getPackageName(),
|
||||
exact_match=True, pocket='Release')
|
||||
"""
|
||||
archive = Distribution("ubuntu").getArchive()
|
||||
spph = archive.getPublishedSources(
|
||||
source_name=srcpkg.getPackageName(), exact_match=True, pocket="Release"
|
||||
)
|
||||
debian_info = DebianDistroInfo()
|
||||
topline = re.compile(r'^(\w%(name_chars)s*) \(([^\(\) \t]+)\)'
|
||||
r'((\s+%(name_chars)s+)+)\;'
|
||||
% {'name_chars': '[-+0-9a-z.]'},
|
||||
re.IGNORECASE)
|
||||
topline = re.compile(
|
||||
r"^(\w%(name_chars)s*) \(([^\(\) \t]+)\)"
|
||||
r"((\s+%(name_chars)s+)+)\;" % {"name_chars": "[-+0-9a-z.]"},
|
||||
re.IGNORECASE,
|
||||
)
|
||||
delta = []
|
||||
for record in spph:
|
||||
changes_url = record.changesFileUrl()
|
||||
@ -135,56 +144,53 @@ def get_ubuntu_delta_changelog(srcpkg):
|
||||
Logger.error(str(e))
|
||||
break
|
||||
if response.status != 200:
|
||||
Logger.error("%s: %s %s", changes_url, response.status,
|
||||
response.reason)
|
||||
Logger.error("%s: %s %s", changes_url, response.status, response.reason)
|
||||
break
|
||||
|
||||
changes = Changes(Http().request(changes_url)[1])
|
||||
for line in changes['Changes'].splitlines():
|
||||
for line in changes["Changes"].splitlines():
|
||||
line = line[1:]
|
||||
m = topline.match(line)
|
||||
if m:
|
||||
distribution = m.group(3).split()[0].split('-')[0]
|
||||
distribution = m.group(3).split()[0].split("-")[0]
|
||||
if debian_info.valid(distribution):
|
||||
break
|
||||
if line.startswith(u' '):
|
||||
if line.startswith(" "):
|
||||
delta.append(line)
|
||||
else:
|
||||
continue
|
||||
break
|
||||
|
||||
return '\n'.join(delta)
|
||||
return "\n".join(delta)
|
||||
|
||||
|
||||
def post_bug(srcpkg, subscribe, status, bugtitle, bugtext):
|
||||
'''
|
||||
"""
|
||||
Use the LP API to file the sync request.
|
||||
'''
|
||||
"""
|
||||
|
||||
print('The final report is:\nSummary: %s\nDescription:\n%s\n'
|
||||
% (bugtitle, bugtext))
|
||||
print("The final report is:\nSummary: %s\nDescription:\n%s\n" % (bugtitle, bugtext))
|
||||
confirmation_prompt()
|
||||
|
||||
if srcpkg:
|
||||
bug_target = DistributionSourcePackage(
|
||||
'%subuntu/+source/%s' % (Launchpad._root_uri, srcpkg))
|
||||
"%subuntu/+source/%s" % (Launchpad._root_uri, srcpkg)
|
||||
)
|
||||
else:
|
||||
# new source package
|
||||
bug_target = Distribution('ubuntu')
|
||||
bug_target = Distribution("ubuntu")
|
||||
|
||||
# create bug
|
||||
bug = Launchpad.bugs.createBug(title=bugtitle, description=bugtext,
|
||||
target=bug_target())
|
||||
bug = Launchpad.bugs.createBug(title=bugtitle, description=bugtext, target=bug_target())
|
||||
|
||||
# newly created bugreports have only one task
|
||||
task = bug.bug_tasks[0]
|
||||
# only members of ubuntu-bugcontrol can set importance
|
||||
if PersonTeam.me.isLpTeamMember('ubuntu-bugcontrol'):
|
||||
task.importance = 'Wishlist'
|
||||
if PersonTeam.me.isLpTeamMember("ubuntu-bugcontrol"):
|
||||
task.importance = "Wishlist"
|
||||
task.status = status
|
||||
task.lp_save()
|
||||
|
||||
bug.subscribe(person=PersonTeam(subscribe)())
|
||||
|
||||
print('Sync request filed as bug #%i: %s'
|
||||
% (bug.id, bug.web_link))
|
||||
print("Sync request filed as bug #%i: %s" % (bug.id, bug.web_link))
|
||||
|
@ -36,16 +36,17 @@ from ubuntutools.lp.udtexceptions import PackageNotFoundException
|
||||
from ubuntutools.question import confirmation_prompt, YesNoQuestion
|
||||
|
||||
import logging
|
||||
|
||||
Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
__all__ = [
|
||||
'get_debian_srcpkg',
|
||||
'get_ubuntu_srcpkg',
|
||||
'need_sponsorship',
|
||||
'check_existing_reports',
|
||||
'get_ubuntu_delta_changelog',
|
||||
'mail_bug',
|
||||
"get_debian_srcpkg",
|
||||
"get_ubuntu_srcpkg",
|
||||
"need_sponsorship",
|
||||
"check_existing_reports",
|
||||
"get_ubuntu_delta_changelog",
|
||||
"mail_bug",
|
||||
]
|
||||
|
||||
|
||||
@ -67,73 +68,91 @@ def get_ubuntu_srcpkg(name, release):
|
||||
|
||||
|
||||
def need_sponsorship(name, component, release):
|
||||
'''
|
||||
"""
|
||||
Ask the user if he has upload permissions for the package or the
|
||||
component.
|
||||
'''
|
||||
"""
|
||||
|
||||
val = YesNoQuestion().ask("Do you have upload permissions for the '%s' component or "
|
||||
"the package '%s' in Ubuntu %s?\nIf in doubt answer 'n'." %
|
||||
(component, name, release), 'no')
|
||||
return val == 'no'
|
||||
val = YesNoQuestion().ask(
|
||||
"Do you have upload permissions for the '%s' component or "
|
||||
"the package '%s' in Ubuntu %s?\nIf in doubt answer 'n'." % (component, name, release),
|
||||
"no",
|
||||
)
|
||||
return val == "no"
|
||||
|
||||
|
||||
def check_existing_reports(srcpkg):
|
||||
'''
|
||||
"""
|
||||
Point the user to the URL to manually check for duplicate bug reports.
|
||||
'''
|
||||
print('Please check on '
|
||||
'https://bugs.launchpad.net/ubuntu/+source/%s/+bugs\n'
|
||||
'for duplicate sync requests before continuing.' % srcpkg)
|
||||
"""
|
||||
print(
|
||||
"Please check on "
|
||||
"https://bugs.launchpad.net/ubuntu/+source/%s/+bugs\n"
|
||||
"for duplicate sync requests before continuing." % srcpkg
|
||||
)
|
||||
confirmation_prompt()
|
||||
|
||||
|
||||
def get_ubuntu_delta_changelog(srcpkg):
|
||||
'''
|
||||
"""
|
||||
Download the Ubuntu changelog and extract the entries since the last sync
|
||||
from Debian.
|
||||
'''
|
||||
"""
|
||||
changelog = Changelog(srcpkg.getChangelog())
|
||||
if changelog is None:
|
||||
return ''
|
||||
return ""
|
||||
delta = []
|
||||
debian_info = DebianDistroInfo()
|
||||
for block in changelog:
|
||||
distribution = block.distributions.split()[0].split('-')[0]
|
||||
distribution = block.distributions.split()[0].split("-")[0]
|
||||
if debian_info.valid(distribution):
|
||||
break
|
||||
delta += [str(change) for change in block.changes()
|
||||
if change.strip()]
|
||||
delta += [str(change) for change in block.changes() if change.strip()]
|
||||
|
||||
return '\n'.join(delta)
|
||||
return "\n".join(delta)
|
||||
|
||||
|
||||
def mail_bug(srcpkg, subscribe, status, bugtitle, bugtext, bug_mail_domain,
|
||||
keyid, myemailaddr, mailserver_host, mailserver_port,
|
||||
mailserver_user, mailserver_pass):
|
||||
'''
|
||||
def mail_bug(
|
||||
srcpkg,
|
||||
subscribe,
|
||||
status,
|
||||
bugtitle,
|
||||
bugtext,
|
||||
bug_mail_domain,
|
||||
keyid,
|
||||
myemailaddr,
|
||||
mailserver_host,
|
||||
mailserver_port,
|
||||
mailserver_user,
|
||||
mailserver_pass,
|
||||
):
|
||||
"""
|
||||
Submit the sync request per email.
|
||||
'''
|
||||
"""
|
||||
|
||||
to = 'new@' + bug_mail_domain
|
||||
to = "new@" + bug_mail_domain
|
||||
|
||||
# generate mailbody
|
||||
if srcpkg:
|
||||
mailbody = ' affects ubuntu/%s\n' % srcpkg
|
||||
mailbody = " affects ubuntu/%s\n" % srcpkg
|
||||
else:
|
||||
mailbody = ' affects ubuntu\n'
|
||||
mailbody += '''\
|
||||
mailbody = " affects ubuntu\n"
|
||||
mailbody += """\
|
||||
status %s
|
||||
importance wishlist
|
||||
subscribe %s
|
||||
done
|
||||
|
||||
%s''' % (status, subscribe, bugtext)
|
||||
%s""" % (
|
||||
status,
|
||||
subscribe,
|
||||
bugtext,
|
||||
)
|
||||
|
||||
# prepare sign command
|
||||
gpg_command = None
|
||||
for cmd in ('gnome-gpg', 'gpg2', 'gpg'):
|
||||
if os.access('/usr/bin/%s' % cmd, os.X_OK):
|
||||
for cmd in ("gnome-gpg", "gpg2", "gpg"):
|
||||
if os.access("/usr/bin/%s" % cmd, os.X_OK):
|
||||
gpg_command = [cmd]
|
||||
break
|
||||
|
||||
@ -141,107 +160,135 @@ def mail_bug(srcpkg, subscribe, status, bugtitle, bugtext, bug_mail_domain,
|
||||
Logger.error("Cannot locate gpg, please install the 'gnupg' package!")
|
||||
sys.exit(1)
|
||||
|
||||
gpg_command.append('--clearsign')
|
||||
gpg_command.append("--clearsign")
|
||||
if keyid:
|
||||
gpg_command.extend(('-u', keyid))
|
||||
gpg_command.extend(("-u", keyid))
|
||||
|
||||
# sign the mail body
|
||||
gpg = subprocess.Popen(
|
||||
gpg_command, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||
encoding='utf-8')
|
||||
gpg_command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, encoding="utf-8"
|
||||
)
|
||||
signed_report = gpg.communicate(mailbody)[0]
|
||||
if gpg.returncode != 0:
|
||||
Logger.error("%s failed.", gpg_command[0])
|
||||
sys.exit(1)
|
||||
|
||||
# generate email
|
||||
mail = '''\
|
||||
mail = """\
|
||||
From: %s
|
||||
To: %s
|
||||
Subject: %s
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
|
||||
%s''' % (myemailaddr, to, bugtitle, signed_report)
|
||||
%s""" % (
|
||||
myemailaddr,
|
||||
to,
|
||||
bugtitle,
|
||||
signed_report,
|
||||
)
|
||||
|
||||
print('The final report is:\n%s' % mail)
|
||||
print("The final report is:\n%s" % mail)
|
||||
confirmation_prompt()
|
||||
|
||||
# save mail in temporary file
|
||||
backup = tempfile.NamedTemporaryFile(
|
||||
mode='w',
|
||||
mode="w",
|
||||
delete=False,
|
||||
prefix='requestsync-' + re.sub(r'[^a-zA-Z0-9_-]', '', bugtitle.replace(' ', '_'))
|
||||
prefix="requestsync-" + re.sub(r"[^a-zA-Z0-9_-]", "", bugtitle.replace(" ", "_")),
|
||||
)
|
||||
with backup:
|
||||
backup.write(mail)
|
||||
|
||||
Logger.info('The e-mail has been saved in %s and will be deleted '
|
||||
'after succesful transmission', backup.name)
|
||||
Logger.info(
|
||||
"The e-mail has been saved in %s and will be deleted after succesful transmission",
|
||||
backup.name,
|
||||
)
|
||||
|
||||
# connect to the server
|
||||
while True:
|
||||
try:
|
||||
Logger.info('Connecting to %s:%s ...', mailserver_host,
|
||||
mailserver_port)
|
||||
Logger.info("Connecting to %s:%s ...", mailserver_host, mailserver_port)
|
||||
s = smtplib.SMTP(mailserver_host, mailserver_port)
|
||||
break
|
||||
except smtplib.SMTPConnectError as s:
|
||||
try:
|
||||
# py2 path
|
||||
# pylint: disable=unsubscriptable-object
|
||||
Logger.error('Could not connect to %s:%s: %s (%i)',
|
||||
mailserver_host, mailserver_port, s[1], s[0])
|
||||
Logger.error(
|
||||
"Could not connect to %s:%s: %s (%i)",
|
||||
mailserver_host,
|
||||
mailserver_port,
|
||||
s[1],
|
||||
s[0],
|
||||
)
|
||||
except TypeError:
|
||||
# pylint: disable=no-member
|
||||
Logger.error('Could not connect to %s:%s: %s (%i)',
|
||||
mailserver_host, mailserver_port, s.strerror, s.errno)
|
||||
Logger.error(
|
||||
"Could not connect to %s:%s: %s (%i)",
|
||||
mailserver_host,
|
||||
mailserver_port,
|
||||
s.strerror,
|
||||
s.errno,
|
||||
)
|
||||
if s.smtp_code == 421:
|
||||
confirmation_prompt(message='This is a temporary error, press [Enter] '
|
||||
'to retry. Press [Ctrl-C] to abort now.')
|
||||
confirmation_prompt(
|
||||
message="This is a temporary error, press [Enter] "
|
||||
"to retry. Press [Ctrl-C] to abort now."
|
||||
)
|
||||
except socket.error as s:
|
||||
try:
|
||||
# py2 path
|
||||
# pylint: disable=unsubscriptable-object
|
||||
Logger.error('Could not connect to %s:%s: %s (%i)',
|
||||
mailserver_host, mailserver_port, s[1], s[0])
|
||||
Logger.error(
|
||||
"Could not connect to %s:%s: %s (%i)",
|
||||
mailserver_host,
|
||||
mailserver_port,
|
||||
s[1],
|
||||
s[0],
|
||||
)
|
||||
except TypeError:
|
||||
# pylint: disable=no-member
|
||||
Logger.error('Could not connect to %s:%s: %s (%i)',
|
||||
mailserver_host, mailserver_port, s.strerror, s.errno)
|
||||
Logger.error(
|
||||
"Could not connect to %s:%s: %s (%i)",
|
||||
mailserver_host,
|
||||
mailserver_port,
|
||||
s.strerror,
|
||||
s.errno,
|
||||
)
|
||||
return
|
||||
|
||||
if mailserver_user and mailserver_pass:
|
||||
try:
|
||||
s.login(mailserver_user, mailserver_pass)
|
||||
except smtplib.SMTPAuthenticationError:
|
||||
Logger.error('Error authenticating to the server: '
|
||||
'invalid username and password.')
|
||||
Logger.error("Error authenticating to the server: invalid username and password.")
|
||||
s.quit()
|
||||
return
|
||||
except smtplib.SMTPException:
|
||||
Logger.error('Unknown SMTP error.')
|
||||
Logger.error("Unknown SMTP error.")
|
||||
s.quit()
|
||||
return
|
||||
|
||||
while True:
|
||||
try:
|
||||
s.sendmail(myemailaddr, to, mail.encode('utf-8'))
|
||||
s.sendmail(myemailaddr, to, mail.encode("utf-8"))
|
||||
s.quit()
|
||||
os.remove(backup.name)
|
||||
Logger.info('Sync request mailed.')
|
||||
Logger.info("Sync request mailed.")
|
||||
break
|
||||
except smtplib.SMTPRecipientsRefused as smtperror:
|
||||
smtp_code, smtp_message = smtperror.recipients[to]
|
||||
Logger.error('Error while sending: %i, %s', smtp_code, smtp_message)
|
||||
Logger.error("Error while sending: %i, %s", smtp_code, smtp_message)
|
||||
if smtp_code == 450:
|
||||
confirmation_prompt(message='This is a temporary error, press [Enter] '
|
||||
'to retry. Press [Ctrl-C] to abort now.')
|
||||
confirmation_prompt(
|
||||
message="This is a temporary error, press [Enter] "
|
||||
"to retry. Press [Ctrl-C] to abort now."
|
||||
)
|
||||
else:
|
||||
return
|
||||
except smtplib.SMTPResponseException as e:
|
||||
Logger.error('Error while sending: %i, %s',
|
||||
e.smtp_code, e.smtp_error)
|
||||
Logger.error("Error while sending: %i, %s", e.smtp_code, e.smtp_error)
|
||||
return
|
||||
except smtplib.SMTPServerDisconnected:
|
||||
Logger.error('Server disconnected while sending the mail.')
|
||||
Logger.error("Server disconnected while sending the mail.")
|
||||
return
|
||||
|
@ -26,6 +26,7 @@ import httplib2
|
||||
from ubuntutools.version import Version
|
||||
|
||||
import logging
|
||||
|
||||
Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -58,7 +59,7 @@ class BugTask(object):
|
||||
self.series = components[2].lower()
|
||||
|
||||
if self.package is None:
|
||||
title_re = r'^Sync ([a-z0-9+.-]+) [a-z0-9.+:~-]+ \([a-z]+\) from.*'
|
||||
title_re = r"^Sync ([a-z0-9+.-]+) [a-z0-9.+:~-]+ \([a-z]+\) from.*"
|
||||
match = re.match(title_re, self.get_bug_title(), re.U | re.I)
|
||||
if match is not None:
|
||||
self.package = match.group(1)
|
||||
@ -74,7 +75,7 @@ class BugTask(object):
|
||||
if url.endswith(".dsc"):
|
||||
response, data = httplib2.Http().request(url)
|
||||
assert response.status == 200
|
||||
with open(filename, 'wb') as f:
|
||||
with open(filename, "wb") as f:
|
||||
f.write(data)
|
||||
|
||||
dsc_file = os.path.join(os.getcwd(), filename)
|
||||
@ -84,18 +85,26 @@ class BugTask(object):
|
||||
return dsc_file
|
||||
|
||||
def get_branch_link(self):
|
||||
return "lp:" + self.project + "/" + self.get_series() + "/" + \
|
||||
self.package
|
||||
return "lp:" + self.project + "/" + self.get_series() + "/" + self.package
|
||||
|
||||
def get_bug_title(self):
|
||||
"""Returns the title of the related bug."""
|
||||
return self.bug_task.bug.title
|
||||
|
||||
def get_long_info(self):
|
||||
return "Bug task: " + str(self.bug_task) + "\n" + \
|
||||
"Package: " + str(self.package) + "\n" + \
|
||||
"Project: " + str(self.project) + "\n" + \
|
||||
"Series: " + str(self.series)
|
||||
return (
|
||||
"Bug task: "
|
||||
+ str(self.bug_task)
|
||||
+ "\n"
|
||||
+ "Package: "
|
||||
+ str(self.package)
|
||||
+ "\n"
|
||||
+ "Project: "
|
||||
+ str(self.project)
|
||||
+ "\n"
|
||||
+ "Series: "
|
||||
+ str(self.series)
|
||||
)
|
||||
|
||||
def get_lp_task(self):
|
||||
"""Returns the Launchpad bug task object."""
|
||||
@ -137,14 +146,16 @@ class BugTask(object):
|
||||
dist = self.launchpad.distributions[project]
|
||||
archive = dist.getArchive(name="primary")
|
||||
distro_series = dist.getSeries(name_or_version=series)
|
||||
published = archive.getPublishedSources(source_name=self.package,
|
||||
distro_series=distro_series,
|
||||
status="Published",
|
||||
exact_match=True)
|
||||
published = archive.getPublishedSources(
|
||||
source_name=self.package,
|
||||
distro_series=distro_series,
|
||||
status="Published",
|
||||
exact_match=True,
|
||||
)
|
||||
|
||||
latest_source = None
|
||||
for source in published:
|
||||
if source.pocket in ('Release', 'Security', 'Updates', 'Proposed'):
|
||||
if source.pocket in ("Release", "Security", "Updates", "Proposed"):
|
||||
latest_source = source
|
||||
break
|
||||
return latest_source
|
||||
@ -156,7 +167,7 @@ class BugTask(object):
|
||||
def get_latest_released_version(self):
|
||||
source = self.get_source(True)
|
||||
if source is None: # Not currently published in Ubuntu
|
||||
version = '~'
|
||||
version = "~"
|
||||
else:
|
||||
version = source.source_package_version
|
||||
return Version(version)
|
||||
|
@ -23,6 +23,7 @@ from ubuntutools.sponsor_patch.question import ask_for_manual_fixing
|
||||
from functools import reduce
|
||||
|
||||
import logging
|
||||
|
||||
Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -32,10 +33,10 @@ class Patch(object):
|
||||
def __init__(self, patch):
|
||||
self._patch = patch
|
||||
self._patch_file = re.sub(" |/", "_", patch.title)
|
||||
if not reduce(lambda r, x: r or self._patch.title.endswith(x),
|
||||
(".debdiff", ".diff", ".patch"), False):
|
||||
Logger.debug("Patch %s does not have a proper file extension." %
|
||||
(self._patch.title))
|
||||
if not reduce(
|
||||
lambda r, x: r or self._patch.title.endswith(x), (".debdiff", ".diff", ".patch"), False
|
||||
):
|
||||
Logger.debug("Patch %s does not have a proper file extension." % (self._patch.title))
|
||||
self._patch_file += ".patch"
|
||||
self._full_path = os.path.realpath(self._patch_file)
|
||||
self._changed_files = None
|
||||
@ -45,21 +46,36 @@ class Patch(object):
|
||||
assert self._changed_files is not None, "You forgot to download the patch."
|
||||
edit = False
|
||||
if self.is_debdiff():
|
||||
cmd = ["patch", "--merge", "--force", "-p",
|
||||
str(self.get_strip_level()), "-i", self._full_path]
|
||||
Logger.debug(' '.join(cmd))
|
||||
cmd = [
|
||||
"patch",
|
||||
"--merge",
|
||||
"--force",
|
||||
"-p",
|
||||
str(self.get_strip_level()),
|
||||
"-i",
|
||||
self._full_path,
|
||||
]
|
||||
Logger.debug(" ".join(cmd))
|
||||
if subprocess.call(cmd) != 0:
|
||||
Logger.error("Failed to apply debdiff %s to %s %s.",
|
||||
self._patch_file, task.package, task.get_version())
|
||||
Logger.error(
|
||||
"Failed to apply debdiff %s to %s %s.",
|
||||
self._patch_file,
|
||||
task.package,
|
||||
task.get_version(),
|
||||
)
|
||||
if not edit:
|
||||
ask_for_manual_fixing()
|
||||
edit = True
|
||||
else:
|
||||
cmd = ["add-patch", self._full_path]
|
||||
Logger.debug(' '.join(cmd))
|
||||
Logger.debug(" ".join(cmd))
|
||||
if subprocess.call(cmd) != 0:
|
||||
Logger.error("Failed to apply diff %s to %s %s.",
|
||||
self._patch_file, task.package, task.get_version())
|
||||
Logger.error(
|
||||
"Failed to apply diff %s to %s %s.",
|
||||
self._patch_file,
|
||||
task.package,
|
||||
task.get_version(),
|
||||
)
|
||||
if not edit:
|
||||
ask_for_manual_fixing()
|
||||
edit = True
|
||||
@ -73,7 +89,7 @@ class Patch(object):
|
||||
patch_f.close()
|
||||
|
||||
cmd = ["diffstat", "-l", "-p0", self._full_path]
|
||||
changed_files = subprocess.check_output(cmd, encoding='utf-8')
|
||||
changed_files = subprocess.check_output(cmd, encoding="utf-8")
|
||||
self._changed_files = [f for f in changed_files.split("\n") if f != ""]
|
||||
|
||||
def get_strip_level(self):
|
||||
@ -81,13 +97,11 @@ class Patch(object):
|
||||
assert self._changed_files is not None, "You forgot to download the patch."
|
||||
strip_level = None
|
||||
if self.is_debdiff():
|
||||
changelog = [f for f in self._changed_files
|
||||
if f.endswith("debian/changelog")][0]
|
||||
changelog = [f for f in self._changed_files if f.endswith("debian/changelog")][0]
|
||||
strip_level = len(changelog.split(os.sep)) - 2
|
||||
return strip_level
|
||||
|
||||
def is_debdiff(self):
|
||||
"""Checks if the patch is a debdiff (= modifies debian/changelog)."""
|
||||
assert self._changed_files is not None, "You forgot to download the patch."
|
||||
return len([f for f in self._changed_files
|
||||
if f.endswith("debian/changelog")]) > 0
|
||||
return len([f for f in self._changed_files if f.endswith("debian/changelog")]) > 0
|
||||
|
@ -37,8 +37,7 @@ def ask_for_ignoring_or_fixing():
|
||||
def ask_for_manual_fixing():
|
||||
"""Ask the user to resolve an issue manually."""
|
||||
|
||||
answer = YesNoQuestion().ask("Do you want to resolve this issue manually",
|
||||
"yes")
|
||||
answer = YesNoQuestion().ask("Do you want to resolve this issue manually", "yes")
|
||||
if answer == "no":
|
||||
user_abort()
|
||||
|
||||
|
@ -25,20 +25,24 @@ import debian.deb822
|
||||
|
||||
from ubuntutools.question import Question, YesNoQuestion
|
||||
|
||||
from ubuntutools.sponsor_patch.question import (ask_for_ignoring_or_fixing,
|
||||
ask_for_manual_fixing,
|
||||
user_abort)
|
||||
from ubuntutools.sponsor_patch.question import (
|
||||
ask_for_ignoring_or_fixing,
|
||||
ask_for_manual_fixing,
|
||||
user_abort,
|
||||
)
|
||||
|
||||
import logging
|
||||
|
||||
Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_series(launchpad):
|
||||
"""Returns a tuple with the development and list of supported series."""
|
||||
ubuntu = launchpad.distributions['ubuntu']
|
||||
ubuntu = launchpad.distributions["ubuntu"]
|
||||
devel_series = ubuntu.current_series.name
|
||||
supported_series = [series.name for series in ubuntu.series
|
||||
if series.active and series.name != devel_series]
|
||||
supported_series = [
|
||||
series.name for series in ubuntu.series if series.active and series.name != devel_series
|
||||
]
|
||||
return (devel_series, supported_series)
|
||||
|
||||
|
||||
@ -49,10 +53,10 @@ def strip_epoch(version):
|
||||
return "1.1.3-1".
|
||||
"""
|
||||
|
||||
parts = version.full_version.split(':')
|
||||
parts = version.full_version.split(":")
|
||||
if len(parts) > 1:
|
||||
del parts[0]
|
||||
version_without_epoch = ':'.join(parts)
|
||||
version_without_epoch = ":".join(parts)
|
||||
return version_without_epoch
|
||||
|
||||
|
||||
@ -74,8 +78,7 @@ class SourcePackage(object):
|
||||
if upload == "ubuntu":
|
||||
self._print_logs()
|
||||
question = Question(["yes", "edit", "no"])
|
||||
answer = question.ask("Do you want to acknowledge the sync request",
|
||||
"no")
|
||||
answer = question.ask("Do you want to acknowledge the sync request", "no")
|
||||
if answer == "edit":
|
||||
return False
|
||||
elif answer == "no":
|
||||
@ -90,33 +93,33 @@ class SourcePackage(object):
|
||||
|
||||
msg = "Sync request ACK'd."
|
||||
if self._build_log:
|
||||
msg = ("%s %s builds on %s. " + msg) % \
|
||||
(self._package, self._version,
|
||||
self._builder.get_architecture())
|
||||
msg = ("%s %s builds on %s. " + msg) % (
|
||||
self._package,
|
||||
self._version,
|
||||
self._builder.get_architecture(),
|
||||
)
|
||||
bug.newMessage(content=msg, subject="sponsor-patch")
|
||||
Logger.debug("Acknowledged sync request bug #%i.", bug.id)
|
||||
|
||||
bug.subscribe(person=launchpad.people['ubuntu-archive'])
|
||||
bug.subscribe(person=launchpad.people["ubuntu-archive"])
|
||||
Logger.debug("Subscribed ubuntu-archive to bug #%i.", bug.id)
|
||||
|
||||
bug.subscribe(person=launchpad.me)
|
||||
Logger.debug("Subscribed me to bug #%i.", bug.id)
|
||||
|
||||
sponsorsteam = launchpad.people['ubuntu-sponsors']
|
||||
sponsorsteam = launchpad.people["ubuntu-sponsors"]
|
||||
for sub in bug.subscriptions:
|
||||
if sub.person == sponsorsteam and sub.canBeUnsubscribedByUser():
|
||||
bug.unsubscribe(person=launchpad.people['ubuntu-sponsors'])
|
||||
Logger.debug("Unsubscribed ubuntu-sponsors from bug #%i.",
|
||||
bug.id)
|
||||
bug.unsubscribe(person=launchpad.people["ubuntu-sponsors"])
|
||||
Logger.debug("Unsubscribed ubuntu-sponsors from bug #%i.", bug.id)
|
||||
elif sub.person == sponsorsteam:
|
||||
Logger.debug("Couldn't unsubscribe ubuntu-sponsors from "
|
||||
"bug #%i.", bug.id)
|
||||
Logger.debug("Couldn't unsubscribe ubuntu-sponsors from bug #%i.", bug.id)
|
||||
|
||||
Logger.info("Successfully acknowledged sync request bug #%i.",
|
||||
bug.id)
|
||||
Logger.info("Successfully acknowledged sync request bug #%i.", bug.id)
|
||||
else:
|
||||
Logger.error("Sync requests can only be acknowledged when the "
|
||||
"upload target is Ubuntu.")
|
||||
Logger.error(
|
||||
"Sync requests can only be acknowledged when the upload target is Ubuntu."
|
||||
)
|
||||
sys.exit(1)
|
||||
return True
|
||||
|
||||
@ -141,28 +144,29 @@ class SourcePackage(object):
|
||||
elif answer == "no":
|
||||
user_abort()
|
||||
cmd = ["dput", "--force", upload, self._changes_file]
|
||||
Logger.debug(' '.join(cmd))
|
||||
Logger.debug(" ".join(cmd))
|
||||
if subprocess.call(cmd) != 0:
|
||||
Logger.error("Upload of %s to %s failed." %
|
||||
(os.path.basename(self._changes_file), upload))
|
||||
Logger.error(
|
||||
"Upload of %s to %s failed." % (os.path.basename(self._changes_file), upload)
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# Push the branch if the package is uploaded to the Ubuntu archive.
|
||||
if upload == "ubuntu" and self._branch:
|
||||
cmd = ['debcommit']
|
||||
Logger.debug(' '.join(cmd))
|
||||
cmd = ["debcommit"]
|
||||
Logger.debug(" ".join(cmd))
|
||||
if subprocess.call(cmd) != 0:
|
||||
Logger.error('Bzr commit failed.')
|
||||
Logger.error("Bzr commit failed.")
|
||||
sys.exit(1)
|
||||
cmd = ['bzr', 'mark-uploaded']
|
||||
Logger.debug(' '.join(cmd))
|
||||
cmd = ["bzr", "mark-uploaded"]
|
||||
Logger.debug(" ".join(cmd))
|
||||
if subprocess.call(cmd) != 0:
|
||||
Logger.error('Bzr tagging failed.')
|
||||
Logger.error("Bzr tagging failed.")
|
||||
sys.exit(1)
|
||||
cmd = ['bzr', 'push', ':parent']
|
||||
Logger.debug(' '.join(cmd))
|
||||
cmd = ["bzr", "push", ":parent"]
|
||||
Logger.debug(" ".join(cmd))
|
||||
if subprocess.call(cmd) != 0:
|
||||
Logger.error('Bzr push failed.')
|
||||
Logger.error("Bzr push failed.")
|
||||
sys.exit(1)
|
||||
return True
|
||||
|
||||
@ -175,8 +179,9 @@ class SourcePackage(object):
|
||||
|
||||
if dist is None:
|
||||
dist = re.sub("-.*$", "", self._changelog.distributions)
|
||||
build_name = "{}_{}_{}.build".format(self._package, strip_epoch(self._version),
|
||||
self._builder.get_architecture())
|
||||
build_name = "{}_{}_{}.build".format(
|
||||
self._package, strip_epoch(self._version), self._builder.get_architecture()
|
||||
)
|
||||
self._build_log = os.path.join(self._buildresult, build_name)
|
||||
|
||||
successful_built = False
|
||||
@ -191,8 +196,7 @@ class SourcePackage(object):
|
||||
update = False
|
||||
|
||||
# build package
|
||||
result = self._builder.build(self._dsc_file, dist,
|
||||
self._buildresult)
|
||||
result = self._builder.build(self._dsc_file, dist, self._buildresult)
|
||||
if result != 0:
|
||||
question = Question(["yes", "update", "retry", "no"])
|
||||
answer = question.ask("Do you want to resolve this issue manually", "yes")
|
||||
@ -224,13 +228,14 @@ class SourcePackage(object):
|
||||
"""
|
||||
|
||||
if self._branch:
|
||||
cmd = ['bzr', 'builddeb', '--builder=debuild', '-S',
|
||||
'--', '--no-lintian', '-nc']
|
||||
cmd = ["bzr", "builddeb", "--builder=debuild", "-S", "--", "--no-lintian", "-nc"]
|
||||
else:
|
||||
cmd = ['debuild', '--no-lintian', '-nc', '-S']
|
||||
cmd = ["debuild", "--no-lintian", "-nc", "-S"]
|
||||
cmd.append("-v" + previous_version.full_version)
|
||||
if previous_version.upstream_version == \
|
||||
self._changelog.upstream_version and upload == "ubuntu":
|
||||
if (
|
||||
previous_version.upstream_version == self._changelog.upstream_version
|
||||
and upload == "ubuntu"
|
||||
):
|
||||
# FIXME: Add proper check that catches cases like changed
|
||||
# compression (.tar.gz -> tar.bz2) and multiple orig source tarballs
|
||||
cmd.append("-sd")
|
||||
@ -239,9 +244,9 @@ class SourcePackage(object):
|
||||
if keyid is not None:
|
||||
cmd += ["-k" + keyid]
|
||||
env = os.environ
|
||||
if upload == 'ubuntu':
|
||||
env['DEB_VENDOR'] = 'Ubuntu'
|
||||
Logger.debug(' '.join(cmd))
|
||||
if upload == "ubuntu":
|
||||
env["DEB_VENDOR"] = "Ubuntu"
|
||||
Logger.debug(" ".join(cmd))
|
||||
if subprocess.call(cmd, env=env) != 0:
|
||||
Logger.error("Failed to build source tarball.")
|
||||
# TODO: Add a "retry" option
|
||||
@ -252,8 +257,9 @@ class SourcePackage(object):
|
||||
@property
|
||||
def _changes_file(self):
|
||||
"""Returns the file name of the .changes file."""
|
||||
return os.path.join(self._workdir, "{}_{}_source.changes"
|
||||
.format(self._package, strip_epoch(self._version)))
|
||||
return os.path.join(
|
||||
self._workdir, "{}_{}_source.changes".format(self._package, strip_epoch(self._version))
|
||||
)
|
||||
|
||||
def check_target(self, upload, launchpad):
|
||||
"""Make sure that the target is correct.
|
||||
@ -265,18 +271,22 @@ class SourcePackage(object):
|
||||
(devel_series, supported_series) = _get_series(launchpad)
|
||||
|
||||
if upload == "ubuntu":
|
||||
allowed = supported_series + \
|
||||
[s + "-proposed" for s in supported_series] + \
|
||||
[devel_series]
|
||||
allowed = (
|
||||
supported_series + [s + "-proposed" for s in supported_series] + [devel_series]
|
||||
)
|
||||
if self._changelog.distributions not in allowed:
|
||||
Logger.error("%s is not an allowed series. It needs to be one of %s." %
|
||||
(self._changelog.distributions, ", ".join(allowed)))
|
||||
Logger.error(
|
||||
"%s is not an allowed series. It needs to be one of %s."
|
||||
% (self._changelog.distributions, ", ".join(allowed))
|
||||
)
|
||||
return ask_for_ignoring_or_fixing()
|
||||
elif upload and upload.startswith("ppa/"):
|
||||
allowed = supported_series + [devel_series]
|
||||
if self._changelog.distributions not in allowed:
|
||||
Logger.error("%s is not an allowed series. It needs to be one of %s." %
|
||||
(self._changelog.distributions, ", ".join(allowed)))
|
||||
Logger.error(
|
||||
"%s is not an allowed series. It needs to be one of %s."
|
||||
% (self._changelog.distributions, ", ".join(allowed))
|
||||
)
|
||||
return ask_for_ignoring_or_fixing()
|
||||
return True
|
||||
|
||||
@ -288,14 +298,17 @@ class SourcePackage(object):
|
||||
"""
|
||||
|
||||
if self._version <= previous_version:
|
||||
Logger.error("The version %s is not greater than the already "
|
||||
"available %s.", self._version, previous_version)
|
||||
Logger.error(
|
||||
"The version %s is not greater than the already available %s.",
|
||||
self._version,
|
||||
previous_version,
|
||||
)
|
||||
return ask_for_ignoring_or_fixing()
|
||||
return True
|
||||
|
||||
def check_sync_request_version(self, bug_number, task):
|
||||
"""Check if the downloaded version of the package is mentioned in the
|
||||
bug title."""
|
||||
bug title."""
|
||||
|
||||
if not task.title_contains(self._version):
|
||||
print("Bug #%i title: %s" % (bug_number, task.get_bug_title()))
|
||||
@ -313,20 +326,20 @@ class SourcePackage(object):
|
||||
@property
|
||||
def _dsc_file(self):
|
||||
"""Returns the file name of the .dsc file."""
|
||||
return os.path.join(self._workdir, "{}_{}.dsc".format(self._package,
|
||||
strip_epoch(self._version)))
|
||||
return os.path.join(
|
||||
self._workdir, "{}_{}.dsc".format(self._package, strip_epoch(self._version))
|
||||
)
|
||||
|
||||
def generate_debdiff(self, dsc_file):
|
||||
"""Generates a debdiff between the given .dsc file and this source
|
||||
package."""
|
||||
package."""
|
||||
|
||||
assert os.path.isfile(dsc_file), "%s does not exist." % (dsc_file)
|
||||
assert os.path.isfile(self._dsc_file), "%s does not exist." % \
|
||||
(self._dsc_file)
|
||||
assert os.path.isfile(self._dsc_file), "%s does not exist." % (self._dsc_file)
|
||||
cmd = ["debdiff", dsc_file, self._dsc_file]
|
||||
if not Logger.isEnabledFor(logging.DEBUG):
|
||||
cmd.insert(1, "-q")
|
||||
Logger.debug(' '.join(cmd) + " > " + self._debdiff_filename)
|
||||
Logger.debug(" ".join(cmd) + " > " + self._debdiff_filename)
|
||||
with open(self._debdiff_filename, "w") as debdiff_file:
|
||||
debdiff = subprocess.run(cmd, check=False, stdout=debdiff_file)
|
||||
assert debdiff.returncode in (0, 1)
|
||||
@ -376,8 +389,7 @@ class SourcePackage(object):
|
||||
# Check the changelog
|
||||
self._changelog = debian.changelog.Changelog()
|
||||
try:
|
||||
self._changelog.parse_changelog(open("debian/changelog"),
|
||||
max_blocks=1, strict=True)
|
||||
self._changelog.parse_changelog(open("debian/changelog"), max_blocks=1, strict=True)
|
||||
except debian.changelog.ChangelogParseError as error:
|
||||
Logger.error("The changelog entry doesn't validate: %s", str(error))
|
||||
ask_for_manual_fixing()
|
||||
@ -387,8 +399,10 @@ class SourcePackage(object):
|
||||
try:
|
||||
self._version = self._changelog.get_version()
|
||||
except IndexError:
|
||||
Logger.error("Debian package version could not be determined. "
|
||||
"debian/changelog is probably malformed.")
|
||||
Logger.error(
|
||||
"Debian package version could not be determined. "
|
||||
"debian/changelog is probably malformed."
|
||||
)
|
||||
ask_for_manual_fixing()
|
||||
return False
|
||||
|
||||
@ -402,22 +416,26 @@ class SourcePackage(object):
|
||||
|
||||
# Determine whether to use the source or binary build for lintian
|
||||
if self._build_log:
|
||||
build_changes = self._package + "_" + strip_epoch(self._version) + \
|
||||
"_" + self._builder.get_architecture() + ".changes"
|
||||
build_changes = (
|
||||
self._package
|
||||
+ "_"
|
||||
+ strip_epoch(self._version)
|
||||
+ "_"
|
||||
+ self._builder.get_architecture()
|
||||
+ ".changes"
|
||||
)
|
||||
changes_for_lintian = os.path.join(self._buildresult, build_changes)
|
||||
else:
|
||||
changes_for_lintian = self._changes_file
|
||||
|
||||
# Check lintian
|
||||
assert os.path.isfile(changes_for_lintian), "%s does not exist." % \
|
||||
(changes_for_lintian)
|
||||
cmd = ["lintian", "-IE", "--pedantic", "-q", "--profile", "ubuntu",
|
||||
changes_for_lintian]
|
||||
lintian_filename = os.path.join(self._workdir,
|
||||
self._package + "_" +
|
||||
strip_epoch(self._version) + ".lintian")
|
||||
Logger.debug(' '.join(cmd) + " > " + lintian_filename)
|
||||
report = subprocess.check_output(cmd, encoding='utf-8')
|
||||
assert os.path.isfile(changes_for_lintian), "%s does not exist." % (changes_for_lintian)
|
||||
cmd = ["lintian", "-IE", "--pedantic", "-q", "--profile", "ubuntu", changes_for_lintian]
|
||||
lintian_filename = os.path.join(
|
||||
self._workdir, self._package + "_" + strip_epoch(self._version) + ".lintian"
|
||||
)
|
||||
Logger.debug(" ".join(cmd) + " > " + lintian_filename)
|
||||
report = subprocess.check_output(cmd, encoding="utf-8")
|
||||
|
||||
# write lintian report file
|
||||
lintian_file = open(lintian_filename, "w")
|
||||
@ -430,17 +448,25 @@ class SourcePackage(object):
|
||||
"""Does a sync of the source package."""
|
||||
|
||||
if upload == "ubuntu":
|
||||
cmd = ["syncpackage", self._package, "-b", str(bug_number), "-f",
|
||||
"-s", requester, "-V", str(self._version),
|
||||
"-d", series]
|
||||
Logger.debug(' '.join(cmd))
|
||||
cmd = [
|
||||
"syncpackage",
|
||||
self._package,
|
||||
"-b",
|
||||
str(bug_number),
|
||||
"-f",
|
||||
"-s",
|
||||
requester,
|
||||
"-V",
|
||||
str(self._version),
|
||||
"-d",
|
||||
series,
|
||||
]
|
||||
Logger.debug(" ".join(cmd))
|
||||
if subprocess.call(cmd) != 0:
|
||||
Logger.error("Syncing of %s %s failed.", self._package,
|
||||
str(self._version))
|
||||
Logger.error("Syncing of %s %s failed.", self._package, str(self._version))
|
||||
sys.exit(1)
|
||||
else:
|
||||
# FIXME: Support this use case!
|
||||
Logger.error("Uploading a synced package other than to Ubuntu "
|
||||
"is not supported yet!")
|
||||
Logger.error("Uploading a synced package other than to Ubuntu is not supported yet!")
|
||||
sys.exit(1)
|
||||
return True
|
||||
|
@ -25,8 +25,7 @@ from distro_info import UbuntuDistroInfo
|
||||
|
||||
from launchpadlib.launchpad import Launchpad
|
||||
|
||||
from ubuntutools.update_maintainer import (update_maintainer,
|
||||
MaintainerUpdateException)
|
||||
from ubuntutools.update_maintainer import update_maintainer, MaintainerUpdateException
|
||||
from ubuntutools.question import input_number
|
||||
|
||||
from ubuntutools.sponsor_patch.bugtask import BugTask, is_sync
|
||||
@ -35,34 +34,35 @@ from ubuntutools.sponsor_patch.question import ask_for_manual_fixing
|
||||
from ubuntutools.sponsor_patch.source_package import SourcePackage
|
||||
|
||||
import logging
|
||||
|
||||
Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def is_command_available(command, check_sbin=False):
|
||||
"Is command in $PATH?"
|
||||
path = os.environ.get('PATH', '/usr/bin:/bin').split(':')
|
||||
path = os.environ.get("PATH", "/usr/bin:/bin").split(":")
|
||||
if check_sbin:
|
||||
path += [directory[:-3] + 'sbin'
|
||||
for directory in path if directory.endswith('/bin')]
|
||||
return any(os.access(os.path.join(directory, command), os.X_OK)
|
||||
for directory in path)
|
||||
path += [directory[:-3] + "sbin" for directory in path if directory.endswith("/bin")]
|
||||
return any(os.access(os.path.join(directory, command), os.X_OK) for directory in path)
|
||||
|
||||
|
||||
def check_dependencies():
|
||||
"Do we have all the commands we need for full functionality?"
|
||||
missing = []
|
||||
for cmd in ('patch', 'bzr', 'quilt', 'dput', 'lintian'):
|
||||
for cmd in ("patch", "bzr", "quilt", "dput", "lintian"):
|
||||
if not is_command_available(cmd):
|
||||
missing.append(cmd)
|
||||
if not is_command_available('bzr-buildpackage'):
|
||||
missing.append('bzr-builddeb')
|
||||
if not any(is_command_available(cmd, check_sbin=True)
|
||||
for cmd in ('pbuilder', 'sbuild', 'cowbuilder')):
|
||||
missing.append('pbuilder/cowbuilder/sbuild')
|
||||
if not is_command_available("bzr-buildpackage"):
|
||||
missing.append("bzr-builddeb")
|
||||
if not any(
|
||||
is_command_available(cmd, check_sbin=True) for cmd in ("pbuilder", "sbuild", "cowbuilder")
|
||||
):
|
||||
missing.append("pbuilder/cowbuilder/sbuild")
|
||||
|
||||
if missing:
|
||||
Logger.warning("sponsor-patch requires %s to be installed for full "
|
||||
"functionality", ', '.join(missing))
|
||||
Logger.warning(
|
||||
"sponsor-patch requires %s to be installed for full functionality", ", ".join(missing)
|
||||
)
|
||||
|
||||
|
||||
def get_source_package_name(bug_task):
|
||||
@ -84,12 +84,16 @@ def get_user_shell():
|
||||
def edit_source():
|
||||
# Spawn shell to allow modifications
|
||||
cmd = [get_user_shell()]
|
||||
Logger.debug(' '.join(cmd))
|
||||
print("""An interactive shell was launched in
|
||||
Logger.debug(" ".join(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
|
||||
process, exit the shell such that it returns an exit code other than zero.
|
||||
""" % (os.getcwd()), end=' ')
|
||||
"""
|
||||
% (os.getcwd()),
|
||||
end=" ",
|
||||
)
|
||||
returncode = subprocess.call(cmd)
|
||||
if returncode != 0:
|
||||
Logger.error("Shell exited with exit value %i." % (returncode))
|
||||
@ -100,11 +104,15 @@ def ask_for_patch_or_branch(bug, attached_patches, linked_branches):
|
||||
patch = None
|
||||
branch = None
|
||||
if len(attached_patches) == 0:
|
||||
msg = "https://launchpad.net/bugs/%i has %i branches linked:" % \
|
||||
(bug.id, len(linked_branches))
|
||||
msg = "https://launchpad.net/bugs/%i has %i branches linked:" % (
|
||||
bug.id,
|
||||
len(linked_branches),
|
||||
)
|
||||
elif len(linked_branches) == 0:
|
||||
msg = "https://launchpad.net/bugs/%i has %i patches attached:" % \
|
||||
(bug.id, len(attached_patches))
|
||||
msg = "https://launchpad.net/bugs/%i has %i patches attached:" % (
|
||||
bug.id,
|
||||
len(attached_patches),
|
||||
)
|
||||
else:
|
||||
branches = "%i branch" % len(linked_branches)
|
||||
if len(linked_branches) > 1:
|
||||
@ -112,8 +120,11 @@ def ask_for_patch_or_branch(bug, attached_patches, linked_branches):
|
||||
patches = "%i patch" % len(attached_patches)
|
||||
if len(attached_patches) > 1:
|
||||
patches += "es"
|
||||
msg = "https://launchpad.net/bugs/%i has %s linked and %s attached:" % \
|
||||
(bug.id, branches, patches)
|
||||
msg = "https://launchpad.net/bugs/%i has %s linked and %s attached:" % (
|
||||
bug.id,
|
||||
branches,
|
||||
patches,
|
||||
)
|
||||
Logger.info(msg)
|
||||
i = 0
|
||||
for linked_branch in linked_branches:
|
||||
@ -122,8 +133,7 @@ def ask_for_patch_or_branch(bug, attached_patches, linked_branches):
|
||||
for attached_patch in attached_patches:
|
||||
i += 1
|
||||
print("%i) %s" % (i, attached_patch.title))
|
||||
selected = input_number("Which branch or patch do you want to download",
|
||||
1, i, i)
|
||||
selected = input_number("Which branch or patch do you want to download", 1, i, i)
|
||||
if selected <= len(linked_branches):
|
||||
branch = linked_branches[selected - 1].bzr_identity
|
||||
else:
|
||||
@ -139,21 +149,26 @@ def get_patch_or_branch(bug):
|
||||
linked_branches = [b.branch for b in bug.linked_branches]
|
||||
if len(attached_patches) == 0 and len(linked_branches) == 0:
|
||||
if len(bug.attachments) == 0:
|
||||
Logger.error("No attachment and no linked branch found on "
|
||||
"bug #%i. Add the tag sync to the bug if it is "
|
||||
"a sync request.", bug.id)
|
||||
Logger.error(
|
||||
"No attachment and no linked branch found on "
|
||||
"bug #%i. Add the tag sync to the bug if it is "
|
||||
"a sync request.",
|
||||
bug.id,
|
||||
)
|
||||
else:
|
||||
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)
|
||||
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)
|
||||
elif len(attached_patches) == 1 and len(linked_branches) == 0:
|
||||
patch = Patch(attached_patches[0])
|
||||
elif len(attached_patches) == 0 and len(linked_branches) == 1:
|
||||
branch = linked_branches[0].bzr_identity
|
||||
else:
|
||||
patch, branch = ask_for_patch_or_branch(bug, attached_patches,
|
||||
linked_branches)
|
||||
patch, branch = ask_for_patch_or_branch(bug, attached_patches, linked_branches)
|
||||
return (patch, branch)
|
||||
|
||||
|
||||
@ -162,7 +177,7 @@ def download_branch(branch):
|
||||
if os.path.isdir(dir_name):
|
||||
shutil.rmtree(dir_name)
|
||||
cmd = ["bzr", "branch", branch]
|
||||
Logger.debug(' '.join(cmd))
|
||||
Logger.debug(" ".join(cmd))
|
||||
if subprocess.call(cmd) != 0:
|
||||
Logger.error("Failed to download branch %s." % (branch))
|
||||
sys.exit(1)
|
||||
@ -172,7 +187,7 @@ def download_branch(branch):
|
||||
def merge_branch(branch):
|
||||
edit = False
|
||||
cmd = ["bzr", "merge", branch]
|
||||
Logger.debug(' '.join(cmd))
|
||||
Logger.debug(" ".join(cmd))
|
||||
if subprocess.call(cmd) != 0:
|
||||
Logger.error("Failed to merge branch %s." % (branch))
|
||||
ask_for_manual_fixing()
|
||||
@ -184,7 +199,7 @@ def extract_source(dsc_file, verbose=False):
|
||||
cmd = ["dpkg-source", "--skip-patches", "-x", dsc_file]
|
||||
if not verbose:
|
||||
cmd.insert(1, "-q")
|
||||
Logger.debug(' '.join(cmd))
|
||||
Logger.debug(" ".join(cmd))
|
||||
if subprocess.call(cmd) != 0:
|
||||
Logger.error("Extraction of %s failed." % (os.path.basename(dsc_file)))
|
||||
sys.exit(1)
|
||||
@ -201,7 +216,7 @@ def get_open_ubuntu_bug_task(launchpad, bug, branch=None):
|
||||
ubuntu_tasks = [x for x in bug_tasks if x.is_ubuntu_task()]
|
||||
bug_id = bug.id
|
||||
if branch:
|
||||
branch = branch.split('/')
|
||||
branch = branch.split("/")
|
||||
# Non-production LP?
|
||||
if len(branch) > 5:
|
||||
branch = branch[3:]
|
||||
@ -211,9 +226,8 @@ def get_open_ubuntu_bug_task(launchpad, bug, branch=None):
|
||||
sys.exit(1)
|
||||
elif len(ubuntu_tasks) == 1:
|
||||
task = ubuntu_tasks[0]
|
||||
if len(ubuntu_tasks) > 1 and branch and branch[1] == 'ubuntu':
|
||||
tasks = [t for t in ubuntu_tasks if
|
||||
t.get_series() == branch[2] and t.package == branch[3]]
|
||||
if len(ubuntu_tasks) > 1 and branch and branch[1] == "ubuntu":
|
||||
tasks = [t for t in ubuntu_tasks if t.get_series() == branch[2] and t.package == branch[3]]
|
||||
if len(tasks) > 1:
|
||||
# A bug targeted to the development series?
|
||||
tasks = [t for t in tasks if t.series is not None]
|
||||
@ -221,19 +235,24 @@ def get_open_ubuntu_bug_task(launchpad, bug, branch=None):
|
||||
task = tasks[0]
|
||||
elif len(ubuntu_tasks) > 1:
|
||||
task_list = [t.get_short_info() for t in ubuntu_tasks]
|
||||
Logger.debug("%i Ubuntu tasks exist for bug #%i.\n%s", len(ubuntu_tasks),
|
||||
bug_id, "\n".join(task_list))
|
||||
Logger.debug(
|
||||
"%i Ubuntu tasks exist for bug #%i.\n%s",
|
||||
len(ubuntu_tasks),
|
||||
bug_id,
|
||||
"\n".join(task_list),
|
||||
)
|
||||
open_ubuntu_tasks = [x for x in ubuntu_tasks if not x.is_complete()]
|
||||
if len(open_ubuntu_tasks) == 1:
|
||||
task = open_ubuntu_tasks[0]
|
||||
else:
|
||||
Logger.info("https://launchpad.net/bugs/%i has %i Ubuntu tasks:" %
|
||||
(bug_id, len(ubuntu_tasks)))
|
||||
Logger.info(
|
||||
"https://launchpad.net/bugs/%i has %i Ubuntu tasks:" % (bug_id, len(ubuntu_tasks))
|
||||
)
|
||||
for i in range(len(ubuntu_tasks)):
|
||||
print("%i) %s" % (i + 1,
|
||||
ubuntu_tasks[i].get_package_and_series()))
|
||||
selected = input_number("To which Ubuntu task does the patch belong",
|
||||
1, len(ubuntu_tasks))
|
||||
print("%i) %s" % (i + 1, ubuntu_tasks[i].get_package_and_series()))
|
||||
selected = input_number(
|
||||
"To which Ubuntu task does the patch belong", 1, len(ubuntu_tasks)
|
||||
)
|
||||
task = ubuntu_tasks[selected - 1]
|
||||
Logger.debug("Selected Ubuntu task: %s" % (task.get_short_info()))
|
||||
return task
|
||||
@ -246,8 +265,10 @@ def _create_and_change_into(workdir):
|
||||
try:
|
||||
os.makedirs(workdir)
|
||||
except os.error as error:
|
||||
Logger.error("Failed to create the working directory %s [Errno %i]: %s." %
|
||||
(workdir, error.errno, error.strerror))
|
||||
Logger.error(
|
||||
"Failed to create the working directory %s [Errno %i]: %s."
|
||||
% (workdir, error.errno, error.strerror)
|
||||
)
|
||||
sys.exit(1)
|
||||
if workdir != os.getcwd():
|
||||
Logger.debug("cd " + workdir)
|
||||
@ -267,7 +288,7 @@ def _update_maintainer_field():
|
||||
def _update_timestamp():
|
||||
"""Run dch to update the timestamp of debian/changelog."""
|
||||
cmd = ["dch", "--maintmaint", "--release", ""]
|
||||
Logger.debug(' '.join(cmd))
|
||||
Logger.debug(" ".join(cmd))
|
||||
if subprocess.call(cmd) != 0:
|
||||
Logger.debug("Failed to update timestamp in debian/changelog.")
|
||||
|
||||
@ -294,13 +315,12 @@ def _download_and_change_into(task, dsc_file, patch, branch):
|
||||
extract_source(dsc_file, Logger.isEnabledFor(logging.DEBUG))
|
||||
|
||||
# change directory
|
||||
directory = task.package + '-' + task.get_version().upstream_version
|
||||
directory = task.package + "-" + task.get_version().upstream_version
|
||||
Logger.debug("cd " + directory)
|
||||
os.chdir(directory)
|
||||
|
||||
|
||||
def sponsor_patch(bug_number, build, builder, edit, keyid, lpinstance, update,
|
||||
upload, workdir):
|
||||
def sponsor_patch(bug_number, build, builder, edit, keyid, lpinstance, update, upload, workdir):
|
||||
workdir = os.path.realpath(os.path.expanduser(workdir))
|
||||
_create_and_change_into(workdir)
|
||||
|
||||
@ -331,8 +351,7 @@ def sponsor_patch(bug_number, build, builder, edit, keyid, lpinstance, update,
|
||||
update = False
|
||||
else:
|
||||
# We are going to run lintian, so we need a source package
|
||||
successful = source_package.build_source(None, upload,
|
||||
previous_version)
|
||||
successful = source_package.build_source(None, upload, previous_version)
|
||||
|
||||
if successful:
|
||||
series = task.get_debian_source_series()
|
||||
@ -363,8 +382,7 @@ def sponsor_patch(bug_number, build, builder, edit, keyid, lpinstance, update,
|
||||
|
||||
_update_timestamp()
|
||||
|
||||
if not source_package.build_source(keyid, upload,
|
||||
task.get_previous_version()):
|
||||
if not source_package.build_source(keyid, upload, task.get_previous_version()):
|
||||
continue
|
||||
|
||||
source_package.generate_debdiff(dsc_file)
|
||||
|
@ -23,38 +23,38 @@ from ubuntutools.version import Version
|
||||
|
||||
|
||||
class ExamplePackage(object):
|
||||
def __init__(self, source='example', version='1.0-1', destdir='test-data'):
|
||||
def __init__(self, source="example", version="1.0-1", destdir="test-data"):
|
||||
self.source = source
|
||||
self.version = Version(version)
|
||||
self.destdir = Path(destdir)
|
||||
|
||||
self.env = dict(os.environ)
|
||||
self.env['DEBFULLNAME'] = 'Example'
|
||||
self.env['DEBEMAIL'] = 'example@example.net'
|
||||
self.env["DEBFULLNAME"] = "Example"
|
||||
self.env["DEBEMAIL"] = "example@example.net"
|
||||
|
||||
@property
|
||||
def orig(self):
|
||||
return self.destdir / f'{self.source}_{self.version.upstream_version}.orig.tar.xz'
|
||||
return self.destdir / f"{self.source}_{self.version.upstream_version}.orig.tar.xz"
|
||||
|
||||
@property
|
||||
def debian(self):
|
||||
return self.destdir / f'{self.source}_{self.version}.debian.tar.xz'
|
||||
return self.destdir / f"{self.source}_{self.version}.debian.tar.xz"
|
||||
|
||||
@property
|
||||
def dsc(self):
|
||||
return self.destdir / f'{self.source}_{self.version}.dsc'
|
||||
return self.destdir / f"{self.source}_{self.version}.dsc"
|
||||
|
||||
@property
|
||||
def dirname(self):
|
||||
return f'{self.source}-{self.version.upstream_version}'
|
||||
return f"{self.source}-{self.version.upstream_version}"
|
||||
|
||||
@property
|
||||
def content_filename(self):
|
||||
return 'content'
|
||||
return "content"
|
||||
|
||||
@property
|
||||
def content_text(self):
|
||||
return 'my content'
|
||||
return "my content"
|
||||
|
||||
def create(self):
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
@ -66,14 +66,24 @@ class ExamplePackage(object):
|
||||
(pkgdir / self.content_filename).write_text(self.content_text)
|
||||
|
||||
# run dh_make to create orig tarball
|
||||
subprocess.run('dh_make -sy --createorig'.split(),
|
||||
check=True, env=self.env, cwd=str(pkgdir),
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
subprocess.run(
|
||||
"dh_make -sy --createorig".split(),
|
||||
check=True,
|
||||
env=self.env,
|
||||
cwd=str(pkgdir),
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
|
||||
# run dpkg-source -b to create debian tar and dsc
|
||||
subprocess.run(f'dpkg-source -b {self.dirname}'.split(),
|
||||
check=True, env=self.env, cwd=str(d),
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
subprocess.run(
|
||||
f"dpkg-source -b {self.dirname}".split(),
|
||||
check=True,
|
||||
env=self.env,
|
||||
cwd=str(d),
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
|
||||
# move tarballs and dsc to destdir
|
||||
self.destdir.mkdir(parents=True, exist_ok=True)
|
||||
|
@ -41,7 +41,7 @@ class DscVerificationTestCase(BaseVerificationTestCase):
|
||||
self.assertTrue(self.dsc.verify_file(self.pkg.debian))
|
||||
|
||||
def test_missing(self):
|
||||
self.assertFalse(self.dsc.verify_file(self.pkg.destdir / 'does.not.exist'))
|
||||
self.assertFalse(self.dsc.verify_file(self.pkg.destdir / "does.not.exist"))
|
||||
|
||||
def test_bad(self):
|
||||
data = self.pkg.orig.read_bytes()
|
||||
@ -51,13 +51,13 @@ class DscVerificationTestCase(BaseVerificationTestCase):
|
||||
self.assertFalse(self.dsc.verify_file(self.pkg.orig))
|
||||
|
||||
def test_sha1(self):
|
||||
del self.dsc['Checksums-Sha256']
|
||||
del self.dsc["Checksums-Sha256"]
|
||||
self.test_good()
|
||||
self.test_bad()
|
||||
|
||||
def test_md5(self):
|
||||
del self.dsc['Checksums-Sha256']
|
||||
del self.dsc['Checksums-Sha1']
|
||||
del self.dsc["Checksums-Sha256"]
|
||||
del self.dsc["Checksums-Sha1"]
|
||||
self.test_good()
|
||||
self.test_bad()
|
||||
|
||||
@ -72,7 +72,7 @@ class LocalSourcePackageTestCase(BaseVerificationTestCase):
|
||||
self.workdir = Path(d.name)
|
||||
|
||||
def pull(self, **kwargs):
|
||||
''' Do the pull from pkg dir to the workdir, return the SourcePackage '''
|
||||
"""Do the pull from pkg dir to the workdir, return the SourcePackage"""
|
||||
srcpkg = self.SourcePackage(dscfile=self.pkg.dsc, workdir=self.workdir, **kwargs)
|
||||
srcpkg.pull()
|
||||
return srcpkg
|
||||
@ -85,11 +85,11 @@ class LocalSourcePackageTestCase(BaseVerificationTestCase):
|
||||
return srcpkg
|
||||
|
||||
def test_unpack(self, **kwargs):
|
||||
srcpkg = kwargs.get('srcpkg', self.pull(**kwargs))
|
||||
srcpkg = kwargs.get("srcpkg", self.pull(**kwargs))
|
||||
srcpkg.unpack()
|
||||
content = self.workdir / self.pkg.dirname / self.pkg.content_filename
|
||||
self.assertEqual(self.pkg.content_text, content.read_text())
|
||||
debian = self.workdir / self.pkg.dirname / 'debian'
|
||||
debian = self.workdir / self.pkg.dirname / "debian"
|
||||
self.assertTrue(debian.exists())
|
||||
self.assertTrue(debian.is_dir())
|
||||
|
||||
@ -103,12 +103,12 @@ class LocalSourcePackageTestCase(BaseVerificationTestCase):
|
||||
self.test_pull_and_unpack(package=self.pkg.source, version=self.pkg.version)
|
||||
|
||||
def test_with_package_version_component(self):
|
||||
self.test_pull_and_unpack(package=self.pkg.source,
|
||||
version=self.pkg.version,
|
||||
componet='main')
|
||||
self.test_pull_and_unpack(
|
||||
package=self.pkg.source, version=self.pkg.version, componet="main"
|
||||
)
|
||||
|
||||
def test_verification(self):
|
||||
corruption = b'CORRUPTION'
|
||||
corruption = b"CORRUPTION"
|
||||
|
||||
self.pull()
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
import locale
|
||||
import os
|
||||
# import sys
|
||||
import unittest
|
||||
|
||||
from io import StringIO
|
||||
@ -27,17 +26,14 @@ from ubuntutools.config import UDTConfig, ubu_email
|
||||
|
||||
|
||||
class ConfigTestCase(unittest.TestCase):
|
||||
_config_files = {
|
||||
'system': '',
|
||||
'user': '',
|
||||
}
|
||||
_config_files = {"system": "", "user": ""}
|
||||
|
||||
def _fake_open(self, filename, mode='r'):
|
||||
if mode != 'r':
|
||||
def _fake_open(self, filename, mode="r"):
|
||||
if mode != "r":
|
||||
raise IOError("Read only fake-file")
|
||||
files = {
|
||||
'/etc/devscripts.conf': self._config_files['system'],
|
||||
os.path.expanduser('~/.devscripts'): self._config_files['user'],
|
||||
"/etc/devscripts.conf": self._config_files["system"],
|
||||
os.path.expanduser("~/.devscripts"): self._config_files["user"],
|
||||
}
|
||||
if filename not in files:
|
||||
raise IOError("No such file or directory: '%s'" % filename)
|
||||
@ -47,7 +43,7 @@ class ConfigTestCase(unittest.TestCase):
|
||||
super(ConfigTestCase, self).setUp()
|
||||
m = mock.mock_open()
|
||||
m.side_effect = self._fake_open
|
||||
patcher = mock.patch('builtins.open', m)
|
||||
patcher = mock.patch("builtins.open", m)
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher.start()
|
||||
|
||||
@ -65,14 +61,16 @@ class ConfigTestCase(unittest.TestCase):
|
||||
self.clean_environment()
|
||||
|
||||
def clean_environment(self):
|
||||
self._config_files['system'] = ''
|
||||
self._config_files['user'] = ''
|
||||
self._config_files["system"] = ""
|
||||
self._config_files["user"] = ""
|
||||
for k in list(os.environ.keys()):
|
||||
if k.startswith(('UBUNTUTOOLS_', 'TEST_')):
|
||||
if k.startswith(("UBUNTUTOOLS_", "TEST_")):
|
||||
del os.environ[k]
|
||||
|
||||
def test_config_parsing(self):
|
||||
self._config_files['user'] = """#COMMENT=yes
|
||||
self._config_files[
|
||||
"user"
|
||||
] = """#COMMENT=yes
|
||||
\tTAB_INDENTED=yes
|
||||
SPACE_INDENTED=yes
|
||||
SPACE_SUFFIX=yes
|
||||
@ -85,19 +83,22 @@ INHERIT=user
|
||||
REPEAT=no
|
||||
REPEAT=yes
|
||||
"""
|
||||
self._config_files['system'] = 'INHERIT=system'
|
||||
self.assertEqual(UDTConfig(prefix='TEST').config, {
|
||||
'TAB_INDENTED': 'yes',
|
||||
'SPACE_INDENTED': 'yes',
|
||||
'SPACE_SUFFIX': 'yes',
|
||||
'SINGLE_QUOTE': 'yes no',
|
||||
'DOUBLE_QUOTE': 'yes no',
|
||||
'QUOTED_QUOTE': "it's",
|
||||
'PAIR_QUOTES': 'yes a no',
|
||||
'COMMAND_EXECUTION': 'a',
|
||||
'INHERIT': 'user',
|
||||
'REPEAT': 'yes',
|
||||
})
|
||||
self._config_files["system"] = "INHERIT=system"
|
||||
self.assertEqual(
|
||||
UDTConfig(prefix="TEST").config,
|
||||
{
|
||||
"TAB_INDENTED": "yes",
|
||||
"SPACE_INDENTED": "yes",
|
||||
"SPACE_SUFFIX": "yes",
|
||||
"SINGLE_QUOTE": "yes no",
|
||||
"DOUBLE_QUOTE": "yes no",
|
||||
"QUOTED_QUOTE": "it's",
|
||||
"PAIR_QUOTES": "yes a no",
|
||||
"COMMAND_EXECUTION": "a",
|
||||
"INHERIT": "user",
|
||||
"REPEAT": "yes",
|
||||
},
|
||||
)
|
||||
# errs = Logger.stderr.getvalue().strip()
|
||||
# Logger.stderr = StringIO()
|
||||
# self.assertEqual(len(errs.splitlines()), 1)
|
||||
@ -105,39 +106,40 @@ REPEAT=yes
|
||||
# r'Warning: Cannot parse.*\bCOMMAND_EXECUTION=a')
|
||||
|
||||
def get_value(self, *args, **kwargs):
|
||||
config = UDTConfig(prefix='TEST')
|
||||
config = UDTConfig(prefix="TEST")
|
||||
return config.get_value(*args, **kwargs)
|
||||
|
||||
def test_defaults(self):
|
||||
self.assertEqual(self.get_value('BUILDER'), 'pbuilder')
|
||||
self.assertEqual(self.get_value("BUILDER"), "pbuilder")
|
||||
|
||||
def test_provided_default(self):
|
||||
self.assertEqual(self.get_value('BUILDER', default='foo'), 'foo')
|
||||
self.assertEqual(self.get_value("BUILDER", default="foo"), "foo")
|
||||
|
||||
def test_scriptname_precedence(self):
|
||||
self._config_files['user'] = """TEST_BUILDER=foo
|
||||
self._config_files[
|
||||
"user"
|
||||
] = """TEST_BUILDER=foo
|
||||
UBUNTUTOOLS_BUILDER=bar"""
|
||||
self.assertEqual(self.get_value('BUILDER'), 'foo')
|
||||
self.assertEqual(self.get_value("BUILDER"), "foo")
|
||||
|
||||
def test_configfile_precedence(self):
|
||||
self._config_files['system'] = "UBUNTUTOOLS_BUILDER=foo"
|
||||
self._config_files['user'] = "UBUNTUTOOLS_BUILDER=bar"
|
||||
self.assertEqual(self.get_value('BUILDER'), 'bar')
|
||||
self._config_files["system"] = "UBUNTUTOOLS_BUILDER=foo"
|
||||
self._config_files["user"] = "UBUNTUTOOLS_BUILDER=bar"
|
||||
self.assertEqual(self.get_value("BUILDER"), "bar")
|
||||
|
||||
def test_environment_precedence(self):
|
||||
self._config_files['user'] = "UBUNTUTOOLS_BUILDER=bar"
|
||||
os.environ['UBUNTUTOOLS_BUILDER'] = 'baz'
|
||||
self.assertEqual(self.get_value('BUILDER'), 'baz')
|
||||
self._config_files["user"] = "UBUNTUTOOLS_BUILDER=bar"
|
||||
os.environ["UBUNTUTOOLS_BUILDER"] = "baz"
|
||||
self.assertEqual(self.get_value("BUILDER"), "baz")
|
||||
|
||||
def test_general_environment_specific_config_precedence(self):
|
||||
self._config_files['user'] = "TEST_BUILDER=bar"
|
||||
os.environ['UBUNTUTOOLS_BUILDER'] = 'foo'
|
||||
self.assertEqual(self.get_value('BUILDER'), 'bar')
|
||||
self._config_files["user"] = "TEST_BUILDER=bar"
|
||||
os.environ["UBUNTUTOOLS_BUILDER"] = "foo"
|
||||
self.assertEqual(self.get_value("BUILDER"), "bar")
|
||||
|
||||
def test_compat_keys(self):
|
||||
self._config_files['user'] = 'COMPATFOOBAR=bar'
|
||||
self.assertEqual(self.get_value('QUX', compat_keys=['COMPATFOOBAR']),
|
||||
'bar')
|
||||
self._config_files["user"] = "COMPATFOOBAR=bar"
|
||||
self.assertEqual(self.get_value("QUX", compat_keys=["COMPATFOOBAR"]), "bar")
|
||||
# errs = Logger.stderr.getvalue().strip()
|
||||
# Logger.stderr = StringIO()
|
||||
# self.assertEqual(len(errs.splitlines()), 1)
|
||||
@ -145,16 +147,16 @@ REPEAT=yes
|
||||
# r'deprecated.*\bCOMPATFOOBAR\b.*\bTEST_QUX\b')
|
||||
|
||||
def test_boolean(self):
|
||||
self._config_files['user'] = "TEST_BOOLEAN=yes"
|
||||
self.assertEqual(self.get_value('BOOLEAN', boolean=True), True)
|
||||
self._config_files['user'] = "TEST_BOOLEAN=no"
|
||||
self.assertEqual(self.get_value('BOOLEAN', boolean=True), False)
|
||||
self._config_files['user'] = "TEST_BOOLEAN=true"
|
||||
self.assertEqual(self.get_value('BOOLEAN', boolean=True), None)
|
||||
self._config_files["user"] = "TEST_BOOLEAN=yes"
|
||||
self.assertEqual(self.get_value("BOOLEAN", boolean=True), True)
|
||||
self._config_files["user"] = "TEST_BOOLEAN=no"
|
||||
self.assertEqual(self.get_value("BOOLEAN", boolean=True), False)
|
||||
self._config_files["user"] = "TEST_BOOLEAN=true"
|
||||
self.assertEqual(self.get_value("BOOLEAN", boolean=True), None)
|
||||
|
||||
def test_nonpackagewide(self):
|
||||
self._config_files['user'] = 'UBUNTUTOOLS_FOOBAR=a'
|
||||
self.assertEqual(self.get_value('FOOBAR'), None)
|
||||
self._config_files["user"] = "UBUNTUTOOLS_FOOBAR=a"
|
||||
self.assertEqual(self.get_value("FOOBAR"), None)
|
||||
|
||||
|
||||
class UbuEmailTestCase(unittest.TestCase):
|
||||
@ -165,71 +167,72 @@ class UbuEmailTestCase(unittest.TestCase):
|
||||
self.clean_environment()
|
||||
|
||||
def clean_environment(self):
|
||||
for k in ('UBUMAIL', 'DEBEMAIL', 'DEBFULLNAME'):
|
||||
for k in ("UBUMAIL", "DEBEMAIL", "DEBFULLNAME"):
|
||||
if k in os.environ:
|
||||
del os.environ[k]
|
||||
|
||||
def test_pristine(self):
|
||||
os.environ['DEBFULLNAME'] = name = 'Joe Developer'
|
||||
os.environ['DEBEMAIL'] = email = 'joe@example.net'
|
||||
os.environ["DEBFULLNAME"] = name = "Joe Developer"
|
||||
os.environ["DEBEMAIL"] = email = "joe@example.net"
|
||||
self.assertEqual(ubu_email(), (name, email))
|
||||
|
||||
def test_two_hat(self):
|
||||
os.environ['DEBFULLNAME'] = name = 'Joe Developer'
|
||||
os.environ['DEBEMAIL'] = 'joe@debian.org'
|
||||
os.environ['UBUMAIL'] = email = 'joe@ubuntu.com'
|
||||
os.environ["DEBFULLNAME"] = name = "Joe Developer"
|
||||
os.environ["DEBEMAIL"] = "joe@debian.org"
|
||||
os.environ["UBUMAIL"] = email = "joe@ubuntu.com"
|
||||
self.assertEqual(ubu_email(), (name, email))
|
||||
self.assertEqual(os.environ['DEBFULLNAME'], name)
|
||||
self.assertEqual(os.environ['DEBEMAIL'], email)
|
||||
self.assertEqual(os.environ["DEBFULLNAME"], name)
|
||||
self.assertEqual(os.environ["DEBEMAIL"], email)
|
||||
|
||||
def test_two_hat_cmdlineoverride(self):
|
||||
os.environ['DEBFULLNAME'] = 'Joe Developer'
|
||||
os.environ['DEBEMAIL'] = 'joe@debian.org'
|
||||
os.environ['UBUMAIL'] = 'joe@ubuntu.com'
|
||||
name = 'Foo Bar'
|
||||
email = 'joe@example.net'
|
||||
os.environ["DEBFULLNAME"] = "Joe Developer"
|
||||
os.environ["DEBEMAIL"] = "joe@debian.org"
|
||||
os.environ["UBUMAIL"] = "joe@ubuntu.com"
|
||||
name = "Foo Bar"
|
||||
email = "joe@example.net"
|
||||
self.assertEqual(ubu_email(name, email), (name, email))
|
||||
self.assertEqual(os.environ['DEBFULLNAME'], name)
|
||||
self.assertEqual(os.environ['DEBEMAIL'], email)
|
||||
self.assertEqual(os.environ["DEBFULLNAME"], name)
|
||||
self.assertEqual(os.environ["DEBEMAIL"], email)
|
||||
|
||||
def test_two_hat_noexport(self):
|
||||
os.environ['DEBFULLNAME'] = name = 'Joe Developer'
|
||||
os.environ['DEBEMAIL'] = demail = 'joe@debian.org'
|
||||
os.environ['UBUMAIL'] = uemail = 'joe@ubuntu.com'
|
||||
os.environ["DEBFULLNAME"] = name = "Joe Developer"
|
||||
os.environ["DEBEMAIL"] = demail = "joe@debian.org"
|
||||
os.environ["UBUMAIL"] = uemail = "joe@ubuntu.com"
|
||||
self.assertEqual(ubu_email(export=False), (name, uemail))
|
||||
self.assertEqual(os.environ['DEBFULLNAME'], name)
|
||||
self.assertEqual(os.environ['DEBEMAIL'], demail)
|
||||
self.assertEqual(os.environ["DEBFULLNAME"], name)
|
||||
self.assertEqual(os.environ["DEBEMAIL"], demail)
|
||||
|
||||
def test_two_hat_with_name(self):
|
||||
os.environ['DEBFULLNAME'] = 'Joe Developer'
|
||||
os.environ['DEBEMAIL'] = 'joe@debian.org'
|
||||
name = 'Joe Ubuntunista'
|
||||
email = 'joe@ubuntu.com'
|
||||
os.environ['UBUMAIL'] = '%s <%s>' % (name, email)
|
||||
os.environ["DEBFULLNAME"] = "Joe Developer"
|
||||
os.environ["DEBEMAIL"] = "joe@debian.org"
|
||||
name = "Joe Ubuntunista"
|
||||
email = "joe@ubuntu.com"
|
||||
os.environ["UBUMAIL"] = "%s <%s>" % (name, email)
|
||||
self.assertEqual(ubu_email(), (name, email))
|
||||
self.assertEqual(os.environ['DEBFULLNAME'], name)
|
||||
self.assertEqual(os.environ['DEBEMAIL'], email)
|
||||
self.assertEqual(os.environ["DEBFULLNAME"], name)
|
||||
self.assertEqual(os.environ["DEBEMAIL"], email)
|
||||
|
||||
def test_debemail_with_name(self):
|
||||
name = 'Joe Developer'
|
||||
email = 'joe@example.net'
|
||||
os.environ['DEBEMAIL'] = orig = '%s <%s>' % (name, email)
|
||||
name = "Joe Developer"
|
||||
email = "joe@example.net"
|
||||
os.environ["DEBEMAIL"] = orig = "%s <%s>" % (name, email)
|
||||
self.assertEqual(ubu_email(), (name, email))
|
||||
self.assertEqual(os.environ['DEBEMAIL'], orig)
|
||||
self.assertEqual(os.environ["DEBEMAIL"], orig)
|
||||
|
||||
def test_unicode_name(self):
|
||||
encoding = locale.getdefaultlocale()[1]
|
||||
if not encoding:
|
||||
encoding = 'utf-8'
|
||||
name = 'Jöe Déveloper'
|
||||
encoding = "utf-8"
|
||||
name = "Jöe Déveloper"
|
||||
env_name = name
|
||||
if isinstance(name, bytes):
|
||||
name = 'Jöe Déveloper'.decode('utf-8')
|
||||
name = "Jöe Déveloper".decode("utf-8")
|
||||
env_name = name.encode(encoding)
|
||||
try:
|
||||
os.environ['DEBFULLNAME'] = env_name
|
||||
os.environ["DEBFULLNAME"] = env_name
|
||||
except UnicodeEncodeError:
|
||||
raise unittest.SkipTest("python interpreter is not running in an "
|
||||
"unicode capable locale")
|
||||
os.environ['DEBEMAIL'] = email = 'joe@example.net'
|
||||
raise unittest.SkipTest(
|
||||
"python interpreter is not running in an unicode capable locale"
|
||||
)
|
||||
os.environ["DEBEMAIL"] = email = "joe@example.net"
|
||||
self.assertEqual(ubu_email(), (name, email))
|
||||
|
@ -27,10 +27,12 @@ class HelpTestCase(unittest.TestCase):
|
||||
def test_script(self):
|
||||
for script in scripts:
|
||||
with self.subTest(script=script):
|
||||
result = subprocess.run([f'./{script}', '--help'],
|
||||
encoding='UTF-8',
|
||||
timeout=10,
|
||||
check=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
result = subprocess.run(
|
||||
[f"./{script}", "--help"],
|
||||
encoding="UTF-8",
|
||||
timeout=10,
|
||||
check=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
self.assertFalse(result.stderr.strip())
|
||||
|
@ -17,7 +17,6 @@
|
||||
"""Test suite for ubuntutools.update_maintainer"""
|
||||
|
||||
import os
|
||||
# import sys
|
||||
import unittest
|
||||
|
||||
from io import StringIO
|
||||
@ -200,24 +199,23 @@ class UpdateMaintainerTestCase(unittest.TestCase):
|
||||
"""TestCase object for ubuntutools.update_maintainer"""
|
||||
|
||||
_directory = "/"
|
||||
_files = {
|
||||
"changelog": None,
|
||||
"control": None,
|
||||
"control.in": None,
|
||||
"rules": None,
|
||||
}
|
||||
_files = {"changelog": None, "control": None, "control.in": None, "rules": None}
|
||||
|
||||
def _fake_isfile(self, filename):
|
||||
"""Check only for existing fake files."""
|
||||
directory, base = os.path.split(filename)
|
||||
return (directory == self._directory and base in self._files and
|
||||
self._files[base] is not None)
|
||||
return (
|
||||
directory == self._directory and base in self._files and self._files[base] is not None
|
||||
)
|
||||
|
||||
def _fake_open(self, filename, mode='r'):
|
||||
def _fake_open(self, filename, mode="r"):
|
||||
"""Provide StringIO objects instead of real files."""
|
||||
directory, base = os.path.split(filename)
|
||||
if (directory != self._directory or base not in self._files or
|
||||
(mode == "r" and self._files[base] is None)):
|
||||
if (
|
||||
directory != self._directory
|
||||
or base not in self._files
|
||||
or (mode == "r" and self._files[base] is None)
|
||||
):
|
||||
raise IOError("No such file or directory: '%s'" % filename)
|
||||
if mode == "w":
|
||||
self._files[base] = StringIO()
|
||||
@ -228,11 +226,11 @@ class UpdateMaintainerTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
m = mock.mock_open()
|
||||
m.side_effect = self._fake_open
|
||||
patcher = mock.patch('builtins.open', m)
|
||||
patcher = mock.patch("builtins.open", m)
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher.start()
|
||||
m = mock.MagicMock(side_effect=self._fake_isfile)
|
||||
patcher = mock.patch('os.path.isfile', m)
|
||||
patcher = mock.patch("os.path.isfile", m)
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher.start()
|
||||
self._files["rules"] = StringIO(_SIMPLE_RULES)
|
||||
@ -260,8 +258,8 @@ class UpdateMaintainerTestCase(unittest.TestCase):
|
||||
def test_original_ubuntu_maintainer(self):
|
||||
"""Test: Original maintainer is Ubuntu developer.
|
||||
|
||||
The Maintainer field needs to be update even if
|
||||
XSBC-Original-Maintainer has an @ubuntu.com address."""
|
||||
The Maintainer field needs to be update even if
|
||||
XSBC-Original-Maintainer has an @ubuntu.com address."""
|
||||
self._files["changelog"] = StringIO(_LUCID_CHANGELOG)
|
||||
self._files["control"] = StringIO(_AXIS2C_CONTROL)
|
||||
update_maintainer(self._directory)
|
||||
@ -288,12 +286,11 @@ class UpdateMaintainerTestCase(unittest.TestCase):
|
||||
|
||||
def test_comments_in_control(self):
|
||||
"""Test: Update Maintainer field in a control file containing
|
||||
comments."""
|
||||
comments."""
|
||||
self._files["changelog"] = StringIO(_LUCID_CHANGELOG)
|
||||
self._files["control"] = StringIO(_SEAHORSE_PLUGINS_CONTROL)
|
||||
update_maintainer(self._directory)
|
||||
self.assertEqual(self._files["control"].getvalue(),
|
||||
_SEAHORSE_PLUGINS_UPDATED)
|
||||
self.assertEqual(self._files["control"].getvalue(), _SEAHORSE_PLUGINS_UPDATED)
|
||||
|
||||
def test_skip_smart_rules(self):
|
||||
"""Test: Skip update when XSBC-Original in debian/rules."""
|
||||
|
@ -22,6 +22,7 @@ import re
|
||||
import debian.changelog
|
||||
|
||||
import logging
|
||||
|
||||
Logger = logging.getLogger(__name__)
|
||||
|
||||
# Prior May 2009 these Maintainers were used:
|
||||
@ -47,16 +48,16 @@ class Control(object):
|
||||
|
||||
def get_maintainer(self):
|
||||
"""Returns the value of the Maintainer field."""
|
||||
maintainer = re.search("^Maintainer: ?(.*)$", self._content,
|
||||
re.MULTILINE)
|
||||
maintainer = re.search("^Maintainer: ?(.*)$", self._content, re.MULTILINE)
|
||||
if maintainer:
|
||||
maintainer = maintainer.group(1)
|
||||
return maintainer
|
||||
|
||||
def get_original_maintainer(self):
|
||||
"""Returns the value of the XSBC-Original-Maintainer field."""
|
||||
orig_maintainer = re.search("^(?:[XSBC]*-)?Original-Maintainer: ?(.*)$",
|
||||
self._content, re.MULTILINE)
|
||||
orig_maintainer = re.search(
|
||||
"^(?:[XSBC]*-)?Original-Maintainer: ?(.*)$", self._content, re.MULTILINE
|
||||
)
|
||||
if orig_maintainer:
|
||||
orig_maintainer = orig_maintainer.group(1)
|
||||
return orig_maintainer
|
||||
@ -78,25 +79,23 @@ class Control(object):
|
||||
"""Sets the value of the XSBC-Original-Maintainer field."""
|
||||
original_maintainer = "XSBC-Original-Maintainer: " + original_maintainer
|
||||
if self.get_original_maintainer():
|
||||
pattern = re.compile("^(?:[XSBC]*-)?Original-Maintainer:.*$",
|
||||
re.MULTILINE)
|
||||
pattern = re.compile("^(?:[XSBC]*-)?Original-Maintainer:.*$", re.MULTILINE)
|
||||
self._content = pattern.sub(original_maintainer, self._content)
|
||||
else:
|
||||
pattern = re.compile("^(Maintainer:.*)$", re.MULTILINE)
|
||||
self._content = pattern.sub(r"\1\n" + original_maintainer,
|
||||
self._content)
|
||||
self._content = pattern.sub(r"\1\n" + original_maintainer, self._content)
|
||||
|
||||
def remove_original_maintainer(self):
|
||||
"""Strip out out the XSBC-Original-Maintainer line"""
|
||||
pattern = re.compile("^(?:[XSBC]*-)?Original-Maintainer:.*?$.*?^",
|
||||
re.MULTILINE | re.DOTALL)
|
||||
self._content = pattern.sub('', self._content)
|
||||
pattern = re.compile(
|
||||
"^(?:[XSBC]*-)?Original-Maintainer:.*?$.*?^", re.MULTILINE | re.DOTALL
|
||||
)
|
||||
self._content = pattern.sub("", self._content)
|
||||
|
||||
|
||||
def _get_distribution(changelog_file):
|
||||
"""get distribution of latest changelog entry"""
|
||||
changelog = debian.changelog.Changelog(open(changelog_file), strict=False,
|
||||
max_blocks=1)
|
||||
changelog = debian.changelog.Changelog(open(changelog_file), strict=False, max_blocks=1)
|
||||
distribution = changelog.distributions.split()[0]
|
||||
# Strip things like "-proposed-updates" or "-security" from distribution
|
||||
return distribution.split("-", 1)[0]
|
||||
@ -107,25 +106,21 @@ def _find_files(debian_directory, verbose):
|
||||
Returns (changelog, control files list)
|
||||
Raises an exception if none can be found.
|
||||
"""
|
||||
possible_contol_files = [os.path.join(debian_directory, f) for
|
||||
f in ["control.in", "control"]]
|
||||
possible_contol_files = [os.path.join(debian_directory, f) for f in ["control.in", "control"]]
|
||||
|
||||
changelog_file = os.path.join(debian_directory, "changelog")
|
||||
control_files = [f for f in possible_contol_files if os.path.isfile(f)]
|
||||
|
||||
# Make sure that a changelog and control file is available
|
||||
if len(control_files) == 0:
|
||||
raise MaintainerUpdateException(
|
||||
"No control file found in %s." % debian_directory)
|
||||
raise MaintainerUpdateException("No control file found in %s." % debian_directory)
|
||||
if not os.path.isfile(changelog_file):
|
||||
raise MaintainerUpdateException(
|
||||
"No changelog file found in %s." % debian_directory)
|
||||
raise MaintainerUpdateException("No changelog file found in %s." % debian_directory)
|
||||
|
||||
# If the rules file accounts for XSBC-Original-Maintainer, we should not
|
||||
# touch it in this package (e.g. the python package).
|
||||
rules_file = os.path.join(debian_directory, "rules")
|
||||
if os.path.isfile(rules_file) and \
|
||||
'XSBC-Original-' in open(rules_file).read():
|
||||
if os.path.isfile(rules_file) and "XSBC-Original-" in open(rules_file).read():
|
||||
if verbose:
|
||||
print("XSBC-Original is managed by 'rules' file. Doing nothing.")
|
||||
control_files = []
|
||||
@ -178,8 +173,9 @@ def update_maintainer(debian_directory, verbose=False):
|
||||
return
|
||||
|
||||
if control.get_original_maintainer() is not None:
|
||||
Logger.warning("Overwriting original maintainer: %s",
|
||||
control.get_original_maintainer())
|
||||
Logger.warning(
|
||||
"Overwriting original maintainer: %s", control.get_original_maintainer()
|
||||
)
|
||||
|
||||
if verbose:
|
||||
print("The original maintainer is: %s" % original_maintainer)
|
||||
|
@ -17,28 +17,28 @@ import debian.debian_support
|
||||
|
||||
class Version(debian.debian_support.Version):
|
||||
def strip_epoch(self):
|
||||
'''Removes the epoch from a Debian version string.
|
||||
"""Removes the epoch from a Debian version string.
|
||||
|
||||
strip_epoch(1:1.52-1) will return "1.52-1" and strip_epoch(1.1.3-1)
|
||||
will return "1.1.3-1".
|
||||
'''
|
||||
parts = self.full_version.split(':')
|
||||
"""
|
||||
parts = self.full_version.split(":")
|
||||
if len(parts) > 1:
|
||||
del parts[0]
|
||||
version_without_epoch = ':'.join(parts)
|
||||
version_without_epoch = ":".join(parts)
|
||||
return version_without_epoch
|
||||
|
||||
def get_related_debian_version(self):
|
||||
'''Strip the ubuntu-specific bits off the version'''
|
||||
"""Strip the ubuntu-specific bits off the version"""
|
||||
related_debian_version = self.full_version
|
||||
uidx = related_debian_version.find('ubuntu')
|
||||
uidx = related_debian_version.find("ubuntu")
|
||||
if uidx > 0:
|
||||
related_debian_version = related_debian_version[:uidx]
|
||||
uidx = related_debian_version.find('build')
|
||||
uidx = related_debian_version.find("build")
|
||||
if uidx > 0:
|
||||
related_debian_version = related_debian_version[:uidx]
|
||||
return Version(related_debian_version)
|
||||
|
||||
def is_modified_in_ubuntu(self):
|
||||
'''Did Ubuntu modify this (and mark the version appropriately)?'''
|
||||
return 'ubuntu' in self.full_version
|
||||
"""Did Ubuntu modify this (and mark the version appropriately)?"""
|
||||
return "ubuntu" in self.full_version
|
||||
|
@ -18,9 +18,11 @@ import optparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
from ubuntutools.update_maintainer import (update_maintainer,
|
||||
restore_maintainer,
|
||||
MaintainerUpdateException)
|
||||
from ubuntutools.update_maintainer import (
|
||||
update_maintainer,
|
||||
restore_maintainer,
|
||||
MaintainerUpdateException,
|
||||
)
|
||||
|
||||
|
||||
def find_debian_dir(depth=6):
|
||||
@ -30,10 +32,11 @@ def find_debian_dir(depth=6):
|
||||
:rtype: str
|
||||
:returns: a path to an existing debian/ directory, or None
|
||||
"""
|
||||
for path in ['../'*n or './' for n in list(range(0, depth+1))]:
|
||||
debian_path = '{}debian'.format(path)
|
||||
if os.path.exists(os.path.join(debian_path, 'control')) \
|
||||
and os.path.exists(os.path.join(debian_path, 'changelog')):
|
||||
for path in ["../" * n or "./" for n in list(range(0, depth + 1))]:
|
||||
debian_path = "{}debian".format(path)
|
||||
if os.path.exists(os.path.join(debian_path, "control")) and os.path.exists(
|
||||
os.path.join(debian_path, "changelog")
|
||||
):
|
||||
return debian_path
|
||||
return None
|
||||
|
||||
@ -43,20 +46,37 @@ def main():
|
||||
usage = "%s [options]" % (script_name)
|
||||
epilog = "See %s(1) for more info." % (script_name)
|
||||
parser = optparse.OptionParser(usage=usage, epilog=epilog)
|
||||
parser.add_option("-d", "--debian-directory", dest="debian_directory",
|
||||
help="location of the 'debian' directory (default: "
|
||||
"%default).", metavar="PATH",
|
||||
default=find_debian_dir() or './debian')
|
||||
parser.add_option("-r", "--restore",
|
||||
help="Restore the original maintainer",
|
||||
action='store_true', default=False)
|
||||
parser.add_option("-q", "--quiet", help="print no informational messages",
|
||||
dest="quiet", action="store_true", default=False)
|
||||
parser.add_option(
|
||||
"-d",
|
||||
"--debian-directory",
|
||||
dest="debian_directory",
|
||||
help="location of the 'debian' directory (default: %default).",
|
||||
metavar="PATH",
|
||||
default=find_debian_dir() or "./debian",
|
||||
)
|
||||
parser.add_option(
|
||||
"-r",
|
||||
"--restore",
|
||||
help="Restore the original maintainer",
|
||||
action="store_true",
|
||||
default=False,
|
||||
)
|
||||
parser.add_option(
|
||||
"-q",
|
||||
"--quiet",
|
||||
help="print no informational messages",
|
||||
dest="quiet",
|
||||
action="store_true",
|
||||
default=False,
|
||||
)
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if len(args) != 0:
|
||||
print("%s: Error: Unsupported additional parameters specified: %s"
|
||||
% (script_name, ", ".join(args)), file=sys.stderr)
|
||||
print(
|
||||
"%s: Error: Unsupported additional parameters specified: %s"
|
||||
% (script_name, ", ".join(args)),
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
if not options.restore:
|
||||
|
Loading…
x
Reference in New Issue
Block a user