xref: /cloud-hypervisor/scripts/gitlint/rules/TitleStartsWithComponent.py (revision 8a80bea4c576aa92cf1f2dbd6ced47fa7df1dbc3)
1ea7999e0SRuslan Mstoifrom gitlint.rules import LineRule, RuleViolation, CommitMessageTitle
2ea7999e0SRuslan Mstoiimport re
3ea7999e0SRuslan Mstoi
4ea7999e0SRuslan Mstoi
5ea7999e0SRuslan Mstoiclass TitleStartsWithComponent(LineRule):
6851ab0adSRuslan Mstoi    """A rule to enforce valid commit message title
7851ab0adSRuslan Mstoi
8851ab0adSRuslan Mstoi    Valid title format:
9851ab0adSRuslan Mstoi    component1[, component2, componentN]: submodule: summary
10851ab0adSRuslan Mstoi
11851ab0adSRuslan Mstoi    Title should have at least one component
12851ab0adSRuslan Mstoi    Components are separated by comma+space: ", "
13851ab0adSRuslan Mstoi    Components are validated to be in valid_components
14851ab0adSRuslan Mstoi    Components list is ended by a colon
15851ab0adSRuslan Mstoi    Submodules are not validated
16851ab0adSRuslan Mstoi
17ea7999e0SRuslan Mstoi    """
18ea7999e0SRuslan Mstoi
19ea7999e0SRuslan Mstoi    # A rule MUST have a human friendly name
20ea7999e0SRuslan Mstoi    name = "title-has-valid-component"
21ea7999e0SRuslan Mstoi
22ea7999e0SRuslan Mstoi    # A rule MUST have a *unique* id.
23ea7999e0SRuslan Mstoi    # We recommend starting with UL (for User-defined Line-rule)
24ea7999e0SRuslan Mstoi    id = "UL1"
25ea7999e0SRuslan Mstoi
26ea7999e0SRuslan Mstoi    # A line-rule MUST have a target (not required for CommitRules).
27ea7999e0SRuslan Mstoi    target = CommitMessageTitle
28ea7999e0SRuslan Mstoi
29ea7999e0SRuslan Mstoi    def validate(self, line, _commit):
30851ab0adSRuslan Mstoi        valid_components = (
31ea7999e0SRuslan Mstoi            'api_client',
32ea7999e0SRuslan Mstoi            'arch',
33ea7999e0SRuslan Mstoi            'block',
34ea7999e0SRuslan Mstoi            'build',
35ea7999e0SRuslan Mstoi            'ch-remote',
36ea7999e0SRuslan Mstoi            'ci',
37ea7999e0SRuslan Mstoi            'devices',
38ea7999e0SRuslan Mstoi            'docs',
39ea7999e0SRuslan Mstoi            'event_monitor',
40ea7999e0SRuslan Mstoi            'fuzz',
41ea7999e0SRuslan Mstoi            'github',
42ea7999e0SRuslan Mstoi            'gitignore',
4307475d2bSBo Chen            'gitlint',
44ea7999e0SRuslan Mstoi            'hypervisor',
4566f8841bSMuminul Islam            'main',
46ea7999e0SRuslan Mstoi            'misc',
47ea7999e0SRuslan Mstoi            'net_gen',
48ea7999e0SRuslan Mstoi            'net_util',
49*8a80bea4SRuslan Mstoi            'openapi',
50ea7999e0SRuslan Mstoi            'option_parser',
51ea7999e0SRuslan Mstoi            'pci',
52ea7999e0SRuslan Mstoi            'performance-metrics',
53ea7999e0SRuslan Mstoi            'rate_limiter',
54ea7999e0SRuslan Mstoi            'README',
55ea7999e0SRuslan Mstoi            'resources',
56ea7999e0SRuslan Mstoi            'scripts',
57ea7999e0SRuslan Mstoi            'serial_buffer',
58ea7999e0SRuslan Mstoi            'test_data',
59ea7999e0SRuslan Mstoi            'test_infra',
60ea7999e0SRuslan Mstoi            'tests',
61ea7999e0SRuslan Mstoi            'tpm',
62ea7999e0SRuslan Mstoi            'tracer',
63ea7999e0SRuslan Mstoi            'vhost_user_block',
64ea7999e0SRuslan Mstoi            'vhost_user_net',
65ea7999e0SRuslan Mstoi            'virtio-devices',
66ea7999e0SRuslan Mstoi            'vm-allocator',
67ea7999e0SRuslan Mstoi            'vm-device',
68ea7999e0SRuslan Mstoi            'vmm',
69ea7999e0SRuslan Mstoi            'vm-migration',
70851ab0adSRuslan Mstoi            'vm-virtio')
71ea7999e0SRuslan Mstoi
72851ab0adSRuslan Mstoi        ptrn_title = re.compile(r'^(.+?):\s(.+)$')
73851ab0adSRuslan Mstoi        match = ptrn_title.match(line)
74ea7999e0SRuslan Mstoi
75ea7999e0SRuslan Mstoi        if not match:
76ea7999e0SRuslan Mstoi            self.log.debug("Invalid commit title {}", line)
77ea7999e0SRuslan Mstoi            return [RuleViolation(self.id, "Commit title does not comply with "
78ea7999e0SRuslan Mstoi                                  "rule: 'component: change summary'")]
79851ab0adSRuslan Mstoi        components = match.group(1)
80ea7999e0SRuslan Mstoi        summary = match.group(2)
81851ab0adSRuslan Mstoi        self.log.debug(f"\nComponents: {components}\nSummary: {summary}")
82ea7999e0SRuslan Mstoi
83851ab0adSRuslan Mstoi        ptrn_components = re.compile(r',\s')
84851ab0adSRuslan Mstoi        components_list = re.split(ptrn_components, components)
85851ab0adSRuslan Mstoi        self.log.debug("components list: %s" % components_list)
86851ab0adSRuslan Mstoi
87851ab0adSRuslan Mstoi        for component in components_list:
88ea7999e0SRuslan Mstoi            if component not in valid_components:
89ea7999e0SRuslan Mstoi                return [RuleViolation(self.id,
90ea7999e0SRuslan Mstoi                                      f"Invalid component: {component}, "
91851ab0adSRuslan Mstoi                                      "\nValid components are: {}".format(
92851ab0adSRuslan Mstoi                                          " ".join(valid_components)))]
93