summaryrefslogtreecommitdiffstatshomepage
path: root/tools/verifygitlog.py
diff options
context:
space:
mode:
authorAngus Gratton <angus@redyak.com.au>2022-08-29 17:30:14 +1000
committerDamien George <damien@micropython.org>2022-10-04 14:52:48 +1100
commit0e35c4de9b9d55f9288eeb908efd2f7f577dc13b (patch)
tree8e5ddd70fb8d692ab88b07af431f1b0a3d53c8e8 /tools/verifygitlog.py
parentbdac8272d8b1a726012bec897dc51dacbe70b295 (diff)
downloadmicropython-0e35c4de9b9d55f9288eeb908efd2f7f577dc13b.tar.gz
micropython-0e35c4de9b9d55f9288eeb908efd2f7f577dc13b.zip
tools: Add pre-commit support.
Tweak the existing codeformat.py and verifygitlog.py to allow them to be easily called by pre-commit. (This turned out to be easier than using any existing pre-commit hooks, without making subtle changes in the formatting.) This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
Diffstat (limited to 'tools/verifygitlog.py')
-rwxr-xr-xtools/verifygitlog.py97
1 files changed, 62 insertions, 35 deletions
diff --git a/tools/verifygitlog.py b/tools/verifygitlog.py
index ce36791256..f9d98106d6 100755
--- a/tools/verifygitlog.py
+++ b/tools/verifygitlog.py
@@ -7,6 +7,8 @@ import sys
verbosity = 0 # Show what's going on, 0 1 or 2.
suggestions = 1 # Set to 0 to not include lengthy suggestions in error messages.
+ignore_prefixes = []
+
def verbose(*args):
if verbosity:
@@ -18,6 +20,22 @@ def very_verbose(*args):
print(*args)
+class ErrorCollection:
+ # Track errors and warnings as the program runs
+ def __init__(self):
+ self.has_errors = False
+ self.has_warnings = False
+ self.prefix = ""
+
+ def error(self, text):
+ print("error: {}{}".format(self.prefix, text))
+ self.has_errors = True
+
+ def warning(self, text):
+ print("warning: {}{}".format(self.prefix, text))
+ self.has_warnings = True
+
+
def git_log(pretty_format, *args):
# Delete pretty argument from user args so it doesn't interfere with what we do.
args = ["git", "log"] + [arg for arg in args if "--pretty" not in args]
@@ -28,83 +46,88 @@ def git_log(pretty_format, *args):
yield line.decode().rstrip("\r\n")
-def verify(sha):
+def verify(sha, err):
verbose("verify", sha)
- errors = []
- warnings = []
-
- def error_text(err):
- return "commit " + sha + ": " + err
-
- def error(err):
- errors.append(error_text(err))
-
- def warning(err):
- warnings.append(error_text(err))
+ err.prefix = "commit " + sha + ": "
# Author and committer email.
for line in git_log("%ae%n%ce", sha, "-n1"):
very_verbose("email", line)
if "noreply" in line:
- error("Unwanted email address: " + line)
+ err.error("Unwanted email address: " + line)
# Message body.
raw_body = list(git_log("%B", sha, "-n1"))
+ verify_message_body(raw_body, err)
+
+
+def verify_message_body(raw_body, err):
if not raw_body:
- error("Message is empty")
- return errors, warnings
+ err.error("Message is empty")
+ return
# Subject line.
subject_line = raw_body[0]
+ for prefix in ignore_prefixes:
+ if subject_line.startswith(prefix):
+ verbose("Skipping ignored commit message")
+ return
very_verbose("subject_line", subject_line)
subject_line_format = r"^[^!]+: [A-Z]+.+ .+\.$"
if not re.match(subject_line_format, subject_line):
- error("Subject line should match " + repr(subject_line_format) + ": " + subject_line)
+ err.error("Subject line should match " + repr(subject_line_format) + ": " + subject_line)
if len(subject_line) >= 73:
- error("Subject line should be 72 or less characters: " + subject_line)
+ err.error("Subject line should be 72 or less characters: " + subject_line)
# Second one divides subject and body.
if len(raw_body) > 1 and raw_body[1]:
- error("Second message line should be empty: " + raw_body[1])
+ err.error("Second message line should be empty: " + raw_body[1])
# Message body lines.
for line in raw_body[2:]:
# Long lines with URLs are exempt from the line length rule.
if len(line) >= 76 and "://" not in line:
- error("Message lines should be 75 or less characters: " + line)
+ err.error("Message lines should be 75 or less characters: " + line)
if not raw_body[-1].startswith("Signed-off-by: ") or "@" not in raw_body[-1]:
- warning("Message should be signed-off")
-
- return errors, warnings
+ err.warning("Message should be signed-off")
def run(args):
verbose("run", *args)
- has_errors = False
- has_warnings = False
- for sha in git_log("%h", *args):
- errors, warnings = verify(sha)
- has_errors |= any(errors)
- has_warnings |= any(warnings)
- for err in errors:
- print("error:", err)
- for err in warnings:
- print("warning:", err)
- if has_errors or has_warnings:
+
+ err = ErrorCollection()
+
+ if "--check-file" in args:
+ filename = args[-1]
+ verbose("checking commit message from", filename)
+ with open(args[-1]) as f:
+ lines = [line.rstrip("\r\n") for line in f]
+ verify_message_body(lines, err)
+ else: # Normal operation, pass arguments to git log
+ for sha in git_log("%h", *args):
+ verify(sha, err)
+
+ if err.has_errors or err.has_warnings:
if suggestions:
print("See https://github.com/micropython/micropython/blob/master/CODECONVENTIONS.md")
else:
print("ok")
- if has_errors:
+ if err.has_errors:
sys.exit(1)
def show_help():
- print("usage: verifygitlog.py [-v -n -h] ...")
+ print("usage: verifygitlog.py [-v -n -h --check-file] ...")
print("-v : increase verbosity, can be speficied multiple times")
print("-n : do not print multi-line suggestions")
print("-h : print this help message and exit")
+ print(
+ "--check-file : Pass a single argument which is a file containing a candidate commit message"
+ )
+ print(
+ "--ignore-rebase : Skip checking commits with git rebase autosquash prefixes or WIP as a prefix"
+ )
print("... : arguments passed to git log to retrieve commits to verify")
print(" see https://www.git-scm.com/docs/git-log")
print(" passing no arguments at all will verify all commits")
@@ -117,6 +140,10 @@ if __name__ == "__main__":
args = sys.argv[1:]
verbosity = args.count("-v")
suggestions = args.count("-n") == 0
+ if "--ignore-rebase" in args:
+ args.remove("--ignore-rebase")
+ ignore_prefixes = ["squash!", "fixup!", "amend!", "WIP"]
+
if "-h" in args:
show_help()
else: