Codebase list rust-stfu8 / a2ba87f dev / rust-excuses.py
a2ba87f

Tree @a2ba87f (Download .tar.gz)

rust-excuses.py @a2ba87f

d8ce215
 
 
43ed93d
 
1db6a00
00c29ce
 
1db6a00
 
 
60c1369
b4000f3
 
60c1369
7f8aa2c
 
b4000f3
 
 
7f8aa2c
 
1db6a00
 
78e0158
1db6a00
 
 
b4000f3
1db6a00
00c29ce
1db6a00
 
60c1369
1db6a00
 
 
00c29ce
b4000f3
 
00c29ce
b4000f3
 
1db6a00
b4000f3
 
 
00c29ce
 
b4000f3
 
 
 
 
 
 
 
 
 
00c29ce
d8ce215
 
 
a2ba87f
d8ce215
508cd1b
a2ba87f
d8ce215
1db6a00
b4000f3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d8ce215
b4000f3
d8ce215
b4000f3
 
 
 
 
 
 
 
 
 
 
 
 
d8ce215
a2ba87f
d8ce215
 
a2ba87f
d8ce215
60c1369
 
 
 
 
 
 
 
d8ce215
a2ba87f
 
 
 
 
 
 
 
 
508cd1b
d8ce215
b4000f3
 
 
 
 
d166a96
1db6a00
 
b4000f3
 
1db6a00
#!/usr/bin/pypy3
# pypy3 runs faster than python3
#
# the excuses file can be found here:
# https://release.debian.org/britney/excuses.yaml

import re
import subprocess
import sys
import yaml

if len(sys.argv) != 4:
    print(
        """Generates dot files to show the migration deps
Usage: %s excuses.dot excuses_arch.dot regressions.list

Expects excuses.yaml in the current dir
    """
        % sys.argv[0]
    )
    sys.exit(0)

print("parsing excuses.yaml...", file=sys.stderr)
with open("excuses.yaml") as fp:
    y = yaml.load(fp, Loader=yaml.FullLoader)

excuses = {}
for e in y["sources"]:
    excuses[e["source"]] = e

print("generating dot files...", file=sys.stderr)
rust_excuses = open(sys.argv[1], "w")
rust_excuses_arch = open(sys.argv[2], "w")
rust_regressions = open(sys.argv[3], "w")

already_seen = set()

def edge_dep(name, dep):
    return " ".join(['"%s"' % name, "->", '"%s"' % dep])

def edge_dep_label(name, dep, label):
    return " ".join(['"%s"' % name, "->", '"%s"' % dep, '[label="%s"]' % label])

def print_all(*args, **kwargs):
    print(*args, **kwargs, file=rust_excuses)
    print(*args, **kwargs, file=rust_excuses_arch)

is_in_debian_cache = {}
def is_in_debian(src):
    global is_in_debian_cache
    if src not in is_in_debian_cache:
        n = subprocess.check_output(
            "apt-cache showsrc %s 2>/dev/null | grep ^Package: | wc -l" % src,
            shell=True,
        )
        is_in_debian_cache[src] = int(n.strip())
    x = is_in_debian_cache[src]
    return x


BG_NOT_IN_DEBIAN = "#cc0000"
BG_OLD_IN_DEBIAN = "#ffcc66"
BG_TOO_NEW = "#66ff99"
BG_MISC_WAIT = "#ffcc99"
BG_MISC_FAIL = "#ff6666"
BG_SOURCEONLY = "#9999ff"
VERDICT_FAIL = ("REJECTED_CANNOT_DETERMINE_IF_PERMANENT", "REJECTED_NEEDS_APPROVAL", "REJECTED_PERMANENTLY")

def traverse(name, arch="", d=0):
    if name in already_seen:
        return
    already_seen.add(name)
    dependencies = excuses.get(name, {}).get("dependencies", {})

    edges = set()
    for arch, deps in dependencies.get("unsatisfiable-dependencies", {}).items():
        for dep in deps:
            vers = ""
            if dep.startswith("librust-"):
                try:
                    results = re.match(r"librust-(\S+?)(-[.0-9]+)?(\+\S*)?-dev", dep)
                    dep = "rust-" + results.group(1)
                    if results.group(2):
                        vers = results.group(2)
                except Exception:
                    print(dep, file=sys.stderr)
                    raise
            edges.add(edge_dep_label(name, dep, vers))
            print(
                edge_dep_label(name, dep, "%s/%s" % (vers, arch) if vers else arch),
                file=rust_excuses_arch,
            )
            if dep not in already_seen:
                if is_in_debian(dep):
                    print_all('"%s" [fillcolor="%s",style=filled]' % (dep, BG_OLD_IN_DEBIAN))
                else:
                    print_all('"%s" [fillcolor="%s",style=filled]' % (dep, BG_NOT_IN_DEBIAN))
    for edge in edges:
        print(edge, file=rust_excuses)

    for dep in dependencies.get("migrate-after", []) + dependencies.get(
        "blocked-by", []
    ):
        if "/" in dep:
            dep, arch = dep.split("/")
            print(edge_dep_label(name, dep, arch), file=rust_excuses_arch)
        else:
            print_all(edge_dep(name, dep))
        traverse(dep, arch, d + 1)

    policy = excuses.get(name, {}).get("policy_info", {})
    failed = {k: v["verdict"] for (k, v) in policy.items() if v.get("verdict", "") != "PASS"}
    attrs = {"shape": "box"}
    if "age" in failed:
        del failed["age"]
        attrs.update({ "fillcolor": BG_TOO_NEW, "style": "filled" })
    if "autopkgtest" in failed:
        for k, v in policy["autopkgtest"].items():
            if k == "verdict": continue
            for uu in v.values():
                if "REGRESSION" in uu:
                    for u in uu:
                        if u and u.startswith("https://ci.debian.net/data/autopkgtest/testing"):
                            print(u, file=rust_regressions)
    if failed:
        if list(failed.keys()) == ["builtonbuildd"]:
            bg = BG_SOURCEONLY
            attrs.update({ "label": "\\N\\nneeds source-only upload" })
        elif any(v in VERDICT_FAIL for v in failed.values()):
            bg = BG_MISC_FAIL
            attrs.update({ "label": "\\N\\nfailed: %s" % ",".join(failed.keys()) })
        else:
            bg = BG_MISC_WAIT
            attrs.update({ "label": "\\N\\nwaiting: %s" % ",".join(failed.keys()) })
        attrs.update({ "fillcolor": bg, "style": "filled" })
    print_all('"%s" [%s]' % (name, ",".join("%s=\"%s\"" % p for p in attrs.items())))


# import code
# code.interact(local=locals())


print_all("digraph {")
for s in excuses.keys():
    if s.startswith("rust-"):
        traverse(s)
print_all("}")