#!/usr/bin/python3

# This script checks that the committer and author email addresses of commits
# are valid - this is important because we need to be able to import the git
# repo to launchpad and launchpad does not like git commits that are formatted
# badly.
# This can be run either on a merge commit in which case the commits it
# evaluates is just limited to the "other" branch (the one being merged into the
# destination) like we do in CI workflows. Or it can be run on a normal commit
# like a developer would do locally in which case this ends up checking all
# commits locally - some day that may grow to be unwieldy but for now seems ok.

import os
import subprocess
from email.utils import parseaddr


def get_commit_range():
    # For CI, the head revision is a synthesised merge commit,
    # merging the proposed branch into the destination branch.
    # So the first parent is our destination, and the second is
    # our proposal.
    lines = subprocess.check_output(
        ["git", "cat-file", "-p", "@"], text=True
    ).splitlines()
    parents = [
        line[len("parent ") :].strip() for line in lines if line.startswith("parent ")
    ]
    if len(parents) == 1:
        # not a merge commit, so return nothing to use default git log behavior
        # and check all commits
        return ""
    elif len(parents) == 2:
        # merge commit so use "foo..bar" syntax to just check the proposed
        # commits
        dest, proposed = parents
        return "{}..{}".format(dest, proposed)
    else:
        raise RuntimeError("expected two parents, but got {}".format(parents))


def exception_invalid(maybeaddr: str) -> bool:
    # from commit d935048f4ee5f9a8cde640a760bf1fdb9409de7e
    # dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    if "+dependabot[bot]@users.noreply.github.com" in maybeaddr:
        return True
    return False


if __name__ == "__main__":
    if not os.path.exists('.git'):
        exit(0)
    commitrange = get_commit_range()
    args = ["git", "log", "--format=format:%h,%ce%n%h,%ae"]
    if commitrange != "":
        args.append(commitrange)
    for line in subprocess.check_output(args, text=True).split("\n"):
        parsed = line.split(",", 1)
        commithash = parsed[0]
        potentialemail = parsed[1]
        if potentialemail == "":
            continue
        name, addr = parseaddr(potentialemail)
        if addr == "" and not exception_invalid(potentialemail):
            print(
                "Found invalid email %s for commmit %s" % (potentialemail, commithash)
            )
            exit(1)
        if not addr.isascii():
            print(
                "Found invalid non-ascii email %s for commmit %s"
                % (potentialemail, commithash)
            )
            exit(1)
