xref: /cloud-hypervisor/scripts/gitlint/rules/TitleStartsWithComponent.py (revision d580ed55c6b0785aecae9de400f3dab484bb4bd6)
177e04223SPhilipp Schuster# SPDX-License-Identifier: Apache-2.0
277e04223SPhilipp Schuster
3ea7999e0SRuslan Mstoifrom gitlint.rules import LineRule, RuleViolation, CommitMessageTitle
4ea7999e0SRuslan Mstoiimport re
5ea7999e0SRuslan Mstoi
6ea7999e0SRuslan Mstoi
7ea7999e0SRuslan Mstoiclass TitleStartsWithComponent(LineRule):
8851ab0adSRuslan Mstoi    """A rule to enforce valid commit message title
9851ab0adSRuslan Mstoi
10851ab0adSRuslan Mstoi    Valid title format:
11851ab0adSRuslan Mstoi    component1[, component2, componentN]: submodule: summary
12851ab0adSRuslan Mstoi
13851ab0adSRuslan Mstoi    Title should have at least one component
14851ab0adSRuslan Mstoi    Components are separated by comma+space: ", "
15851ab0adSRuslan Mstoi    Components are validated to be in valid_components
16851ab0adSRuslan Mstoi    Components list is ended by a colon
17851ab0adSRuslan Mstoi    Submodules are not validated
18851ab0adSRuslan Mstoi
19ea7999e0SRuslan Mstoi    """
20ea7999e0SRuslan Mstoi
21ea7999e0SRuslan Mstoi    # A rule MUST have a human friendly name
22ea7999e0SRuslan Mstoi    name = "title-has-valid-component"
23ea7999e0SRuslan Mstoi
24ea7999e0SRuslan Mstoi    # A rule MUST have a *unique* id.
25ea7999e0SRuslan Mstoi    # We recommend starting with UL (for User-defined Line-rule)
26ea7999e0SRuslan Mstoi    id = "UL1"
27ea7999e0SRuslan Mstoi
28ea7999e0SRuslan Mstoi    # A line-rule MUST have a target (not required for CommitRules).
29ea7999e0SRuslan Mstoi    target = CommitMessageTitle
30ea7999e0SRuslan Mstoi
31ea7999e0SRuslan Mstoi    def validate(self, line, _commit):
32851ab0adSRuslan Mstoi        valid_components = (
33ea7999e0SRuslan Mstoi            'api_client',
34ea7999e0SRuslan Mstoi            'arch',
35ea7999e0SRuslan Mstoi            'block',
36ea7999e0SRuslan Mstoi            'build',
37ea7999e0SRuslan Mstoi            'ch-remote',
38ea7999e0SRuslan Mstoi            'ci',
39ea7999e0SRuslan Mstoi            'devices',
40ea7999e0SRuslan Mstoi            'docs',
41ea7999e0SRuslan Mstoi            'event_monitor',
42ea7999e0SRuslan Mstoi            'fuzz',
43ea7999e0SRuslan Mstoi            'github',
44ea7999e0SRuslan Mstoi            'gitignore',
4507475d2bSBo Chen            'gitlint',
46ea7999e0SRuslan Mstoi            'hypervisor',
4766f8841bSMuminul Islam            'main',
48ea7999e0SRuslan Mstoi            'misc',
49ea7999e0SRuslan Mstoi            'net_gen',
50ea7999e0SRuslan Mstoi            'net_util',
518a80bea4SRuslan Mstoi            'openapi',
52ea7999e0SRuslan Mstoi            'option_parser',
53ea7999e0SRuslan Mstoi            'pci',
54ea7999e0SRuslan Mstoi            'performance-metrics',
55ea7999e0SRuslan Mstoi            'rate_limiter',
56ea7999e0SRuslan Mstoi            'README',
57ea7999e0SRuslan Mstoi            'resources',
58ea7999e0SRuslan Mstoi            'scripts',
59*d580ed55SPhilipp Schuster            'seccomp',
60ea7999e0SRuslan Mstoi            'serial_buffer',
61ea7999e0SRuslan Mstoi            'test_data',
62ea7999e0SRuslan Mstoi            'test_infra',
63ea7999e0SRuslan Mstoi            'tests',
64ea7999e0SRuslan Mstoi            'tpm',
65ea7999e0SRuslan Mstoi            'tracer',
66ea7999e0SRuslan Mstoi            'vhost_user_block',
67ea7999e0SRuslan Mstoi            'vhost_user_net',
68ea7999e0SRuslan Mstoi            'virtio-devices',
69ea7999e0SRuslan Mstoi            'vm-allocator',
70ea7999e0SRuslan Mstoi            'vm-device',
71ea7999e0SRuslan Mstoi            'vmm',
72ea7999e0SRuslan Mstoi            'vm-migration',
73851ab0adSRuslan Mstoi            'vm-virtio')
74ea7999e0SRuslan Mstoi
75851ab0adSRuslan Mstoi        ptrn_title = re.compile(r'^(.+?):\s(.+)$')
76851ab0adSRuslan Mstoi        match = ptrn_title.match(line)
77ea7999e0SRuslan Mstoi
78ea7999e0SRuslan Mstoi        if not match:
79ea7999e0SRuslan Mstoi            self.log.debug("Invalid commit title {}", line)
80ea7999e0SRuslan Mstoi            return [RuleViolation(self.id, "Commit title does not comply with "
81ea7999e0SRuslan Mstoi                                  "rule: 'component: change summary'")]
82851ab0adSRuslan Mstoi        components = match.group(1)
83ea7999e0SRuslan Mstoi        summary = match.group(2)
84851ab0adSRuslan Mstoi        self.log.debug(f"\nComponents: {components}\nSummary: {summary}")
85ea7999e0SRuslan Mstoi
86851ab0adSRuslan Mstoi        ptrn_components = re.compile(r',\s')
87851ab0adSRuslan Mstoi        components_list = re.split(ptrn_components, components)
88851ab0adSRuslan Mstoi        self.log.debug("components list: %s" % components_list)
89851ab0adSRuslan Mstoi
90851ab0adSRuslan Mstoi        for component in components_list:
91ea7999e0SRuslan Mstoi            if component not in valid_components:
92ea7999e0SRuslan Mstoi                return [RuleViolation(self.id,
93ea7999e0SRuslan Mstoi                                      f"Invalid component: {component}, "
94851ab0adSRuslan Mstoi                                      "\nValid components are: {}".format(
95851ab0adSRuslan Mstoi                                          " ".join(valid_components)))]
96