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 <anas.nashif@intel.com>
This commit is contained in:
parent
a48724fd0f
commit
f48dda0f0f
2 changed files with 49 additions and 114 deletions
|
@ -8,7 +8,7 @@ from email.utils import parseaddr
|
||||||
import sh
|
import sh
|
||||||
import logging
|
import logging
|
||||||
import argparse
|
import argparse
|
||||||
import check_identity
|
#from check_identity import verify_signed_off
|
||||||
|
|
||||||
if "ZEPHYR_BASE" not in os.environ:
|
if "ZEPHYR_BASE" not in os.environ:
|
||||||
logging.error("$ZEPHYR_BASE environment variable undefined.\n")
|
logging.error("$ZEPHYR_BASE environment variable undefined.\n")
|
||||||
|
@ -94,6 +94,50 @@ def run_checkpatch(tc, commit_range):
|
||||||
|
|
||||||
return 0
|
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 <email>." %(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):
|
def check_doc(tc, range):
|
||||||
|
|
||||||
|
@ -114,6 +158,10 @@ tests = {
|
||||||
"call": run_gitlint,
|
"call": run_gitlint,
|
||||||
"name": "Commit message style",
|
"name": "Commit message style",
|
||||||
},
|
},
|
||||||
|
"identity": {
|
||||||
|
"call": run_check_identity,
|
||||||
|
"name": "Author Identity verification",
|
||||||
|
},
|
||||||
"checkpatch": {
|
"checkpatch": {
|
||||||
"call": run_checkpatch,
|
"call": run_checkpatch,
|
||||||
"name": "Code style check using checkpatch",
|
"name": "Code style check using checkpatch",
|
||||||
|
|
|
@ -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 <email>." %(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()
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue