Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 1 | # Checks related to the patch's author |
| 2 | # |
| 3 | # Copyright (C) 2016 Intel Corporation |
| 4 | # |
| 5 | # SPDX-License-Identifier: GPL-2.0-only |
| 6 | |
| 7 | import base |
| 8 | import collections |
| 9 | import parse_shortlog |
| 10 | import parse_signed_off_by |
| 11 | import pyparsing |
| 12 | import subprocess |
| 13 | from data import PatchTestInput |
| 14 | |
| 15 | def 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 | |
| 23 | class 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) |