#! /usr/bin/python from __future__ import print_function import atexit from contextlib import closing import gzip from optparse import OptionParser import shutil import sys import tempfile try: from urllib.request import urlretrieve except ImportError: from urllib import urlretrieve import apt_pkg from launchpadlib.launchpad import Launchpad import lputils tempdir = None def ensure_tempdir(): global tempdir if not tempdir: tempdir = tempfile.mkdtemp(prefix="orphaned-sources") atexit.register(shutil.rmtree, tempdir) def decompress_open(tagfile): if tagfile.startswith("http:") or tagfile.startswith("ftp:"): url = tagfile tagfile = urlretrieve(url)[0] if tagfile.endswith(".gz"): ensure_tempdir() decompressed = tempfile.mktemp(dir=tempdir) with closing(gzip.GzipFile(tagfile)) as fin: with open(decompressed, "wb") as fout: fout.write(fin.read()) return open(decompressed, "r") else: return open(tagfile, "r") def archive_base(archtag): if archtag in ("amd64", "i386", "src"): return "http://archive.ubuntu.com/ubuntu" else: return "http://ports.ubuntu.com/ubuntu-ports" def source_names(options): sources = set() for component in "main", "restricted", "universe", "multiverse": url = "%s/dists/%s/%s/source/Sources.gz" % ( archive_base("src"), options.suite, component) print("Reading %s ..." % url, file=sys.stderr) for section in apt_pkg.TagFile(decompress_open(url)): sources.add(section["Package"]) return sources def referenced_sources(options): sources = set() for component in "main", "restricted", "universe", "multiverse": for arch in options.architectures: archtag = arch.architecture_tag for suffix in "", "/debian-installer": url = "%s/dists/%s/%s%s/binary-%s/Packages.gz" % ( archive_base(archtag), options.suite, component, suffix, archtag) print("Reading %s ..." % url, file=sys.stderr) for section in apt_pkg.TagFile(decompress_open(url)): if "Source" in section: sources.add(section["Source"].split(" ", 1)[0]) else: sources.add(section["Package"]) return sources def main(): parser = OptionParser( description="Check for sources without any remaining binaries.") parser.add_option( "-l", "--launchpad", dest="launchpad_instance", default="production") parser.add_option("-s", "--suite", help="check this suite") options, _ = parser.parse_args() options.distribution = "ubuntu" options.launchpad = Launchpad.login_anonymously( "orphaned-sources", options.launchpad_instance) lputils.setup_location(options) if options.pocket != "Release": parser.error("cannot run on non-release pocket") orphaned_sources = source_names(options) - referenced_sources(options) for source in sorted(orphaned_sources): print(source) if __name__ == '__main__': main()