From aa837c8abcfb183d74dee23c35c675c0af3c21cd Mon Sep 17 00:00:00 2001 From: Matthias Dellweg Date: Tue, 10 Dec 2024 16:28:57 +0100 Subject: [PATCH] Look for WIP in commit messages --- .../.ci/scripts/validate_commit_message.py.j2 | 55 ++++++++++++------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/templates/github/.ci/scripts/validate_commit_message.py.j2 b/templates/github/.ci/scripts/validate_commit_message.py.j2 index 38e92e37..ae0d7ad5 100644 --- a/templates/github/.ci/scripts/validate_commit_message.py.j2 +++ b/templates/github/.ci/scripts/validate_commit_message.py.j2 @@ -1,36 +1,52 @@ {% include 'header.j2' %} +import os import re +import subprocess import sys +import tomllib from pathlib import Path -import subprocess -import os -import warnings + from github import Github -CHANGELOG_EXTS = [".feature", ".bugfix", ".doc", ".removal", ".misc", ".deprecation"] +with open("pyproject.toml", "rb") as fp: + PYPROJECT_TOML = tomllib.load(fp) KEYWORDS = ["fixes", "closes"] +BLOCKING_REGEX = [ + "DRAFT", + "WIP", + "NOMERGE", + r"DO\s*NOT\s*MERGE", + "EXPERIMENT", +] +try: + CHANGELOG_EXTS = [f".{item['directory']}" for item in PYPROJECT_TOML["tool"]["towncrier"]["type"]] +except KeyError: + CHANGELOG_EXTS = [".feature", ".bugfix", ".doc", ".removal", ".misc"] +NOISSUE_MARKER = "[noissue]" sha = sys.argv[1] message = subprocess.check_output(["git", "log", "--format=%B", "-n 1", sha]).decode("utf-8") +if NOISSUE_MARKER in message: + sys.exit(f"Do not add '{NOISSUE_MARKER}' in the commit message.") + +if any((re.match(pattern, message) for pattern in BLOCKING_REGEX)): + sys.exit("This PR is not ready for consumption.") + g = Github(os.environ.get("GITHUB_TOKEN")) repo = g.get_repo("pulp/{{ plugin_name }}") -def __check_status(issue): +def check_status(issue): gi = repo.get_issue(int(issue)) if gi.pull_request: sys.exit(f"Error: issue #{issue} is a pull request.") - if gi.closed_at and "cherry picked from commit" not in message: - warnings.warn( - "When backporting, use the -x flag to append a line that says " - "'(cherry picked from commit ...)' to the original commit message." - ) + if gi.closed_at: sys.exit(f"Error: issue #{issue} is closed.") -def __check_changelog(issue): +def check_changelog(issue): matches = list(Path("CHANGES").rglob(f"{issue}.*")) if len(matches) < 1: @@ -38,21 +54,20 @@ def __check_changelog(issue): for match in matches: if match.suffix not in CHANGELOG_EXTS: sys.exit(f"Invalid extension for changelog entry '{match}'.") - if match.suffix == ".feature" and "cherry picked from commit" in message: - sys.exit(f"Can not backport '{match}' as it is a feature.") print("Checking commit message for {sha}.".format(sha=sha[0:7])) # validate the issue attached to the commit -regex = r"(?:{keywords})[\s:]+#(\d+)".format(keywords=("|").join(KEYWORDS)) -pattern = re.compile(regex, re.IGNORECASE) - -issues = pattern.findall(message) +issue_regex = r"(?:{keywords})[\s:]+#(\d+)".format(keywords=("|").join(KEYWORDS)) +issues = re.findall(issue_regex, message, re.IGNORECASE) +cherry_pick_regex = r"^\s*\(cherry picked from commit [0-9a-f]*\)\s*$" +cherry_pick = re.search(cherry_pick_regex, message, re.MULTILINE) if issues: - for issue in pattern.findall(message): - __check_status(issue) - __check_changelog(issue) + for issue in issues: + if not cherry_pick: + check_status(issue) + check_changelog(issue) print("Commit message for {sha} passed.".format(sha=sha[0:7]))