Commit-msg hook to comply with Commit messages guidelines

Hi,
I’ve created a git hook to check compliance with Commit messages guidelines.
I’m sharing it here in case anyone will find it useful.

#!/usr/bin/env python3

# Usage:
# In the .git/hooks directory, create a file named commit-msg and paste the script.
# Make the file executable by running chmod +x commit-msg.
# Requires Python 3
import sys

# [X] = Check created, [-] = Check cannot be created, [?] = Check not created
# [X] The commit subject and body must be separated by a blank line.
# [X] The commit subject must start with a capital letter.
# [X] The commit subject must not be longer than 72 characters.
# [X] The commit subject must not end with a period.
# [X] The commit body must not contain more than 72 characters per line.
# [?] The commit subject or body must not contain Emojis.
# [-] Commits that change 30 or more lines across at least 3 files should describe these changes in the commit body.
# [?] Use issues, milestones, and merge requests’ full URLs instead of short references,
#     as they are displayed as plain text outside of GitLab.
# [-] The merge request should not contain more than 10 commit messages.
# [X] The commit subject should contain at least 3 words.


# List of the rules as described in
# https://docs.gitlab.com/ee/development/contributing/merge_request_workflow.html#commit-messages-guidelines
# Rules that don't have a check function should be marked as True otherwise the script will always fail.
rules = [
    {"description": "The commit subject and body must be separated by a blank line.", "status": False},
    {"description": "The commit subject must start with a capital letter.", "status": False},
    {"description": "The commit subject must not be longer than 72 characters.", "status": False},
    {"description": "The commit subject must not end with a period.", "status": False},
    {"description": "The commit body must not contain more than 72 characters per line.", "status": False},
    {"description": "The commit subject or body must not contain Emojis.", "status": True},
    {"description": "Commits that change 30 or more lines across at least 3 files should describe these changes in "
                    "the commit body.", "status": True},
    {"description": "Use issues, milestones, and merge requests’ full URLs instead of short references, as they are "
                    "displayed as plain text outside of GitLab.", "status": True},
    {"description": "The merge request should not contain more than 10 commit messages.", "status": True},
    {"description": "The commit subject should contain at least 3 words.", "status": False},
]


def check_commit_message(message):
    # Split the message into lines
    message_lines = message.splitlines()

    # First line is the subject, second line is the separator, the rest is the body
    subject = message_lines[0]
    body = message_lines[2:]

    # Check that the second line is a blank line as described in the rule #1
    rules[0]['status'] = message_lines[1] == ''
    # Subject rules:
    # Check that the subject starts with a capital letter as described in the rule #2
    rules[1]['status'] = subject[0].isupper()
    # Check that the subject is not longer than 72 characters as described in the rule #3
    rules[2]['status'] = len(subject) <= 72
    # Check that the subject does not end with a period as described in the rule #4
    rules[3]['status'] = not subject.endswith('.')
    # Check that the subject contains at least 3 words as described in the rule #9
    # This is done by splitting the subject using space as the delimiter and counting the words.
    # A word is defined (here) as a string that contains at least one letter or number.
    word_counter = 0
    for word in subject.split():
        has_letter = any(char.isalpha() for char in word)
        has_number = any(char.isdigit() for char in word)
        if has_letter or has_number:
            word_counter += 1
    rules[9]['status'] = word_counter >= 3

    # Check that the body does not contain more than 72 characters per line as described in the rule #5
    for line in body:
        if len(line) > 72:
            break
        rules[4]['status'] = True

    # Print the results
    fail = False
    for rule in rules:
        if rule['status']:
            # Will print met rules in white
            print(f"\033[97m[X] {rule['description']}\033[0m")
        else:
            # Will print unmet rules in red
            print(f"\033[91m[ ] {rule['description']}\033[0m")
            fail = True

    return fail


if __name__ == '__main__':
    # Read the commit message from the file
    commit_message_file = sys.argv[1]
    with open(commit_message_file, "r") as f:
        commit_message = f.read().strip()
    # Will exit with 0 if the commit message is correct, otherwise will exit with 1
    exit(check_commit_message(commit_message))