From f48dda0f0f4a44c9be86609331430b0bbc4d149a Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Thu, 23 Nov 2017 11:17:25 -0500 Subject: [PATCH] ci: verify author identity Make sure committers have correct and valid git settings and verify that the committer idenity matches one of the signed-off-by entries. Signed-off-by: Anas Nashif --- scripts/ci/check-compliance.py | 50 ++++++++++++++- scripts/ci/check_identity.py | 113 --------------------------------- 2 files changed, 49 insertions(+), 114 deletions(-) delete mode 100755 scripts/ci/check_identity.py diff --git a/scripts/ci/check-compliance.py b/scripts/ci/check-compliance.py index 5a4e42adaf5..11bfd8ee809 100755 --- a/scripts/ci/check-compliance.py +++ b/scripts/ci/check-compliance.py @@ -8,7 +8,7 @@ from email.utils import parseaddr import sh import logging import argparse -import check_identity +#from check_identity import verify_signed_off if "ZEPHYR_BASE" not in os.environ: logging.error("$ZEPHYR_BASE environment variable undefined.\n") @@ -94,6 +94,50 @@ def run_checkpatch(tc, commit_range): return 0 +def verify_signed_off(tc, commit): + + signed = [] + author = "" + sha = "" + parsed_addr = None + for line in commit.split("\n"): + match = re.search("^commit\s([^\s]*)", line) + if match: + sha = match.group(1) + match = re.search("^Author:\s(.*)", line) + if match: + author = match.group(1) + parsed_addr = parseaddr(author) + match = re.search("signed-off-by:\s(.*)", line, re.IGNORECASE) + if match: + signed.append(match.group(1)) + + error1 = "%s: author email (%s) needs to match one of the signed-off-by entries." %(sha, author) + error2 = "%s: author email (%s) does not follow the syntax: First Last ." %(sha, author) + error = 0 + failure = None + if author not in signed: + failure = ET.SubElement(tc, 'failure', type="failure", message="identity error") + failure.text = error1 + error = 1 + if not parsed_addr or len(parsed_addr[0].split(" ")) < 2: + if not failure: + failure = ET.SubElement(tc, 'failure', type="failure", message="identity error") + failure.text = error2 + else: + failure.text = failure.text + "\n" + error2 + error = 1 + + return error + +def run_check_identity(tc, range): + error = 0 + for f in get_shas(range): + commit = sh.git("log","--decorate=short", "-n 1", f, **sh_special_args) + error += verify_signed_off(tc, commit) + + return error + def check_doc(tc, range): @@ -114,6 +158,10 @@ tests = { "call": run_gitlint, "name": "Commit message style", }, + "identity": { + "call": run_check_identity, + "name": "Author Identity verification", + }, "checkpatch": { "call": run_checkpatch, "name": "Code style check using checkpatch", diff --git a/scripts/ci/check_identity.py b/scripts/ci/check_identity.py deleted file mode 100755 index 750202ca159..00000000000 --- a/scripts/ci/check_identity.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/env python3 - -# A script to check email identities in commit message. We verify the author -# has signed off using the same email address -import sys -import re, os -from email.utils import parseaddr -import sh -import logging -import argparse - -if "ZEPHYR_BASE" not in os.environ: - logging.error("$ZEPHYR_BASE environment variable undefined.\n") - exit(1) - -logger = None - -repository_path = os.environ['ZEPHYR_BASE'] -sh_special_args = { - '_tty_out': False, - '_cwd': repository_path -} - -def init_logs(): - global logger - log_lev = os.environ.get('LOG_LEVEL', None) - level = logging.INFO - if log_lev == "DEBUG": - level = logging.DEBUG - elif log_lev == "ERROR": - level = logging.ERROR - - console = logging.StreamHandler() - format = logging.Formatter('%(levelname)-8s: %(message)s') - console.setFormatter(format) - logger = logging.getLogger('') - logger.addHandler(console) - logger.setLevel(level) - - logging.debug("Log init completed") - -def parse_args(): - parser = argparse.ArgumentParser( - description="Verify that author identity matches Signed-off-by") - parser.add_argument('-c', '--commits', default=None, - help="Commit range in the form: a..b") - return parser.parse_args() - -def get_shas(refspec): - - sha_list = sh.git("rev-list", - '--max-count={0}'.format(-1 if "." in refspec else 1), - refspec, **sh_special_args).split() - - return sha_list - -def verify_signed_off(tc, commit): - - signed = [] - author = "" - sha = "" - parsed_addr = None - for line in commit.split("\n"): - match = re.search("^commit\s([^\s]*)", line) - if match: - sha = match.group(1) - match = re.search("^Author:\s(.*)", line) - if match: - author = match.group(1) - parsed_addr = parseaddr(author) - match = re.search("signed-off-by:\s(.*)", line, re.IGNORECASE) - if match: - signed.append(match.group(1)) - - error1 = "%s: author email (%s) needs to match one of the signed-off-by entries." %(sha, author) - error2 = "%s: author email (%s) does not follow the syntax: First Last ." %(sha, author) - error = 0 - if tc: - failure = None - if author not in signed: - failure = ET.SubElement(tc, 'failure', type="failure", message="identity error on range: %s" %commit_range) - failure.text = error1 - error = 1 - if not parsed_addr or len(parsed_addr[0].split(" ")) < 2: - if not failure: - failure = ET.SubElement(tc, 'failure', type="failure", message="identity error on range: %s" %commit_range) - failure.text = error2 - else: - failure.text = failure.text + "\n" + error2 - error = 1 - - else: - if author not in signed: - print(error1) - error = 1 - - if not parsed_addr or len(parsed_addr[0].split(" ")) < 2: - print(error2) - error = 1 - - return error - -def main(): - args = parse_args() - if not args.commits: - exit(1) - for f in get_shas(args.commits): - commit = sh.git("log","--decorate=short", "-n 1", f, **sh_special_args) - verify_signed_off(None, commit) - -if __name__ == "__main__": - main() -