-
-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit adds these checks. - Title consists of module name(s) and subject or just subject. - If there are two or more module names, the module names has to be separated by a comma followed by an optional space. - First word of the subject is starting from an upper case letter and remaining characters in the first word are lower case letters. "Don't" and a word with a hyphen at the middle are also acceptable. - The title is 72 characters at max including module prefix. - If the subject exceed 50 characters excluding the module prefix, display a warning message. - Titles for commits of revert and merge are ignored. - Full description lines are 72 columns max.
- Loading branch information
Showing
2 changed files
with
156 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
name: Commit Message Check | ||
|
||
on: [pull_request] | ||
|
||
jobs: | ||
ubuntu64: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v2 | ||
with: | ||
fetch-depth: 0 | ||
|
||
- name: Setup environment | ||
run: | | ||
pip3 install -U gitpython | ||
- name: Fetch OBS Studio master | ||
run: | | ||
git fetch origin | ||
- name: Check the log messages | ||
run: | | ||
./CI/check-log-msg.py origin/master.. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
#! /bin/env python3 | ||
|
||
import git | ||
import re | ||
|
||
has_error = False | ||
|
||
LOG_ERROR = 100 | ||
LOG_WARNING = 200 | ||
LOG_INFO = 300 | ||
current_commit = None | ||
|
||
def blog(level, txt): | ||
if level == LOG_ERROR: | ||
global has_error | ||
has_error = True | ||
s_level = "Error: " | ||
elif level == LOG_WARNING: | ||
s_level = "Warning: " | ||
elif level == LOG_INFO: | ||
s_level = "Info: " | ||
print('{level}commit {hexsha}: {txt}'.format(level = s_level, hexsha = current_commit.hexsha[:9], txt = txt)) | ||
|
||
def find_directory_name(name, tree, max_depth): | ||
for t in tree.trees: | ||
if name == t.name: | ||
return True | ||
if max_depth > 1: | ||
for t in tree.trees: | ||
if find_directory_name(name, t, max_depth - 1): | ||
return True | ||
for b in tree.blobs: | ||
if name == b.name: | ||
return True | ||
return False | ||
|
||
def check_path(path, tree): | ||
paths = path.split('/', 1) | ||
name = paths[0] | ||
if len(paths) == 1: | ||
return find_directory_name(name, tree, 1) | ||
else: | ||
for t in tree.trees: | ||
if name == t.name: | ||
return check_path(paths[1], t) | ||
return False | ||
|
||
def find_submodule_name(name, submodules): | ||
for s in submodules: | ||
sname = s.name.split('/')[-1] | ||
if name == sname: | ||
return True | ||
|
||
def check_module_names(names): | ||
for name in names: | ||
if name.find('/') >= 0: | ||
if check_path(name, current_commit.tree): | ||
return True | ||
else: | ||
if find_directory_name(name, current_commit.tree, 3): | ||
return True | ||
# TODO: Cannot handle removed submodules. Implement to parse .gitmodules file. | ||
if find_submodule_name(name, current_commit.repo.submodules): | ||
return True | ||
blog(LOG_ERROR, "unknown module name '%s'" % name) | ||
|
||
def check_message_title(title): | ||
global has_error | ||
title_split = title.split(': ', 1) | ||
if len(title_split) == 2: | ||
check_module_names(re.split(r', *', title_split[0])) | ||
title_text = title_split[1] | ||
else: | ||
title_text = title_split[0] | ||
|
||
if len(title) > 72: | ||
blog(LOG_ERROR, 'Too long title: %s' % title) | ||
|
||
if len(title_text) > 50: | ||
blog(LOG_WARNING, 'Too long title excluding module name: %s' % title_text) | ||
|
||
if not re.match(r"([A-Z][a-z]*(-[a-z]+)*|Don't) ", title_text): | ||
blog(LOG_ERROR, 'Invalid first word: %s' % title_text.split(' ', 1)[0]) | ||
has_error = True | ||
|
||
def check_message_body(body): | ||
for line in body.split('\n'): | ||
if len(line) > 72 and line.find(' ') > 0: | ||
blog(LOG_ERROR, 'Too long description in a line: %s' % line) | ||
pass | ||
|
||
def check_message(c): | ||
msg = c.message.split('\n', 2) | ||
if len(msg) == 0: | ||
blog(LOG_ERROR, 'Commit message is empty.') | ||
return True | ||
|
||
if re.match(r'Revert ', msg[0]): | ||
return False | ||
if re.match(r'Merge [0-9a-f]{40} into [0-9a-f]{40}$', msg[0]): | ||
return False | ||
if re.match(r'Merge pull request', msg[0]): | ||
return False | ||
|
||
if len(msg) > 0: | ||
check_message_title(msg[0]) | ||
if len(msg) > 1: | ||
if len(msg[1]): | ||
blog(LOG_ERROR, '2nd line is not empty.') | ||
if len(msg) > 2: | ||
check_message_body(msg[2]) | ||
return has_error | ||
|
||
def main(): | ||
import sys | ||
import argparse | ||
parser = argparse.ArgumentParser(prog='check-log-msg.py', description='Log message compliance checker') | ||
parser.add_argument('commits') | ||
args = parser.parse_args() | ||
|
||
repo = git.Repo('.') | ||
global has_error | ||
for c in repo.iter_commits(args.commits): # '27.1.0-rc2..27.1.0'): | ||
global current_commit | ||
current_commit = c | ||
blog(LOG_INFO, "Checking commit '%s'" % c.message.split('\n', 1)[0]) | ||
check_message(c) | ||
if has_error: | ||
sys.exit(1) | ||
|
||
if __name__ == '__main__': | ||
ret = main() |