[Collab-qa-commits] r2410 - multi-arch

Jakub Wilk jwilk at alioth.debian.org
Mon Jul 9 11:23:30 UTC 2012


Author: jwilk
Date: 2012-07-09 11:23:30 +0000 (Mon, 09 Jul 2012)
New Revision: 2410

Added:
   multi-arch/report-multiarch-bug
Log:
New tool to help filing multi-arch bugs.


Added: multi-arch/report-multiarch-bug
===================================================================
--- multi-arch/report-multiarch-bug	                        (rev 0)
+++ multi-arch/report-multiarch-bug	2012-07-09 11:23:30 UTC (rev 2410)
@@ -0,0 +1,189 @@
+#!/usr/bin/python3
+
+# Copyright © 2012 Jakub Wilk <jwilk at debian.org>
+
+# Redistribution and use in source and compiled forms, with or without
+# modification, are permitted under any circumstances. No warranty.
+
+import argparse
+import os
+import pwd
+import re
+import shutil
+import socket
+import subprocess as ipc
+import sys
+import tempfile
+
+import apt
+import apt_pkg
+
+import jinja2
+
+template = jinja2.Template('''\
+From: {{name}} <{{email}}>
+To: submit at bugs.debian.org
+Subject: {{package}}: arch-dependent {{plural("file", "files", files|length)}} in "Multi-Arch: same" package
+X-Debbugs-No-Ack: please
+X-Debbugs-CC: {{email}}
+
+Package: {{package}}
+Version: {{version}}
+Severity: important
+User: multiarch-devel at lists.alioth.debian.org
+Usertags: multiarch
+
+{{package}} is marked as "Multi-Arch: same", but the following {{plural("file is", "files are", files|length)}} architecture-dependent:
+{% for file in files %}
+{{file}}\
+{% endfor %}
+
+An example diff between {{architectures[0]}} and {{architectures[1]}} {% if ungzipped %}(after ungzipping) {% endif %}is attached.
+
+''')
+
+extract_filenames_from_archdiff = re.compile(b'^diff -ur [^/]+([^ ]+)', re.MULTILINE).findall
+extract_binary_filenames_from_archdiff = re.compile(b'^Binary files [^/]+([^ ]+)', re.MULTILINE).findall
+subst_archdiff_junk = re.compile(b'^Only in .*\n', re.MULTILINE).sub
+
+subst_architecture = re.compile('(?<=_)[a-z0-9-]+(?=[.]deb$)').sub
+
+def strip_deb_suffix(s, regex=re.compile('[.]deb$')):
+    return regex.sub('', s)
+
+def get_full_name():
+    return (
+        os.getenv('DEBFULLNAME') or
+        pwd.getpwuid(os.getuid()).pw_gecos.split(',')[0]
+    )
+
+def get_email():
+    return (
+        os.getenv('DEBEMAIL') or
+        '{login}@{domain}'.format(login=pwd.getpwduid(os.getuid()).pw_name, domain=socket.getfqdn())
+    )
+
+def plural(x, y, n):
+    return x if n == 1 else y
+
+class UserError(Exception):
+    pass
+
+class VersionError(UserError):
+    pass
+
+class NotMultiArchSame(UserError):
+    pass
+
+def main(options):
+    cache = apt.Cache()
+    packages = cache[options.package].versions
+    try:
+        [package] = [
+            pkg for pkg in packages
+            if options.version is None or options.version == pkg.version
+        ]
+    except ValueError:
+        raise VersionError('select a version: {0}'.format(
+            ', '.join(pkg.version for pkg in packages))
+        )
+    [uri] = package.uris
+    uris = [
+        subst_architecture(arch, uri)
+        for arch in options.architectures
+    ]
+    tmpdir = tempfile.mkdtemp(prefix='report-multiarch-bug.')
+    progress = apt.progress.text.AcquireProgress()
+    fetcher = apt_pkg.Acquire(progress)
+    files = {}
+    for uri in uris:
+        filename = os.path.basename(uri)
+        files[filename] = apt_pkg.AcquireFile(
+            fetcher,
+            uri=uri,
+            descr=filename,
+            destfile=os.path.join(tmpdir, filename),
+        )
+    rc = fetcher.run()
+    if rc != fetcher.RESULT_CONTINUE:
+        raise RuntimeError('fetching files failed')
+    os.chdir(tmpdir)
+    for filename, file in files.items():
+        if file.status != file.STAT_DONE:
+            raise RuntimeError('fetching file failed ({0})'.format(filename))
+        commandline = ['dpkg-deb', '-I', filename, 'control']
+        control = ipc.check_output(commandline)
+        if b'\nMulti-Arch: same\n' not in control:
+            raise NotMultiArchSame('{file} is not "Multi-Arch: same"'.format(file=filename))
+        commandline = ['dpkg-deb', '-x',
+            filename,
+            strip_deb_suffix(filename)
+        ]
+        ipc.check_call(commandline)
+    diff_commandline = ['diff', '-ur'] + [strip_deb_suffix(f) for f in files.keys()]
+    child = ipc.Popen(diff_commandline, stdout=ipc.PIPE)
+    (archdiff, stderr) = child.communicate()
+    child = None
+    archdiff = subst_archdiff_junk(b'', archdiff)
+    ungzipped = [
+        s.decode('ASCII')
+        for s in extract_binary_filenames_from_archdiff(archdiff)
+        if s.endswith(b'.gz')
+    ]
+    ungzipped = dict((f[:-3], f) for f in ungzipped)
+    if ungzipped:
+        ungzip_commandline = ['find', '-type', 'f', '-name', '*.gz', '-exec', 'gzip', '-d', '{}', '+']
+        ipc.check_call(ungzip_commandline)
+        child = ipc.Popen(diff_commandline, stdout=ipc.PIPE)
+        (archdiff, stderr) = child.communicate()
+        child = None
+        archdiff = subst_archdiff_junk(b'', archdiff)
+    archdiff_affected = [
+        s.decode('ASCII')
+        for s in extract_filenames_from_archdiff(archdiff)
+    ]
+    archdiff_affected += [
+        s.decode('ASCII')
+        for s in extract_binary_filenames_from_archdiff(archdiff)
+    ]
+    archdiff_affected = [ungzipped.get(file, file) for file in archdiff_affected]
+    archdiff_filename = '{0}.archdiff'.format(os.path.commonprefix(files.keys())[:-1])
+    with open(archdiff_filename, 'wb') as file:
+        file.write(archdiff)
+        del archdiff
+    mail = template.render(
+        name=get_full_name(),
+        email=get_email(),
+        package=options.package,
+        version=package.version,
+        architectures=options.architectures,
+        plural=plural,
+        files=archdiff_affected,
+        ungzipped=ungzipped,
+    )
+    if options.mutt:
+        with open('template', 'wt') as file:
+            file.write(mail)
+        commandline = ['mutt', '-H', 'template', '-a', archdiff_filename]
+        ipc.check_call(commandline)
+    else:
+        print(mail)
+    os.chdir('/')
+    shutil.rmtree(tmpdir)
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser()
+    parser.add_argument('-m', '--mutt', action='store_true')
+    parser.add_argument('-a', '--architectures', metavar='<arch>', nargs=2, default=['i386', 'amd64'])
+    parser.add_argument('package', metavar='<package>')
+    parser.add_argument('version', metavar='<version>', nargs='?')
+    options = parser.parse_args()
+    try:
+        main(options)
+    except VersionError as exc:
+        parser.error(exc)
+    except UserError as exc:
+        print('Error: {exc}'.format(exc=exc), file=sys.stderr)
+        sys.exit(1)
+
+# vim:ts=4 sw=4 et


Property changes on: multi-arch/report-multiarch-bug
___________________________________________________________________
Added: svn:executable
   + *




More information about the Collab-qa-commits mailing list