blob: 0b623b7d17bb0780f7d63f3095552d807d15eac5 [file] [log] [blame]
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001# Checks related to the patch's author
2#
3# Copyright (C) 2016 Intel Corporation
4#
5# SPDX-License-Identifier: GPL-2.0-only
6
7import base
8import collections
9import parse_shortlog
10import parse_signed_off_by
11import pyparsing
12import subprocess
13from data import PatchTestInput
14
15def headlog():
16 output = subprocess.check_output(
17 "cd %s; git log --pretty='%%h#%%aN#%%cD:#%%s' -1" % PatchTestInput.repodir,
18 universal_newlines=True,
19 shell=True
20 )
21 return output.split('#')
22
23class TestMbox(base.Base):
24
25 auh_email = 'auh@auh.yoctoproject.org'
26
27 invalids = [pyparsing.Regex("^Upgrade Helper.+"),
28 pyparsing.Regex(auh_email),
29 pyparsing.Regex("uh@not\.set"),
30 pyparsing.Regex("\S+@example\.com")]
31
32 rexp_detect = pyparsing.Regex('\[\s?YOCTO.*\]')
33 rexp_validation = pyparsing.Regex('\[(\s?YOCTO\s?#\s?(\d+)\s?,?)+\]')
34 revert_shortlog_regex = pyparsing.Regex('Revert\s+".*"')
35 signoff_prog = parse_signed_off_by.signed_off_by
36 revert_shortlog_regex = pyparsing.Regex('Revert\s+".*"')
37 maxlength = 90
38
39 # base paths of main yocto project sub-projects
40 paths = {
41 'oe-core': ['meta-selftest', 'meta-skeleton', 'meta', 'scripts'],
42 'bitbake': ['bitbake'],
43 'documentation': ['documentation'],
44 'poky': ['meta-poky','meta-yocto-bsp'],
45 'oe': ['meta-gpe', 'meta-gnome', 'meta-efl', 'meta-networking', 'meta-multimedia','meta-initramfs', 'meta-ruby', 'contrib', 'meta-xfce', 'meta-filesystems', 'meta-perl', 'meta-webserver', 'meta-systemd', 'meta-oe', 'meta-python']
46 }
47
48 # scripts folder is a mix of oe-core and poky, most is oe-core code except:
49 poky_scripts = ['scripts/yocto-bsp', 'scripts/yocto-kernel', 'scripts/yocto-layer', 'scripts/lib/bsp']
50
51 Project = collections.namedtuple('Project', ['name', 'listemail', 'gitrepo', 'paths'])
52
53 bitbake = Project(name='Bitbake', listemail='bitbake-devel@lists.openembedded.org', gitrepo='http://git.openembedded.org/bitbake/', paths=paths['bitbake'])
54 doc = Project(name='Documentantion', listemail='yocto@yoctoproject.org', gitrepo='http://git.yoctoproject.org/cgit/cgit.cgi/yocto-docs/', paths=paths['documentation'])
55 poky = Project(name='Poky', listemail='poky@yoctoproject.org', gitrepo='http://git.yoctoproject.org/cgit/cgit.cgi/poky/', paths=paths['poky'])
56 oe = Project(name='oe', listemail='openembedded-devel@lists.openembedded.org', gitrepo='http://git.openembedded.org/meta-openembedded/', paths=paths['oe'])
57
58
59 def test_signed_off_by_presence(self):
60 for commit in TestMbox.commits:
61 # skip those patches that revert older commits, these do not required the tag presence
62 if self.revert_shortlog_regex.search_string(commit.shortlog):
63 continue
64 if not self.signoff_prog.search_string(commit.payload):
65 self.fail('Mbox is missing Signed-off-by. Add it manually or with "git commit --amend -s"',
66 commit=commit)
67
68 def test_shortlog_format(self):
69 for commit in TestMbox.commits:
70 shortlog = commit.shortlog
71 if not shortlog.strip():
72 self.skip('Empty shortlog, no reason to execute shortlog format test')
73 else:
74 # no reason to re-check on revert shortlogs
75 if shortlog.startswith('Revert "'):
76 continue
77 try:
78 parse_shortlog.shortlog.parseString(shortlog)
79 except pyparsing.ParseException as pe:
80 self.fail('Commit shortlog (first line of commit message) should follow the format "<target>: <summary>"',
81 commit=commit)
82
83 def test_shortlog_length(self):
84 for commit in TestMbox.commits:
85 # no reason to re-check on revert shortlogs
86 shortlog = commit.shortlog
87 if shortlog.startswith('Revert "'):
88 continue
89 l = len(shortlog)
90 if l > self.maxlength:
91 self.fail('Edit shortlog so that it is %d characters or less (currently %d characters)' % (self.maxlength, l),
92 commit=commit)
93
94 def test_series_merge_on_head(self):
95 self.skip("Merge test is disabled for now")
96 if PatchTestInput.repo.branch != "master":
97 self.skip("Skipping merge test since patch is not intended for master branch. Target detected is %s" % PatchTestInput.repo.branch)
98 if not PatchTestInput.repo.ismerged:
99 commithash, author, date, shortlog = headlog()
100 self.fail('Series does not apply on top of target branch %s' % PatchTestInput.repo.branch,
101 data=[('Targeted branch', '%s (currently at %s)' % (PatchTestInput.repo.branch, commithash))])
102
103 def test_target_mailing_list(self):
104 """In case of merge failure, check for other targeted projects"""
105 if PatchTestInput.repo.ismerged:
106 self.skip('Series merged, no reason to check other mailing lists')
107
108 # a meta project may be indicted in the message subject, if this is the case, just fail
109 # TODO: there may be other project with no-meta prefix, we also need to detect these
110 project_regex = pyparsing.Regex("\[(?P<project>meta-.+)\]")
111 for commit in TestMbox.commits:
112 match = project_regex.search_string(commit.subject)
113 if match:
114 self.fail('Series sent to the wrong mailing list or some patches from the series correspond to different mailing lists',
115 commit=commit)
116
117 for patch in self.patchset:
118 folders = patch.path.split('/')
119 base_path = folders[0]
120 for project in [self.bitbake, self.doc, self.oe, self.poky]:
121 if base_path in project.paths:
122 self.fail('Series sent to the wrong mailing list or some patches from the series correspond to different mailing lists',
123 data=[('Suggested ML', '%s [%s]' % (project.listemail, project.gitrepo)),
124 ('Patch\'s path:', patch.path)])
125
126 # check for poky's scripts code
127 if base_path.startswith('scripts'):
128 for poky_file in self.poky_scripts:
129 if patch.path.startswith(poky_file):
130 self.fail('Series sent to the wrong mailing list or some patches from the series correspond to different mailing lists',
131 data=[('Suggested ML', '%s [%s]' % (self.poky.listemail, self.poky.gitrepo)),('Patch\'s path:', patch.path)])
132
133 def test_mbox_format(self):
134 if self.unidiff_parse_error:
135 self.fail('Series has malformed diff lines. Create the series again using git-format-patch and ensure it applies using git am',
136 data=[('Diff line',self.unidiff_parse_error)])
137
138 def test_commit_message_presence(self):
139 for commit in TestMbox.commits:
140 if not commit.commit_message.strip():
141 self.fail('Please include a commit message on your patch explaining the change', commit=commit)
142
143 def test_bugzilla_entry_format(self):
144 for commit in TestMbox.commits:
145 if not self.rexp_detect.search_string(commit.commit_message):
146 self.skip("No bug ID found")
147 elif not self.rexp_validation.search_string(commit.commit_message):
148 self.fail('Bugzilla issue ID is not correctly formatted - specify it with format: "[YOCTO #<bugzilla ID>]"', commit=commit)
149
150 def test_author_valid(self):
151 for commit in self.commits:
152 for invalid in self.invalids:
153 if invalid.search_string(commit.author):
154 self.fail('Invalid author %s. Resend the series with a valid patch author' % commit.author, commit=commit)
155
156 def test_non_auh_upgrade(self):
157 for commit in self.commits:
158 if self.auh_email in commit.payload:
159 self.fail('Invalid author %s. Resend the series with a valid patch author' % self.auh_email, commit=commit)