diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 20:36:56 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 20:36:56 +0000 |
commit | 51de1d8436100f725f3576aefa24a2bd2057bc28 (patch) | |
tree | c6d1d5264b6d40a8d7ca34129f36b7d61e188af3 /ci/lint-commit-msg.py | |
parent | Initial commit. (diff) | |
download | mpv-51de1d8436100f725f3576aefa24a2bd2057bc28.tar.xz mpv-51de1d8436100f725f3576aefa24a2bd2057bc28.zip |
Adding upstream version 0.37.0.upstream/0.37.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ci/lint-commit-msg.py')
-rwxr-xr-x | ci/lint-commit-msg.py | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/ci/lint-commit-msg.py b/ci/lint-commit-msg.py new file mode 100755 index 0000000..4198ed4 --- /dev/null +++ b/ci/lint-commit-msg.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +import os, sys, json, subprocess, re + +def call(cmd) -> str: + sys.stdout.flush() + ret = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, text=True) + return ret.stdout + +lint_rules = {} + +def lint_rule(description: str): + def f(func): + assert func.__name__ not in lint_rules.keys() + lint_rules[func.__name__] = (func, description) + return f + +def get_commit_range() -> str: + if len(sys.argv) > 1: + return sys.argv[1] + # https://github.com/actions/runner/issues/342#issuecomment-590670059 + event_name = os.environ["GITHUB_EVENT_NAME"] + with open(os.environ["GITHUB_EVENT_PATH"], "rb") as f: + event = json.load(f) + if event_name == "push": + if event["created"] or event["forced"]: + print("Skipping logic on branch creation or force-push") + return None + return event["before"] + "..." + event["after"] + elif event_name == "pull_request": + return event["pull_request"]["base"]["sha"] + ".." + event["pull_request"]["head"]["sha"] + +def do_lint(commit_range: str) -> bool: + commits = call(["git", "log", "--pretty=format:%h %s", commit_range]).splitlines() + print(f"Linting {len(commits)} commit(s):") + any_failed = False + for commit in commits: + sha, _, _ = commit.partition(' ') + #print(commit) + body = call(["git", "show", "-s", "--format=%B", sha]).splitlines() + failed = [] + if len(body) == 0: + failed.append("* Commit message must not be empty") + else: + for k, v in lint_rules.items(): + if not v[0](body): + failed.append(f"* {v[1]} [{k}]") + if failed: + any_failed = True + print("-" * 40) + sys.stdout.flush() + subprocess.run(["git", "-P", "show", "-s", sha]) + print("\nhas the following issues:") + print("\n".join(failed)) + print("-" * 40) + return any_failed + +################################################################################ + +NO_PREFIX_WHITELIST = r"^Revert \"(.*)\"|^Release [0-9]|^Update VERSION$" + +@lint_rule("Subject line must contain a prefix identifying the sub system") +def subsystem_prefix(body): + if re.search(NO_PREFIX_WHITELIST, body[0]): + return True + m = re.search(r"^([^:]+):\s", body[0]) + if not m: + return False + # a comma-separated list is okay + s = re.sub(r",\s+", "", m.group(1)) + # but no spaces otherwise + return not " " in s + +@lint_rule("First word after : must be lower case") +def description_lowercase(body): + if re.search(NO_PREFIX_WHITELIST, body[0]): + return True + # Allow all caps for acronyms and such + if re.search(r":\s[A-Z]{2,}\s", body[0]): + return True + return re.search(r":\s+[a-z0-9]", body[0]) + +@lint_rule("Subject line must not end with a full stop") +def no_dot(body): + return not body[0].rstrip().endswith('.') + +@lint_rule("There must be an empty line between subject and extended description") +def empty_line(body): + return len(body) == 1 or body[1].strip() == "" + +# been seeing this one all over github lately, must be the webshits +@lint_rule("Do not use 'conventional commits' style") +def no_cc(body): + return not re.search(r"(?i)^(feat|fix|chore|refactor)[!:(]", body[0]) + +@lint_rule("History must be linear, no merge commits") +def no_merge(body): + return not body[0].startswith("Merge ") + +@lint_rule("Subject line should be shorter than 72 characters") +def line_too_long(body): + revert = re.search(r"^Revert \"(.*)\"", body[0]) + return True if revert else len(body[0]) <= 72 + +@lint_rule("Prefix should not include C file extensions (use `vo_gpu: ...` not `vo_gpu.c: ...`)") +def no_file_exts(body): + return not re.search(r"[a-z0-9]\.[ch]:\s", body[0]) + +################################################################################ + +if __name__ == "__main__": + commit_range = get_commit_range() + if commit_range is None: + exit(0) + print("Commit range:", commit_range) + any_failed = do_lint(commit_range) + exit(1 if any_failed else 0) |