blob: f475223aeb86c89e051aa86bc5ceae3fc9285c35 [file] [log] [blame]
#!/usr/bin/env python3
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
import argparse
import subprocess
import sys
from git import Repo
error_msg_prefix = 'ERROR: '
warning_msg_prefix = 'WARNING: '
# Maximum length of the summary line in the commit message (the first line)
# There is no hard limit, but a typical convention is to keep this line at or
# below 50 characters, with occasional outliers.
COMMIT_MSG_MAX_SUMMARAY_LEN = 100
def error(msg, commit=None):
full_msg = msg
if commit:
full_msg = "Commit %s: %s" % (commit.hexsha, msg)
print(error_msg_prefix + full_msg, file=sys.stderr)
def warning(msg, commit=None):
full_msg = msg
if commit:
full_msg = "Commit %s: %s" % (commit.hexsha, msg)
print(warning_msg_prefix + full_msg, file=sys.stderr)
def lint_commit_author(commit):
success = True
if commit.author.email.endswith('users.noreply.github.com'):
error(
'Commit author has no valid email address set: %s. '
'Use "git config user.email user@example.com" to '
'set a valid email address, and update the commit '
'with "git rebase -i" and/or '
'"git commit --amend --reset-author". '
'Also check your GitHub settings at '
'https://github.com/settings/emails: your email address '
'must be verified, and the option "Keep my email address '
'private" must be disabled.' % (commit.author.email, ), commit)
success = False
if not ' ' in commit.author.name:
warning(
'The commit author name "%s" does contain no space. '
'Use "git config user.name \'Johnny English\'" to '
'set your real name, and update the commit with "git rebase -i " '
'and/or "git commit --amend --reset-author".' %
(commit.author.name, ), commit)
# A warning doesn't fail lint.
return success
def lint_commit_message(commit):
success = True
lines = commit.message.splitlines()
# Check length of summary line.
summary_line_len = len(lines[0])
if summary_line_len > COMMIT_MSG_MAX_SUMMARAY_LEN:
error(
"The summary line in the commit message %d characters long, "
"only %d characters are allowed." %
(summary_line_len, COMMIT_MSG_MAX_SUMMARAY_LEN), commit)
success = False
# Check for an empty line separating the summary line from the long
# description.
if len(lines) > 1 and lines[1] != "":
error(
"The second line of a commit message must be empty, as it "
"separates the summary from the long description.", commit)
success = False
return success
def lint_commit(commit):
success = True
if not lint_commit_author(commit):
success = False
if not lint_commit_message(commit):
success = False
return success
def main():
global error_msg_prefix
global warning_msg_prefix
parser = argparse.ArgumentParser(
description='Check commit meta data for common mistakes')
parser.add_argument('--error-msg-prefix',
default=error_msg_prefix,
required=False,
help='string to prepend to all error messages')
parser.add_argument('--warning-msg-prefix',
default=warning_msg_prefix,
required=False,
help='string to prepend to all warning messages')
parser.add_argument('--no-merges',
required=False,
action="store_true",
help='do not check commits with more than one parent')
parser.add_argument('commit_range',
metavar='commit-range',
help='git log-compatible commit range to check')
args = parser.parse_args()
error_msg_prefix = args.error_msg_prefix
warning_msg_prefix = args.warning_msg_prefix
lint_successful = True
repo = Repo()
commits = repo.iter_commits(args.commit_range)
for commit in commits:
print("Checking commit %s" % commit.hexsha)
is_merge = len(commit.parents) > 1
if is_merge and args.no_merges:
print("Skipping merge commit.")
continue
if not lint_commit(commit):
lint_successful = False
if not lint_successful:
error('Commit lint failed.')
sys.exit(1)
if __name__ == '__main__':
main()