From patchwork Wed Dec 21 22:00:05 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 18045 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id CA54FC3213 for ; Wed, 21 Dec 2022 22:00:12 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0DB56633B2; Wed, 21 Dec 2022 23:00:12 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1671660012; bh=0d8Jj3e2ZI94UvyNzHB16Edn84EzgD5w5qGh25RYVTs=; h=To:Date:Subject:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=VPP9j8Z5gvkWh7Z1sjZqkSi8pAOUb2DPgiWGN/j/4ABiDNChRAAKmBm/LrSwq/D8n r+lMTYqFu7kV8Se7drz+B0RyJRBtkQxqHUfxAKPn5ROEq7PkRo8d5mgxK1ZeSYoEss /Wd1Nioc8e0BSRsJWmXC3NDkGNGty0LDwi8xpGfA6jmt6W1+22P2sExlmbd9eFa92S BBVaZ+PvsR1GOS6yIzotn9K464E1Wc8V95Lgc2mLhfj5/ZBHAlr/vC5T1yBkm/oDpS cyzX+raeWIOeqKgUj3C8fkXP5gHZ9vGwoXiETkbFp3WwOVOUIjLCy9jfjfzb0qHJTi +vEFytE1Uc8kQ== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CF5A161F15 for ; Wed, 21 Dec 2022 23:00:10 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="vo5lpAWr"; dkim-atps=neutral Received: from pendragon.ideasonboard.com (213-243-189-158.bb.dnainternet.fi [213.243.189.158]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 3373BFB for ; Wed, 21 Dec 2022 23:00:10 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1671660010; bh=0d8Jj3e2ZI94UvyNzHB16Edn84EzgD5w5qGh25RYVTs=; h=From:To:Subject:Date:From; b=vo5lpAWrvx2Shr28b++YTLWwEMPjjIZNiCEB6xL98cbrmFUHBkX7y6ke6fJL1QTYQ Jqjp5sklsGz99ks9yC1mS1H0rj4thRuIVVgD4tzqzE7rdMsbnT/PyVJSCUFE2aQzeN r6OO8OYP0F7jyjl58/wUormoDjOONxNLDDVn1CcQ= To: libcamera-devel@lists.libcamera.org Date: Thu, 22 Dec 2022 00:00:05 +0200 Message-Id: <20221221220005.1374-1-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.38.2 MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH] utils: checkstyle.py: Add commit title checker X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Laurent Pinchart via libcamera-devel From: Laurent Pinchart Reply-To: Laurent Pinchart Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a commit checker to ensure that commit titles start with a prefix. The commit issue message lists prefix candidates retrieved from the git log. Signed-off-by: Laurent Pinchart --- utils/checkstyle.py | 63 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) base-commit: f66a5c447b65bce774a1bc2d01034f437bf764b5 diff --git a/utils/checkstyle.py b/utils/checkstyle.py index a11d95cc5808..c40b79dd46fc 100755 --- a/utils/checkstyle.py +++ b/utils/checkstyle.py @@ -352,6 +352,69 @@ class HeaderAddChecker(CommitChecker): return issues +class TitleChecker(CommitChecker): + prefix_regex = re.compile(r'[0-9a-f]+ (([a-zA-Z0-9_.-]+: )+)') + release_regex = re.compile(r'libcamera v[0-9]+\.[0-9]+\.[0-9]+') + + @classmethod + def check(cls, commit, top_level): + issues = [] + + title = commit.title + if not TitleChecker.release_regex.fullmatch(title): + prefix_pos = title.find(': ') + if prefix_pos == -1 or prefix_pos == len(title) - 2: + # Find prefix candidates by searching the git history + msgs = subprocess.run(['git', 'log', '--no-decorate', '--oneline', '-n100', '--'] + commit.files(), + stdout=subprocess.PIPE).stdout.decode('utf-8') + prefixes = {} + prefixes_count = 0 + for msg in msgs.splitlines(): + prefix = TitleChecker.prefix_regex.match(msg) + if not prefix: + continue + + prefix = prefix.group(1) + if prefix in prefixes: + prefixes[prefix] += 1 + else: + prefixes[prefix] = 1 + + prefixes_count += 1 + + if prefixes: + # Sort the candidates by number of occurrences and pick the + # best ones. When multiple prefixes are possible without a + # clear winner, we want to display the most common options + # to the user, but without the most unlikely options to + # avoid too long messages. As a heuristic, select enough + # candidates to cover at least 2/3 of the possible + # prefixes, but never more than 4 candidates. + prefixes = list(prefixes.items()) + prefixes.sort(key=lambda x: x[1], reverse=True) + + candidates = [] + candidates_count = 0 + for prefix in prefixes: + candidates.append(f"`{prefix[0]}'") + candidates_count += prefix[1] + if candidates_count >= prefixes_count * 2 / 3 or \ + len(candidates) == 4: + break + + candidates = candidates[:-2] + [' and '.join(candidates[-2:])] + candidates = ', '.join(candidates) + + issue = CommitIssue('Commit title is missing prefix, ' + 'possible candidates are ' + candidates) + else: + issue = CommitIssue('Commit title is missing prefix') + + issues.append(issue) + + return issues + + # ------------------------------------------------------------------------------ # Style Checkers #