|
|
|
#! /usr/bin/python2.7
|
|
|
|
|
|
|
|
from __future__ import print_function
|
|
|
|
|
|
|
|
import atexit
|
|
|
|
import bz2
|
|
|
|
from collections import defaultdict
|
|
|
|
import json
|
|
|
|
import lzma
|
|
|
|
from optparse import OptionParser
|
|
|
|
import requests
|
|
|
|
import shutil
|
|
|
|
import sys
|
|
|
|
import tempfile
|
|
|
|
|
|
|
|
import apt_pkg
|
|
|
|
from launchpadlib.launchpad import Launchpad
|
|
|
|
|
|
|
|
import lputils
|
|
|
|
|
|
|
|
|
|
|
|
tempdir = None
|
|
|
|
|
|
|
|
|
|
|
|
def ensure_tempdir():
|
|
|
|
global tempdir
|
|
|
|
if not tempdir:
|
|
|
|
tempdir = tempfile.mkdtemp(prefix="unsubscribed-packages")
|
|
|
|
atexit.register(shutil.rmtree, tempdir)
|
|
|
|
|
|
|
|
|
|
|
|
def decompress_open(tagfile):
|
|
|
|
if tagfile.startswith("http:") or tagfile.startswith("ftp:"):
|
|
|
|
url = tagfile
|
|
|
|
tagfile = requests.get(url)
|
|
|
|
if tagfile.status_code == 404:
|
|
|
|
url = url.replace(".xz", ".bz2")
|
|
|
|
tagfile = requests.get(url)
|
|
|
|
|
|
|
|
ensure_tempdir()
|
|
|
|
decompressed = tempfile.mktemp(dir=tempdir)
|
|
|
|
with open(decompressed, "wb") as fout:
|
|
|
|
if url.endswith(".xz"):
|
|
|
|
fout.write(lzma.decompress(tagfile.content))
|
|
|
|
elif url.endswith(".bz2"):
|
|
|
|
fout.write(bz2.decompress(tagfile.content))
|
|
|
|
return open(decompressed, "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 = dict()
|
|
|
|
for suite in options.suites:
|
|
|
|
for component in ["main", "restricted"]:
|
|
|
|
url = "%s/dists/%s/%s/source/Sources.xz" % (
|
|
|
|
archive_base("src"), suite, component)
|
|
|
|
if not options.quiet:
|
|
|
|
print("Reading %s ..." % url, file=sys.stderr)
|
|
|
|
for section in apt_pkg.TagFile(decompress_open(url)):
|
|
|
|
pkg = section["Package"]
|
|
|
|
if suite == options.dev_suite:
|
|
|
|
sources[pkg] = True
|
|
|
|
else:
|
|
|
|
if sources.get(pkg, False) == True:
|
|
|
|
continue
|
|
|
|
sources[pkg] = False
|
|
|
|
return sources
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
parser = OptionParser(
|
|
|
|
description="Check for source packages in main or restricted in "
|
|
|
|
"active distro series and return a json file of the teams "
|
|
|
|
"to which they map.")
|
|
|
|
parser.add_option(
|
|
|
|
"-l", "--launchpad", dest="launchpad_instance", default="production")
|
|
|
|
parser.add_option(
|
|
|
|
"-u", "--unsubscribed", action="store_true", default=False,
|
|
|
|
help="Only return packages which have no subscriber")
|
|
|
|
parser.add_option(
|
|
|
|
"-p", "--print", action="store_true", default=False,
|
|
|
|
dest="display",
|
|
|
|
help="Print results to screen instead of a json file")
|
|
|
|
parser.add_option(
|
|
|
|
"-o", "--output-file", default="package-team-mapping.json",
|
|
|
|
help="output JSON to this file")
|
|
|
|
parser.add_option(
|
|
|
|
"-q", "--quiet", action="store_true", default=False,
|
|
|
|
help="Quieten progress messages")
|
|
|
|
options, _ = parser.parse_args()
|
|
|
|
options.suite = None
|
|
|
|
options.distribution = "ubuntu"
|
|
|
|
options.launchpad = Launchpad.login_with(
|
|
|
|
"unsubscribed-packages", options.launchpad_instance)
|
|
|
|
launchpad = options.launchpad
|
|
|
|
ubuntu = launchpad.distributions[options.distribution]
|
|
|
|
options.suites = []
|
|
|
|
for series in ubuntu.series:
|
|
|
|
# very few lucid packages are supported
|
|
|
|
if series.name == 'lucid':
|
|
|
|
continue
|
|
|
|
if series.active:
|
|
|
|
options.suites.append(series.name)
|
|
|
|
# find the dev series
|
|
|
|
if series.status in ['Active Development', 'Pre-release Freeze']:
|
|
|
|
options.dev_suite = series.name
|
|
|
|
|
|
|
|
lputils.setup_location(options)
|
|
|
|
|
|
|
|
team_names = [
|
|
|
|
'checkbox-bugs',
|
|
|
|
'desktop-packages',
|
|
|
|
'documentation-packages',
|
|
|
|
'foundations-bugs',
|
|
|
|
'kernel-packages',
|
|
|
|
'kubuntu-bugs',
|
|
|
|
'landscape',
|
|
|
|
'maas-maintainers',
|
|
|
|
'mir-team',
|
|
|
|
'pkg-ime',
|
|
|
|
'snappy-dev',
|
|
|
|
'translators-packages',
|
|
|
|
'ubuntu-openstack',
|
|
|
|
'ubuntu-printing',
|
|
|
|
'ubuntu-security',
|
|
|
|
'ubuntu-server',
|
|
|
|
]
|
|
|
|
|
|
|
|
data = { "unsubscribed": [] }
|
|
|
|
subscriptions = defaultdict(list)
|
|
|
|
for team_name in team_names:
|
|
|
|
data[team_name] = []
|
|
|
|
team = launchpad.people[team_name]
|
|
|
|
team_subs = team.getBugSubscriberPackages()
|
|
|
|
for src_pkg in team_subs:
|
|
|
|
subscriptions[src_pkg.name].append(team_name)
|
|
|
|
data[team_name].append(src_pkg.name)
|
|
|
|
|
|
|
|
source_packages = source_names(options)
|
|
|
|
for source_package in sorted(source_packages):
|
|
|
|
# we only care about ones people are not subscribed to in the dev release
|
|
|
|
if source_package not in subscriptions and source_packages[source_package]:
|
|
|
|
data["unsubscribed"].append(source_package)
|
|
|
|
if options.display:
|
|
|
|
print("No team is subscribed to: %s" %
|
|
|
|
source_package)
|
|
|
|
else:
|
|
|
|
if not options.unsubscribed:
|
|
|
|
if options.display:
|
|
|
|
print("%s is subscribed to: %s" %
|
|
|
|
(team_name, source_package))
|
|
|
|
|
|
|
|
if not options.display:
|
|
|
|
with open(options.output_file, 'w') as json_file:
|
|
|
|
json_file.write(json.dumps(data, indent=4))
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|