From fd0882ab41eae7a063594d0f1a8d05dcd72e7ae7 Mon Sep 17 00:00:00 2001 From: gcattan Date: Fri, 1 Apr 2022 16:35:53 +0200 Subject: [PATCH 1/5] Refactoring of python module --- example/git-quality-check.py | 70 ++++++ git-quality-check.py | 218 ------------------ git_quality_check/__init__.py | 0 git_quality_check/_version.py | 1 + git_quality_check/hooks/__init__.py | 4 + git_quality_check/hooks/count_bad_words.py | 8 + git_quality_check/hooks/is_empty_body.py | 12 + git_quality_check/hooks/is_test_commit.py | 5 + .../hooks/not_a_squashed_commit.py | 4 + git_quality_check/indicators/__init__.py | 3 + git_quality_check/indicators/count_coupled.py | 11 + .../indicators/count_old_branches.py | 11 + git_quality_check/indicators/process_logs.py | 8 + git_quality_check/utils/__init__.py | 14 ++ git_quality_check/utils/common.py | 27 +++ git_quality_check/utils/git.py | 74 ++++++ setup.py | 40 ++++ 17 files changed, 292 insertions(+), 218 deletions(-) create mode 100644 example/git-quality-check.py delete mode 100644 git-quality-check.py create mode 100644 git_quality_check/__init__.py create mode 100644 git_quality_check/_version.py create mode 100644 git_quality_check/hooks/__init__.py create mode 100644 git_quality_check/hooks/count_bad_words.py create mode 100644 git_quality_check/hooks/is_empty_body.py create mode 100644 git_quality_check/hooks/is_test_commit.py create mode 100644 git_quality_check/hooks/not_a_squashed_commit.py create mode 100644 git_quality_check/indicators/__init__.py create mode 100644 git_quality_check/indicators/count_coupled.py create mode 100644 git_quality_check/indicators/count_old_branches.py create mode 100644 git_quality_check/indicators/process_logs.py create mode 100644 git_quality_check/utils/__init__.py create mode 100644 git_quality_check/utils/common.py create mode 100644 git_quality_check/utils/git.py create mode 100644 setup.py diff --git a/example/git-quality-check.py b/example/git-quality-check.py new file mode 100644 index 0000000..e24161d --- /dev/null +++ b/example/git-quality-check.py @@ -0,0 +1,70 @@ +import os +from git_quality_check.utils import ( + git_logs, + git_all_branches, + set_output, + format_number +) + +from git_quality_check.indicators import ( + count_old_branches, + count_coupled, + process_logs +) + +from git_quality_check.hooks import ( + is_empty_body, + not_a_squashed_commit, + count_bad_words, + is_test_commit, + bad_words +) + +global main_branches + + +def compute_score(bad_commit_index, test_index, + old_branches_index, coupling_index): + return ((100 - bad_commit_index) + + test_index + (100 - old_branches_index) + + (100 - coupling_index))/4 + + +def parse_inputs(): + global bad_words, main_branches + try: + bad_words = os.environ["INPUT_BADWORDS"].split(", ") + except: + bad_words = ["WIP", "work in progress", "in progress", "TODO"] + try: + main_branches = os.environ["INPUT_MAINBRANCHES"].split(", ") + except: + main_branches = ["origin/develop", "origin/master"] + + +if __name__ == "__main__": + + parse_inputs() + + logs = git_logs() + branches = git_all_branches() + + bad_commit_index = process_logs(logs, [not_a_squashed_commit, + is_empty_body, + count_bad_words]) + test_index = process_logs(logs, [is_test_commit]) + + old_branches_index = count_old_branches(branches) + coupling_index = count_coupled(branches, main_branches) + + print(bad_commit_index) + print(test_index) + print(old_branches_index) + print(coupling_index) + + + + overall = compute_score(bad_commit_index, test_index, + old_branches_index, coupling_index) + + set_output(format_number(overall)) \ No newline at end of file diff --git a/git-quality-check.py b/git-quality-check.py deleted file mode 100644 index c363028..0000000 --- a/git-quality-check.py +++ /dev/null @@ -1,218 +0,0 @@ -from multiprocessing.spawn import old_main_modules -import random -import subprocess -from datetime import datetime -import os - -global bad_words, main_branches - -def run_git(command: list[str]): - command.insert(0, "--no-pager") - command.insert(0, "git") - return subprocess.check_output(command).decode() - - -def git_logs(): - return run_git(["log"]).split("commit ") - - -def remove_first_line(log: str): - if is_valid_log: - try: - eol = log.index("\n") - log = log[eol + 1:] - except ValueError: - # commit is empty or just contains one line - return "" - return log - - -def is_valid_log(log: str): - return not log == "" - - -def process_logs(logs: list[str], functions: list[str: int]): - counter = 0 - count = len(logs) * len(functions) - for i in range(len(logs)): - log = logs[i] - for function in functions: - counter += function(log) - return counter / count * 100 - - -def remove_header(log: str): - log = remove_first_line(log) # Hash - log = remove_first_line(log) # Author - log = remove_first_line(log) # Date - return log - - -def is_empty_body(log: str): - if not is_valid_log(log): - return 1 - log = remove_header(log) - if not is_valid_log(log): - return 1 - return 0 - - -def not_a_squashed_commit(log): - if "(#" not in log: - return 1 - return 0 - - -def count_bad_words(log: str): - counter = 0 - for word in bad_words: - if word in log.lower().split(): - counter += 1 - return counter - - -def is_test_commit(log: str): - for word in ["test", "testing"]: - if word in log.lower().split(): - return 1 - return 0 - - -def strip(s: str): - return s.strip().lstrip() - - -def git_all_branches(): - ret = run_git(["branch", "-r"]).split("\n") - return [strip(r) for r in ret if not strip(r) == ''] - - -def is_well_formed_branch(branch:str): - return not "->" in branch - - -def git_get_branch_date(branch:str): - if not is_well_formed_branch(branch): - return None - ret = run_git(["log", "-n", "1", "--date=format:\"%Y-%m-%d\"", branch]).split("Date: ")[1] - ret = ret.replace("\"", "").split("-") - year = int(strip(ret[0])) - month = int(ret[1]) - day = int(ret[2].split("\n")[0]) - return datetime(year, month, day) - - -def get_date(): - return datetime.today() - - -def diff_month(d1, d2): - return (d1.year - d2.year) * 12 + d1.month - d2.month - - -def is_old(branch): - branch_date = git_get_branch_date(branch) - if not branch_date: - return False - date = get_date() - return diff_month(date, branch_date) > 2 - - -def sample(li: list[str], min: int): - count = len(li) - if(count > min): - li = random.sample(branches, min) - count = min - return (li, count) - - -def count_old_branches(branches): - counter = 0 - branches, count = sample(branches, 10) - for branch in branches: - counter += 1 if is_old(branch) else 0 - return counter / count * 100 - - -def are_coupled(branchA: str, branchB: str): - if not is_well_formed_branch(branchA) or\ - not is_well_formed_branch(branchB): - return False - if branchA == branchB: - return False - try: - ret = run_git(["branch", "--contains", branchA, "-r"]).split("\n") - except: - print("Git `branch --contains failed with: ", branchA) - return False - for r in ret: - if branchB == r.strip().lstrip(): - return True - return False - - -def count_coupled(branches): - branches, count = sample(branches, 10) - branches.extend(main_branches) - count += len(main_branches) - counter = 0 - for bA in branches: - for bB in branches: - counter += 1 if are_coupled(bA, bB) else 0 - return counter / count * 100 - - -def format_number(number:float): - return "{0:.2f}".format(number) - -def set_output(output:str): - print(f"::set-output name=score::{output}") - - -def compute_score(bad_commit_index, test_index, - old_branches_index, coupling_index): - return ((100 - bad_commit_index) + - test_index + (100 - old_branches_index) + - (100 - coupling_index))/4 - - -def parse_inputs(): - global bad_words, main_branches - try: - bad_words = os.environ["INPUT_BADWORDS"].split(", ") - except: - bad_words = ["WIP", "work in progress", "in progress", "TODO"] - try: - main_branches = os.environ["INPUT_MAINBRANCHES"].split(", ") - except: - main_branches = ["origin/develop", "origin/master"] - - -if __name__ == "__main__": - - parse_inputs() - - logs = git_logs() - branches = git_all_branches() - - bad_commit_index = process_logs(logs, [not_a_squashed_commit, - is_empty_body, - count_bad_words]) - test_index = process_logs(logs, [is_test_commit]) - - old_branches_index = count_old_branches(branches) - coupling_index = count_coupled(branches) - - - - print(bad_commit_index) - print(test_index) - print(old_branches_index) - print(coupling_index) - - - - overall = compute_score(bad_commit_index, test_index, - old_branches_index, coupling_index) - - set_output(format_number(overall)) \ No newline at end of file diff --git a/git_quality_check/__init__.py b/git_quality_check/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/git_quality_check/_version.py b/git_quality_check/_version.py new file mode 100644 index 0000000..ee7834c --- /dev/null +++ b/git_quality_check/_version.py @@ -0,0 +1 @@ +__version__ = 'v0.0-beta' diff --git a/git_quality_check/hooks/__init__.py b/git_quality_check/hooks/__init__.py new file mode 100644 index 0000000..1cbaacc --- /dev/null +++ b/git_quality_check/hooks/__init__.py @@ -0,0 +1,4 @@ +from .is_empty_body import is_empty_body +from .not_a_squashed_commit import not_a_squashed_commit +from .count_bad_words import count_bad_words, bad_words +from .is_test_commit import is_test_commit \ No newline at end of file diff --git a/git_quality_check/hooks/count_bad_words.py b/git_quality_check/hooks/count_bad_words.py new file mode 100644 index 0000000..457bffb --- /dev/null +++ b/git_quality_check/hooks/count_bad_words.py @@ -0,0 +1,8 @@ +bad_words: list[str] = [] + +def count_bad_words(log: str): + counter = 0 + for word in bad_words: + if word in log.lower().split(): + counter += 1 + return counter \ No newline at end of file diff --git a/git_quality_check/hooks/is_empty_body.py b/git_quality_check/hooks/is_empty_body.py new file mode 100644 index 0000000..0353f33 --- /dev/null +++ b/git_quality_check/hooks/is_empty_body.py @@ -0,0 +1,12 @@ +from git_quality_check.utils import ( + is_valid_log, + remove_header, +) + +def is_empty_body(log: str): + if not is_valid_log(log): + return 1 + log = remove_header(log) + if not is_valid_log(log): + return 1 + return 0 \ No newline at end of file diff --git a/git_quality_check/hooks/is_test_commit.py b/git_quality_check/hooks/is_test_commit.py new file mode 100644 index 0000000..85b3eb7 --- /dev/null +++ b/git_quality_check/hooks/is_test_commit.py @@ -0,0 +1,5 @@ +def is_test_commit(log: str): + for word in ["test", "testing"]: + if word in log.lower().split(): + return 1 + return 0 \ No newline at end of file diff --git a/git_quality_check/hooks/not_a_squashed_commit.py b/git_quality_check/hooks/not_a_squashed_commit.py new file mode 100644 index 0000000..320fe08 --- /dev/null +++ b/git_quality_check/hooks/not_a_squashed_commit.py @@ -0,0 +1,4 @@ +def not_a_squashed_commit(log: str): + if "(#" not in log: + return 1 + return 0 \ No newline at end of file diff --git a/git_quality_check/indicators/__init__.py b/git_quality_check/indicators/__init__.py new file mode 100644 index 0000000..be619e5 --- /dev/null +++ b/git_quality_check/indicators/__init__.py @@ -0,0 +1,3 @@ +from .count_old_branches import count_old_branches +from .count_coupled import count_coupled +from .process_logs import process_logs \ No newline at end of file diff --git a/git_quality_check/indicators/count_coupled.py b/git_quality_check/indicators/count_coupled.py new file mode 100644 index 0000000..f2b64d6 --- /dev/null +++ b/git_quality_check/indicators/count_coupled.py @@ -0,0 +1,11 @@ +from git_quality_check.utils import sample, are_coupled + +def count_coupled(branches, main_branches): + branches, count = sample(branches, 10) + branches.extend(main_branches) + count += len(main_branches) + counter = 0 + for bA in branches: + for bB in branches: + counter += 1 if are_coupled(bA, bB) else 0 + return counter / count * 100 \ No newline at end of file diff --git a/git_quality_check/indicators/count_old_branches.py b/git_quality_check/indicators/count_old_branches.py new file mode 100644 index 0000000..ed43a1b --- /dev/null +++ b/git_quality_check/indicators/count_old_branches.py @@ -0,0 +1,11 @@ +from git_quality_check.utils import ( + sample, + is_old +) + +def count_old_branches(branches): + counter = 0 + branches, count = sample(branches, 10) + for branch in branches: + counter += 1 if is_old(branch) else 0 + return counter / count * 100 \ No newline at end of file diff --git a/git_quality_check/indicators/process_logs.py b/git_quality_check/indicators/process_logs.py new file mode 100644 index 0000000..a3a2ff5 --- /dev/null +++ b/git_quality_check/indicators/process_logs.py @@ -0,0 +1,8 @@ +def process_logs(logs: list[str], functions: list[str: int]): + counter = 0 + count = len(logs) * len(functions) + for i in range(len(logs)): + log = logs[i] + for function in functions: + counter += function(log) + return counter / count * 100 \ No newline at end of file diff --git a/git_quality_check/utils/__init__.py b/git_quality_check/utils/__init__.py new file mode 100644 index 0000000..7f40c79 --- /dev/null +++ b/git_quality_check/utils/__init__.py @@ -0,0 +1,14 @@ +from .git import ( + is_valid_log, + remove_header, + is_old, + are_coupled, + git_logs, + git_all_branches, +) + +from .common import ( + sample, + set_output, + format_number +) \ No newline at end of file diff --git a/git_quality_check/utils/common.py b/git_quality_check/utils/common.py new file mode 100644 index 0000000..a3fae5b --- /dev/null +++ b/git_quality_check/utils/common.py @@ -0,0 +1,27 @@ + +from datetime import datetime +import random + +def strip(s: str): + return s.strip().lstrip() + +def get_date(): + return datetime.today() + +def sample(li: list[str], min: int): + count = len(li) + if(count > min): + li = random.sample(li, min) + count = min + return (li, count) + +def format_number(number:float): + return "{0:.2f}".format(number) + +def set_output(output:str): + print(f"::set-output name=score::{output}") + +def diff_month(d1, d2): + return (d1.year - d2.year) * 12 + d1.month - d2.month + + diff --git a/git_quality_check/utils/git.py b/git_quality_check/utils/git.py new file mode 100644 index 0000000..f63261d --- /dev/null +++ b/git_quality_check/utils/git.py @@ -0,0 +1,74 @@ +import subprocess +from datetime import datetime +from .common import ( + get_date, + diff_month, + strip +) + +def is_valid_log(log: str): + return not log == "" + +def run_git(command: list[str]): + command.insert(0, "--no-pager") + command.insert(0, "git") + return subprocess.check_output(command).decode() + +def git_logs(): + return run_git(["log"]).split("commit ") + +def is_old(branch): + branch_date = git_get_branch_date(branch) + if not branch_date: + return False + date = get_date() + return diff_month(date, branch_date) > 2 + +def git_all_branches(): + ret = run_git(["branch", "-r"]).split("\n") + return [strip(r) for r in ret if not strip(r) == ''] + +def are_coupled(branchA: str, branchB: str): + if not is_well_formed_branch(branchA) or\ + not is_well_formed_branch(branchB): + return False + if branchA == branchB: + return False + try: + ret = run_git(["branch", "--contains", branchA, "-r"]).split("\n") + except: + print("Git `branch --contains failed with: ", branchA) + return False + for r in ret: + if branchB == r.strip().lstrip(): + return True + return False + +def is_well_formed_branch(branch:str): + return not "->" in branch + +def git_get_branch_date(branch:str): + if not is_well_formed_branch(branch): + return None + ret = run_git(["log", "-n", "1", "--date=format:\"%Y-%m-%d\"", branch]).split("Date: ")[1] + ret = ret.replace("\"", "").split("-") + year = int(strip(ret[0])) + month = int(ret[1]) + day = int(ret[2].split("\n")[0]) + return datetime(year, month, day) + +def remove_first_line(log: str): + if is_valid_log: + try: + eol = log.index("\n") + log = log[eol + 1:] + except ValueError: + # commit is empty or just contains one line + return "" + return log + +def remove_header(log: str): + log = remove_first_line(log) # Hash + log = remove_first_line(log) # Author + log = remove_first_line(log) # Date + return log \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..9893299 --- /dev/null +++ b/setup.py @@ -0,0 +1,40 @@ +import os.path as op + +from setuptools import setup, find_packages + + +# get the version (don't import mne here, so dependencies are not needed) +version = None +with open(op.join('git_quality_check', '_version.py'), 'r') as fid: + for line in (line.strip() for line in fid): + if line.startswith('__version__'): + version = line.split('=')[1].strip().strip('\'') + break +if version is None: + raise RuntimeError('Could not determine version') + +with open('README.md', 'r', encoding="utf8") as fid: + long_description = fid.read() + +setup(name='git-quality-check', + version=version, + description='A simple tool to evalute the quality of a git repository.', + url='https://github.com/gcattan/git-quality-check', + author='Gregoire Cattan', + author_email='gcattan@hotmail.com', + license='BSD (3-clause)', + packages=find_packages(), + long_description=long_description, + long_description_content_type='text/markdown', + project_urls={ + 'Documentation': 'https://github.com/gcattan/git-quality-check', + 'Source': 'https://github.com/gcattan/git-quality-check', + 'Tracker': 'https://github.com/gcattan/git-quality-check/issues/', + }, + platforms='any', + python_requires=">=3.9", + install_requires=[], + # extras_require={'docs': ['sphinx-gallery', 'sphinx-bootstrap_theme', 'numpydoc', 'mne', 'seaborn'], + # 'tests': ['pytest', 'seaborn', 'flake8', 'mne', 'pooch', 'tqdm']}, + zip_safe=False, +) From fdd90cf4de494212e875b2968fc14c3edf7b23bc Mon Sep 17 00:00:00 2001 From: gcattan Date: Fri, 1 Apr 2022 20:01:45 +0200 Subject: [PATCH 2/5] renamce packages --- example/git-quality-check.py | 4 ++-- git_quality_check/{hooks => indicators/commits}/__init__.py | 0 .../{hooks => indicators/commits}/count_bad_words.py | 0 .../{hooks => indicators/commits}/is_empty_body.py | 0 .../{hooks => indicators/commits}/is_test_commit.py | 0 .../{hooks => indicators/commits}/not_a_squashed_commit.py | 0 git_quality_check/indicators/{ => counters}/__init__.py | 0 git_quality_check/indicators/{ => counters}/count_coupled.py | 0 .../indicators/{ => counters}/count_old_branches.py | 0 git_quality_check/indicators/{ => counters}/process_logs.py | 0 10 files changed, 2 insertions(+), 2 deletions(-) rename git_quality_check/{hooks => indicators/commits}/__init__.py (100%) rename git_quality_check/{hooks => indicators/commits}/count_bad_words.py (100%) rename git_quality_check/{hooks => indicators/commits}/is_empty_body.py (100%) rename git_quality_check/{hooks => indicators/commits}/is_test_commit.py (100%) rename git_quality_check/{hooks => indicators/commits}/not_a_squashed_commit.py (100%) rename git_quality_check/indicators/{ => counters}/__init__.py (100%) rename git_quality_check/indicators/{ => counters}/count_coupled.py (100%) rename git_quality_check/indicators/{ => counters}/count_old_branches.py (100%) rename git_quality_check/indicators/{ => counters}/process_logs.py (100%) diff --git a/example/git-quality-check.py b/example/git-quality-check.py index e24161d..5cf313b 100644 --- a/example/git-quality-check.py +++ b/example/git-quality-check.py @@ -6,13 +6,13 @@ format_number ) -from git_quality_check.indicators import ( +from git_quality_check.indicators.counters import ( count_old_branches, count_coupled, process_logs ) -from git_quality_check.hooks import ( +from git_quality_check.indicators.commits import ( is_empty_body, not_a_squashed_commit, count_bad_words, diff --git a/git_quality_check/hooks/__init__.py b/git_quality_check/indicators/commits/__init__.py similarity index 100% rename from git_quality_check/hooks/__init__.py rename to git_quality_check/indicators/commits/__init__.py diff --git a/git_quality_check/hooks/count_bad_words.py b/git_quality_check/indicators/commits/count_bad_words.py similarity index 100% rename from git_quality_check/hooks/count_bad_words.py rename to git_quality_check/indicators/commits/count_bad_words.py diff --git a/git_quality_check/hooks/is_empty_body.py b/git_quality_check/indicators/commits/is_empty_body.py similarity index 100% rename from git_quality_check/hooks/is_empty_body.py rename to git_quality_check/indicators/commits/is_empty_body.py diff --git a/git_quality_check/hooks/is_test_commit.py b/git_quality_check/indicators/commits/is_test_commit.py similarity index 100% rename from git_quality_check/hooks/is_test_commit.py rename to git_quality_check/indicators/commits/is_test_commit.py diff --git a/git_quality_check/hooks/not_a_squashed_commit.py b/git_quality_check/indicators/commits/not_a_squashed_commit.py similarity index 100% rename from git_quality_check/hooks/not_a_squashed_commit.py rename to git_quality_check/indicators/commits/not_a_squashed_commit.py diff --git a/git_quality_check/indicators/__init__.py b/git_quality_check/indicators/counters/__init__.py similarity index 100% rename from git_quality_check/indicators/__init__.py rename to git_quality_check/indicators/counters/__init__.py diff --git a/git_quality_check/indicators/count_coupled.py b/git_quality_check/indicators/counters/count_coupled.py similarity index 100% rename from git_quality_check/indicators/count_coupled.py rename to git_quality_check/indicators/counters/count_coupled.py diff --git a/git_quality_check/indicators/count_old_branches.py b/git_quality_check/indicators/counters/count_old_branches.py similarity index 100% rename from git_quality_check/indicators/count_old_branches.py rename to git_quality_check/indicators/counters/count_old_branches.py diff --git a/git_quality_check/indicators/process_logs.py b/git_quality_check/indicators/counters/process_logs.py similarity index 100% rename from git_quality_check/indicators/process_logs.py rename to git_quality_check/indicators/counters/process_logs.py From 59966f75e591651dd36725856413f0a0904dea11 Mon Sep 17 00:00:00 2001 From: gcattan Date: Fri, 1 Apr 2022 20:22:02 +0200 Subject: [PATCH 3/5] remove global declaration --- example/git-quality-check.py | 31 +++++-------------- .../indicators/commits/__init__.py | 2 +- .../indicators/commits/count_bad_words.py | 15 ++++----- git_quality_check/scoring/__init__.py | 1 + git_quality_check/scoring/overall.py | 5 +++ git_quality_check/utils/__init__.py | 3 +- git_quality_check/utils/common.py | 17 +++++++++- 7 files changed, 40 insertions(+), 34 deletions(-) create mode 100644 git_quality_check/scoring/__init__.py create mode 100644 git_quality_check/scoring/overall.py diff --git a/example/git-quality-check.py b/example/git-quality-check.py index 5cf313b..49a1b6a 100644 --- a/example/git-quality-check.py +++ b/example/git-quality-check.py @@ -3,7 +3,8 @@ git_logs, git_all_branches, set_output, - format_number + format_number, + parse_inputs ) from git_quality_check.indicators.counters import ( @@ -12,46 +13,28 @@ process_logs ) + from git_quality_check.indicators.commits import ( is_empty_body, not_a_squashed_commit, count_bad_words, is_test_commit, - bad_words ) -global main_branches - - -def compute_score(bad_commit_index, test_index, - old_branches_index, coupling_index): - return ((100 - bad_commit_index) + - test_index + (100 - old_branches_index) + - (100 - coupling_index))/4 - - -def parse_inputs(): - global bad_words, main_branches - try: - bad_words = os.environ["INPUT_BADWORDS"].split(", ") - except: - bad_words = ["WIP", "work in progress", "in progress", "TODO"] - try: - main_branches = os.environ["INPUT_MAINBRANCHES"].split(", ") - except: - main_branches = ["origin/develop", "origin/master"] +from git_quality_check.scoring import compute_score if __name__ == "__main__": - parse_inputs() + + bad_words, main_branches = parse_inputs() logs = git_logs() branches = git_all_branches() bad_commit_index = process_logs(logs, [not_a_squashed_commit, is_empty_body, - count_bad_words]) + count_bad_words(bad_words)]) test_index = process_logs(logs, [is_test_commit]) old_branches_index = count_old_branches(branches) diff --git a/git_quality_check/indicators/commits/__init__.py b/git_quality_check/indicators/commits/__init__.py index 1cbaacc..09cb675 100644 --- a/git_quality_check/indicators/commits/__init__.py +++ b/git_quality_check/indicators/commits/__init__.py @@ -1,4 +1,4 @@ from .is_empty_body import is_empty_body from .not_a_squashed_commit import not_a_squashed_commit -from .count_bad_words import count_bad_words, bad_words +from .count_bad_words import count_bad_words from .is_test_commit import is_test_commit \ No newline at end of file diff --git a/git_quality_check/indicators/commits/count_bad_words.py b/git_quality_check/indicators/commits/count_bad_words.py index 457bffb..c669d66 100644 --- a/git_quality_check/indicators/commits/count_bad_words.py +++ b/git_quality_check/indicators/commits/count_bad_words.py @@ -1,8 +1,9 @@ -bad_words: list[str] = [] -def count_bad_words(log: str): - counter = 0 - for word in bad_words: - if word in log.lower().split(): - counter += 1 - return counter \ No newline at end of file +def count_bad_words(bad_words: list[str]): + def _count_bad_words(log: str): + counter = 0 + for word in bad_words: + if word in log.lower().split(): + counter += 1 + return counter + return _count_bad_words \ No newline at end of file diff --git a/git_quality_check/scoring/__init__.py b/git_quality_check/scoring/__init__.py new file mode 100644 index 0000000..f6dd561 --- /dev/null +++ b/git_quality_check/scoring/__init__.py @@ -0,0 +1 @@ +from .overall import compute_score \ No newline at end of file diff --git a/git_quality_check/scoring/overall.py b/git_quality_check/scoring/overall.py new file mode 100644 index 0000000..f03a6b0 --- /dev/null +++ b/git_quality_check/scoring/overall.py @@ -0,0 +1,5 @@ +def compute_score(bad_commit_index, test_index, + old_branches_index, coupling_index): + return ((100 - bad_commit_index) + + test_index + (100 - old_branches_index) + + (100 - coupling_index))/4 \ No newline at end of file diff --git a/git_quality_check/utils/__init__.py b/git_quality_check/utils/__init__.py index 7f40c79..5ba68b2 100644 --- a/git_quality_check/utils/__init__.py +++ b/git_quality_check/utils/__init__.py @@ -10,5 +10,6 @@ from .common import ( sample, set_output, - format_number + format_number, + parse_inputs ) \ No newline at end of file diff --git a/git_quality_check/utils/common.py b/git_quality_check/utils/common.py index a3fae5b..d54932c 100644 --- a/git_quality_check/utils/common.py +++ b/git_quality_check/utils/common.py @@ -1,6 +1,21 @@ -from datetime import datetime import random +import os +from datetime import datetime + + +def parse_inputs(): + bad_words = [] + main_branches = [] + try: + bad_words = os.environ["INPUT_BADWORDS"].split(", ") + except: + bad_words = ["WIP", "work in progress", "in progress", "TODO"] + try: + main_branches = os.environ["INPUT_MAINBRANCHES"].split(", ") + except: + main_branches = ["origin/develop", "origin/master"] + return bad_words, main_branches def strip(s: str): return s.strip().lstrip() From 3d77a1a1f9479fb411af3c56db4c5368bdb87a61 Mon Sep 17 00:00:00 2001 From: gcattan Date: Fri, 1 Apr 2022 21:12:16 +0200 Subject: [PATCH 4/5] update Dockerfile --- Dockerfile | 17 ++++++++--------- example/git-quality-check.py | 1 - .../indicators/commits/count_bad_words.py | 1 - 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/Dockerfile b/Dockerfile index fc917ad..55a8667 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,13 +1,12 @@ FROM python:3.9-slim-buster -ADD git-quality-check.py / +ADD git_quality_check /git_quality_check +ADD example /example +ADD setup.py / +ADD README.md / -# RUN pip install pystrich - -RUN apt-get upgrade -y -RUN apt-get update -RUN apt-get -y install software-properties-common -RUN apt-get upgrade -y RUN apt-get update -RUN apt-add-repository ppa:git-core/ppa -y RUN apt-get -y install git -ENTRYPOINT [ "python", "/git-quality-check.py" ] \ No newline at end of file + +RUN python setup.py develop + +ENTRYPOINT [ "python", "/example/git-quality-check.py" ] \ No newline at end of file diff --git a/example/git-quality-check.py b/example/git-quality-check.py index 49a1b6a..67c099d 100644 --- a/example/git-quality-check.py +++ b/example/git-quality-check.py @@ -1,4 +1,3 @@ -import os from git_quality_check.utils import ( git_logs, git_all_branches, diff --git a/git_quality_check/indicators/commits/count_bad_words.py b/git_quality_check/indicators/commits/count_bad_words.py index c669d66..970c195 100644 --- a/git_quality_check/indicators/commits/count_bad_words.py +++ b/git_quality_check/indicators/commits/count_bad_words.py @@ -1,4 +1,3 @@ - def count_bad_words(bad_words: list[str]): def _count_bad_words(log: str): counter = 0 From baccb45f467d875f8182c8befd53fcfb68055845 Mon Sep 17 00:00:00 2001 From: gcattan Date: Fri, 1 Apr 2022 19:29:59 +0000 Subject: [PATCH 5/5] :art: Format Python code with psf/black --- example/git-quality-check.py | 22 ++++---- git_quality_check/_version.py | 2 +- .../indicators/commits/__init__.py | 2 +- .../indicators/commits/count_bad_words.py | 3 +- .../indicators/commits/is_empty_body.py | 3 +- .../indicators/commits/is_test_commit.py | 2 +- .../commits/not_a_squashed_commit.py | 2 +- .../indicators/counters/__init__.py | 2 +- .../indicators/counters/count_coupled.py | 3 +- .../indicators/counters/count_old_branches.py | 8 ++- .../indicators/counters/process_logs.py | 4 +- git_quality_check/scoring/__init__.py | 2 +- git_quality_check/scoring/overall.py | 12 +++-- git_quality_check/utils/__init__.py | 7 +-- git_quality_check/utils/common.py | 15 +++--- git_quality_check/utils/git.py | 35 +++++++----- setup.py | 53 ++++++++++--------- 17 files changed, 92 insertions(+), 85 deletions(-) diff --git a/example/git-quality-check.py b/example/git-quality-check.py index 67c099d..57000c7 100644 --- a/example/git-quality-check.py +++ b/example/git-quality-check.py @@ -3,13 +3,13 @@ git_all_branches, set_output, format_number, - parse_inputs + parse_inputs, ) from git_quality_check.indicators.counters import ( count_old_branches, count_coupled, - process_logs + process_logs, ) @@ -25,28 +25,26 @@ if __name__ == "__main__": - bad_words, main_branches = parse_inputs() logs = git_logs() branches = git_all_branches() - bad_commit_index = process_logs(logs, [not_a_squashed_commit, - is_empty_body, - count_bad_words(bad_words)]) + bad_commit_index = process_logs( + logs, [not_a_squashed_commit, is_empty_body, count_bad_words(bad_words)] + ) test_index = process_logs(logs, [is_test_commit]) old_branches_index = count_old_branches(branches) coupling_index = count_coupled(branches, main_branches) - + print(bad_commit_index) print(test_index) print(old_branches_index) print(coupling_index) + overall = compute_score( + bad_commit_index, test_index, old_branches_index, coupling_index + ) - - overall = compute_score(bad_commit_index, test_index, - old_branches_index, coupling_index) - - set_output(format_number(overall)) \ No newline at end of file + set_output(format_number(overall)) diff --git a/git_quality_check/_version.py b/git_quality_check/_version.py index ee7834c..9d7ccf0 100644 --- a/git_quality_check/_version.py +++ b/git_quality_check/_version.py @@ -1 +1 @@ -__version__ = 'v0.0-beta' +__version__ = "v0.0-beta" diff --git a/git_quality_check/indicators/commits/__init__.py b/git_quality_check/indicators/commits/__init__.py index 09cb675..6dac9b2 100644 --- a/git_quality_check/indicators/commits/__init__.py +++ b/git_quality_check/indicators/commits/__init__.py @@ -1,4 +1,4 @@ from .is_empty_body import is_empty_body from .not_a_squashed_commit import not_a_squashed_commit from .count_bad_words import count_bad_words -from .is_test_commit import is_test_commit \ No newline at end of file +from .is_test_commit import is_test_commit diff --git a/git_quality_check/indicators/commits/count_bad_words.py b/git_quality_check/indicators/commits/count_bad_words.py index 970c195..59da32e 100644 --- a/git_quality_check/indicators/commits/count_bad_words.py +++ b/git_quality_check/indicators/commits/count_bad_words.py @@ -5,4 +5,5 @@ def _count_bad_words(log: str): if word in log.lower().split(): counter += 1 return counter - return _count_bad_words \ No newline at end of file + + return _count_bad_words diff --git a/git_quality_check/indicators/commits/is_empty_body.py b/git_quality_check/indicators/commits/is_empty_body.py index 0353f33..21b62cf 100644 --- a/git_quality_check/indicators/commits/is_empty_body.py +++ b/git_quality_check/indicators/commits/is_empty_body.py @@ -3,10 +3,11 @@ remove_header, ) + def is_empty_body(log: str): if not is_valid_log(log): return 1 log = remove_header(log) if not is_valid_log(log): return 1 - return 0 \ No newline at end of file + return 0 diff --git a/git_quality_check/indicators/commits/is_test_commit.py b/git_quality_check/indicators/commits/is_test_commit.py index 85b3eb7..50058e3 100644 --- a/git_quality_check/indicators/commits/is_test_commit.py +++ b/git_quality_check/indicators/commits/is_test_commit.py @@ -2,4 +2,4 @@ def is_test_commit(log: str): for word in ["test", "testing"]: if word in log.lower().split(): return 1 - return 0 \ No newline at end of file + return 0 diff --git a/git_quality_check/indicators/commits/not_a_squashed_commit.py b/git_quality_check/indicators/commits/not_a_squashed_commit.py index 320fe08..bce21a8 100644 --- a/git_quality_check/indicators/commits/not_a_squashed_commit.py +++ b/git_quality_check/indicators/commits/not_a_squashed_commit.py @@ -1,4 +1,4 @@ def not_a_squashed_commit(log: str): if "(#" not in log: return 1 - return 0 \ No newline at end of file + return 0 diff --git a/git_quality_check/indicators/counters/__init__.py b/git_quality_check/indicators/counters/__init__.py index be619e5..3ca3f36 100644 --- a/git_quality_check/indicators/counters/__init__.py +++ b/git_quality_check/indicators/counters/__init__.py @@ -1,3 +1,3 @@ from .count_old_branches import count_old_branches from .count_coupled import count_coupled -from .process_logs import process_logs \ No newline at end of file +from .process_logs import process_logs diff --git a/git_quality_check/indicators/counters/count_coupled.py b/git_quality_check/indicators/counters/count_coupled.py index f2b64d6..0b7b665 100644 --- a/git_quality_check/indicators/counters/count_coupled.py +++ b/git_quality_check/indicators/counters/count_coupled.py @@ -1,5 +1,6 @@ from git_quality_check.utils import sample, are_coupled + def count_coupled(branches, main_branches): branches, count = sample(branches, 10) branches.extend(main_branches) @@ -8,4 +9,4 @@ def count_coupled(branches, main_branches): for bA in branches: for bB in branches: counter += 1 if are_coupled(bA, bB) else 0 - return counter / count * 100 \ No newline at end of file + return counter / count * 100 diff --git a/git_quality_check/indicators/counters/count_old_branches.py b/git_quality_check/indicators/counters/count_old_branches.py index ed43a1b..b27c5f2 100644 --- a/git_quality_check/indicators/counters/count_old_branches.py +++ b/git_quality_check/indicators/counters/count_old_branches.py @@ -1,11 +1,9 @@ -from git_quality_check.utils import ( - sample, - is_old -) +from git_quality_check.utils import sample, is_old + def count_old_branches(branches): counter = 0 branches, count = sample(branches, 10) for branch in branches: counter += 1 if is_old(branch) else 0 - return counter / count * 100 \ No newline at end of file + return counter / count * 100 diff --git a/git_quality_check/indicators/counters/process_logs.py b/git_quality_check/indicators/counters/process_logs.py index a3a2ff5..b8963bf 100644 --- a/git_quality_check/indicators/counters/process_logs.py +++ b/git_quality_check/indicators/counters/process_logs.py @@ -1,8 +1,8 @@ -def process_logs(logs: list[str], functions: list[str: int]): +def process_logs(logs: list[str], functions: list[str:int]): counter = 0 count = len(logs) * len(functions) for i in range(len(logs)): log = logs[i] for function in functions: counter += function(log) - return counter / count * 100 \ No newline at end of file + return counter / count * 100 diff --git a/git_quality_check/scoring/__init__.py b/git_quality_check/scoring/__init__.py index f6dd561..12672e1 100644 --- a/git_quality_check/scoring/__init__.py +++ b/git_quality_check/scoring/__init__.py @@ -1 +1 @@ -from .overall import compute_score \ No newline at end of file +from .overall import compute_score diff --git a/git_quality_check/scoring/overall.py b/git_quality_check/scoring/overall.py index f03a6b0..906a153 100644 --- a/git_quality_check/scoring/overall.py +++ b/git_quality_check/scoring/overall.py @@ -1,5 +1,7 @@ -def compute_score(bad_commit_index, test_index, - old_branches_index, coupling_index): - return ((100 - bad_commit_index) + - test_index + (100 - old_branches_index) + - (100 - coupling_index))/4 \ No newline at end of file +def compute_score(bad_commit_index, test_index, old_branches_index, coupling_index): + return ( + (100 - bad_commit_index) + + test_index + + (100 - old_branches_index) + + (100 - coupling_index) + ) / 4 diff --git a/git_quality_check/utils/__init__.py b/git_quality_check/utils/__init__.py index 5ba68b2..808a56d 100644 --- a/git_quality_check/utils/__init__.py +++ b/git_quality_check/utils/__init__.py @@ -7,9 +7,4 @@ git_all_branches, ) -from .common import ( - sample, - set_output, - format_number, - parse_inputs -) \ No newline at end of file +from .common import sample, set_output, format_number, parse_inputs diff --git a/git_quality_check/utils/common.py b/git_quality_check/utils/common.py index d54932c..68a2ec4 100644 --- a/git_quality_check/utils/common.py +++ b/git_quality_check/utils/common.py @@ -1,4 +1,3 @@ - import random import os from datetime import datetime @@ -17,26 +16,30 @@ def parse_inputs(): main_branches = ["origin/develop", "origin/master"] return bad_words, main_branches + def strip(s: str): return s.strip().lstrip() + def get_date(): return datetime.today() + def sample(li: list[str], min: int): count = len(li) - if(count > min): + if count > min: li = random.sample(li, min) count = min return (li, count) -def format_number(number:float): + +def format_number(number: float): return "{0:.2f}".format(number) -def set_output(output:str): + +def set_output(output: str): print(f"::set-output name=score::{output}") + def diff_month(d1, d2): return (d1.year - d2.year) * 12 + d1.month - d2.month - - diff --git a/git_quality_check/utils/git.py b/git_quality_check/utils/git.py index f63261d..447fb5a 100644 --- a/git_quality_check/utils/git.py +++ b/git_quality_check/utils/git.py @@ -1,22 +1,22 @@ import subprocess from datetime import datetime -from .common import ( - get_date, - diff_month, - strip -) +from .common import get_date, diff_month, strip + def is_valid_log(log: str): return not log == "" + def run_git(command: list[str]): command.insert(0, "--no-pager") command.insert(0, "git") return subprocess.check_output(command).decode() + def git_logs(): return run_git(["log"]).split("commit ") + def is_old(branch): branch_date = git_get_branch_date(branch) if not branch_date: @@ -24,13 +24,14 @@ def is_old(branch): date = get_date() return diff_month(date, branch_date) > 2 + def git_all_branches(): ret = run_git(["branch", "-r"]).split("\n") - return [strip(r) for r in ret if not strip(r) == ''] + return [strip(r) for r in ret if not strip(r) == ""] + def are_coupled(branchA: str, branchB: str): - if not is_well_formed_branch(branchA) or\ - not is_well_formed_branch(branchB): + if not is_well_formed_branch(branchA) or not is_well_formed_branch(branchB): return False if branchA == branchB: return False @@ -44,31 +45,37 @@ def are_coupled(branchA: str, branchB: str): return True return False -def is_well_formed_branch(branch:str): + +def is_well_formed_branch(branch: str): return not "->" in branch -def git_get_branch_date(branch:str): + +def git_get_branch_date(branch: str): if not is_well_formed_branch(branch): return None - ret = run_git(["log", "-n", "1", "--date=format:\"%Y-%m-%d\"", branch]).split("Date: ")[1] - ret = ret.replace("\"", "").split("-") + ret = run_git(["log", "-n", "1", '--date=format:"%Y-%m-%d"', branch]).split( + "Date: " + )[1] + ret = ret.replace('"', "").split("-") year = int(strip(ret[0])) month = int(ret[1]) day = int(ret[2].split("\n")[0]) return datetime(year, month, day) + def remove_first_line(log: str): if is_valid_log: try: eol = log.index("\n") - log = log[eol + 1:] + log = log[eol + 1 :] except ValueError: # commit is empty or just contains one line return "" return log + def remove_header(log: str): log = remove_first_line(log) # Hash log = remove_first_line(log) # Author log = remove_first_line(log) # Date - return log \ No newline at end of file + return log diff --git a/setup.py b/setup.py index 9893299..ccb32c5 100644 --- a/setup.py +++ b/setup.py @@ -5,36 +5,37 @@ # get the version (don't import mne here, so dependencies are not needed) version = None -with open(op.join('git_quality_check', '_version.py'), 'r') as fid: +with open(op.join("git_quality_check", "_version.py"), "r") as fid: for line in (line.strip() for line in fid): - if line.startswith('__version__'): - version = line.split('=')[1].strip().strip('\'') + if line.startswith("__version__"): + version = line.split("=")[1].strip().strip("'") break if version is None: - raise RuntimeError('Could not determine version') + raise RuntimeError("Could not determine version") -with open('README.md', 'r', encoding="utf8") as fid: +with open("README.md", "r", encoding="utf8") as fid: long_description = fid.read() -setup(name='git-quality-check', - version=version, - description='A simple tool to evalute the quality of a git repository.', - url='https://github.com/gcattan/git-quality-check', - author='Gregoire Cattan', - author_email='gcattan@hotmail.com', - license='BSD (3-clause)', - packages=find_packages(), - long_description=long_description, - long_description_content_type='text/markdown', - project_urls={ - 'Documentation': 'https://github.com/gcattan/git-quality-check', - 'Source': 'https://github.com/gcattan/git-quality-check', - 'Tracker': 'https://github.com/gcattan/git-quality-check/issues/', - }, - platforms='any', - python_requires=">=3.9", - install_requires=[], - # extras_require={'docs': ['sphinx-gallery', 'sphinx-bootstrap_theme', 'numpydoc', 'mne', 'seaborn'], - # 'tests': ['pytest', 'seaborn', 'flake8', 'mne', 'pooch', 'tqdm']}, - zip_safe=False, +setup( + name="git-quality-check", + version=version, + description="A simple tool to evalute the quality of a git repository.", + url="https://github.com/gcattan/git-quality-check", + author="Gregoire Cattan", + author_email="gcattan@hotmail.com", + license="BSD (3-clause)", + packages=find_packages(), + long_description=long_description, + long_description_content_type="text/markdown", + project_urls={ + "Documentation": "https://github.com/gcattan/git-quality-check", + "Source": "https://github.com/gcattan/git-quality-check", + "Tracker": "https://github.com/gcattan/git-quality-check/issues/", + }, + platforms="any", + python_requires=">=3.9", + install_requires=[], + # extras_require={'docs': ['sphinx-gallery', 'sphinx-bootstrap_theme', 'numpydoc', 'mne', 'seaborn'], + # 'tests': ['pytest', 'seaborn', 'flake8', 'mne', 'pooch', 'tqdm']}, + zip_safe=False, )