xref: /cloud-hypervisor/scripts/gitlint/rules/TitleStartsWithComponent.py (revision 851ab0ad433608fbfa2ac6d6fb34f0bbedb174fd)
1ea7999e0SRuslan Mstoifrom gitlint.rules import LineRule, RuleViolation, CommitMessageTitle
2ea7999e0SRuslan Mstoiimport re
3ea7999e0SRuslan Mstoi
4ea7999e0SRuslan Mstoi
5ea7999e0SRuslan Mstoiclass TitleStartsWithComponent(LineRule):
6*851ab0adSRuslan Mstoi    """A rule to enforce valid commit message title
7*851ab0adSRuslan Mstoi
8*851ab0adSRuslan Mstoi    Valid title format:
9*851ab0adSRuslan Mstoi    component1[, component2, componentN]: submodule: summary
10*851ab0adSRuslan Mstoi
11*851ab0adSRuslan Mstoi    Title should have at least one component
12*851ab0adSRuslan Mstoi    Components are separated by comma+space: ", "
13*851ab0adSRuslan Mstoi    Components are validated to be in valid_components
14*851ab0adSRuslan Mstoi    Components list is ended by a colon
15*851ab0adSRuslan Mstoi    Submodules are not validated
16*851ab0adSRuslan 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):
30*851ab0adSRuslan 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',
45ea7999e0SRuslan Mstoi            'Jenkinsfile',
46ea7999e0SRuslan Mstoi            'misc',
47ea7999e0SRuslan Mstoi            'net_gen',
48ea7999e0SRuslan Mstoi            'net_util',
49ea7999e0SRuslan Mstoi            'option_parser',
50ea7999e0SRuslan Mstoi            'pci',
51ea7999e0SRuslan Mstoi            'performance-metrics',
52ea7999e0SRuslan Mstoi            'rate_limiter',
53ea7999e0SRuslan Mstoi            'README',
54ea7999e0SRuslan Mstoi            'resources',
55ea7999e0SRuslan Mstoi            'scripts',
56ea7999e0SRuslan Mstoi            'serial_buffer',
57ea7999e0SRuslan Mstoi            'test_data',
58ea7999e0SRuslan Mstoi            'test_infra',
59ea7999e0SRuslan Mstoi            'tests',
60ea7999e0SRuslan Mstoi            'tpm',
61ea7999e0SRuslan Mstoi            'tracer',
62ea7999e0SRuslan Mstoi            'vhost_user_block',
63ea7999e0SRuslan Mstoi            'vhost_user_net',
64ea7999e0SRuslan Mstoi            'virtio-devices',
65ea7999e0SRuslan Mstoi            'vm-allocator',
66ea7999e0SRuslan Mstoi            'vm-device',
67ea7999e0SRuslan Mstoi            'vmm',
68ea7999e0SRuslan Mstoi            'vm-migration',
69*851ab0adSRuslan Mstoi            'vm-virtio')
70ea7999e0SRuslan Mstoi
71*851ab0adSRuslan Mstoi        ptrn_title = re.compile(r'^(.+?):\s(.+)$')
72*851ab0adSRuslan Mstoi        match = ptrn_title.match(line)
73ea7999e0SRuslan Mstoi
74ea7999e0SRuslan Mstoi        if not match:
75ea7999e0SRuslan Mstoi            self.log.debug("Invalid commit title {}", line)
76ea7999e0SRuslan Mstoi            return [RuleViolation(self.id, "Commit title does not comply with "
77ea7999e0SRuslan Mstoi                                  "rule: 'component: change summary'")]
78*851ab0adSRuslan Mstoi        components = match.group(1)
79ea7999e0SRuslan Mstoi        summary = match.group(2)
80*851ab0adSRuslan Mstoi        self.log.debug(f"\nComponents: {components}\nSummary: {summary}")
81ea7999e0SRuslan Mstoi
82*851ab0adSRuslan Mstoi        ptrn_components = re.compile(r',\s')
83*851ab0adSRuslan Mstoi        components_list = re.split(ptrn_components, components)
84*851ab0adSRuslan Mstoi        self.log.debug("components list: %s" % components_list)
85*851ab0adSRuslan Mstoi
86*851ab0adSRuslan Mstoi        for component in components_list:
87ea7999e0SRuslan Mstoi            if component not in valid_components:
88ea7999e0SRuslan Mstoi                return [RuleViolation(self.id,
89ea7999e0SRuslan Mstoi                                      f"Invalid component: {component}, "
90*851ab0adSRuslan Mstoi                                      "\nValid components are: {}".format(
91*851ab0adSRuslan Mstoi                                          " ".join(valid_components)))]
92