123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- # Checks related to the patch's author
- #
- # Copyright (C) 2016 Intel Corporation
- #
- # SPDX-License-Identifier: GPL-2.0-only
- import base
- import collections
- import patchtest_patterns
- import pyparsing
- import re
- import subprocess
- from patchtest_parser import PatchtestParser
- def headlog():
- output = subprocess.check_output(
- "cd %s; git log --pretty='%%h#%%aN#%%cD:#%%s' -1" % PatchtestParser.repodir,
- universal_newlines=True,
- shell=True
- )
- return output.split('#')
- class TestMbox(base.Base):
- # base paths of main yocto project sub-projects
- paths = {
- 'oe-core': ['meta-selftest', 'meta-skeleton', 'meta', 'scripts'],
- 'bitbake': ['bitbake'],
- 'documentation': ['documentation'],
- 'poky': ['meta-poky','meta-yocto-bsp'],
- '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']
- }
- # scripts folder is a mix of oe-core and poky, most is oe-core code except:
- poky_scripts = ['scripts/yocto-bsp', 'scripts/yocto-kernel', 'scripts/yocto-layer', 'scripts/lib/bsp']
- Project = collections.namedtuple('Project', ['name', 'listemail', 'gitrepo', 'paths'])
- bitbake = Project(name='Bitbake', listemail='bitbake-devel@lists.openembedded.org', gitrepo='http://git.openembedded.org/bitbake/', paths=paths['bitbake'])
- doc = Project(name='Documentantion', listemail='yocto@yoctoproject.org', gitrepo='http://git.yoctoproject.org/cgit/cgit.cgi/yocto-docs/', paths=paths['documentation'])
- poky = Project(name='Poky', listemail='poky@yoctoproject.org', gitrepo='http://git.yoctoproject.org/cgit/cgit.cgi/poky/', paths=paths['poky'])
- oe = Project(name='oe', listemail='openembedded-devel@lists.openembedded.org', gitrepo='http://git.openembedded.org/meta-openembedded/', paths=paths['oe'])
- def test_signed_off_by_presence(self):
- for commit in self.commits:
- # skip those patches that revert older commits, these do not required the tag presence
- if patchtest_patterns.mbox_revert_shortlog_regex.search_string(commit.shortlog):
- continue
- if not patchtest_patterns.signed_off_by.search_string(commit.payload):
- self.fail(
- 'Mbox is missing Signed-off-by. Add it manually or with "git commit --amend -s"',
- commit=commit,
- )
- def test_shortlog_format(self):
- for commit in self.commits:
- shortlog = commit.shortlog
- if not shortlog.strip():
- self.skip('Empty shortlog, no reason to execute shortlog format test')
- else:
- # no reason to re-check on revert shortlogs
- if shortlog.startswith('Revert "'):
- continue
- try:
- patchtest_patterns.shortlog.parseString(shortlog)
- except pyparsing.ParseException as pe:
- self.fail('Commit shortlog (first line of commit message) should follow the format "<target>: <summary>"',
- commit=commit)
- def test_shortlog_length(self):
- for commit in self.commits:
- # no reason to re-check on revert shortlogs
- shortlog = re.sub(r'^(\[.*?\])+ ', '', commit.shortlog)
- if shortlog.startswith('Revert "'):
- continue
- l = len(shortlog)
- if l > patchtest_patterns.mbox_shortlog_maxlength:
- self.fail(
- "Edit shortlog so that it is %d characters or less (currently %d characters)"
- % (patchtest_patterns.mbox_shortlog_maxlength, l),
- commit=commit,
- )
- def test_series_merge_on_head(self):
- self.skip("Merge test is disabled for now")
- if PatchtestParser.repo.patch.branch != "master":
- self.skip(
- "Skipping merge test since patch is not intended"
- " for master branch. Target detected is %s"
- % PatchtestParser.repo.patch.branch
- )
- if not PatchtestParser.repo.canbemerged():
- commithash, author, date, shortlog = headlog()
- self.fail(
- "Series does not apply on top of target branch %s"
- % PatchtestParser.repo.patch.branch,
- data=[
- (
- "Targeted branch",
- "%s (currently at %s)"
- % (PatchtestParser.repo.patch.branch, commithash),
- )
- ],
- )
- def test_target_mailing_list(self):
- """Check for other targeted projects"""
- # a meta project may be indicted in the message subject, if this is the case, just fail
- # TODO: there may be other project with no-meta prefix, we also need to detect these
- project_regex = pyparsing.Regex(r"\[(?P<project>meta-.+)\]")
- for commit in self.commits:
- match = project_regex.search_string(commit.subject)
- if match:
- self.fail('Series sent to the wrong mailing list or some patches from the series correspond to different mailing lists',
- commit=commit)
- for patch in self.patchset:
- folders = patch.path.split('/')
- base_path = folders[0]
- for project in [self.bitbake, self.doc, self.oe, self.poky]:
- if base_path in project.paths:
- self.fail('Series sent to the wrong mailing list or some patches from the series correspond to different mailing lists',
- data=[('Suggested ML', '%s [%s]' % (project.listemail, project.gitrepo)),
- ('Patch\'s path:', patch.path)])
- # check for poky's scripts code
- if base_path.startswith('scripts'):
- for poky_file in self.poky_scripts:
- if patch.path.startswith(poky_file):
- self.fail('Series sent to the wrong mailing list or some patches from the series correspond to different mailing lists',
- data=[('Suggested ML', '%s [%s]' % (self.poky.listemail, self.poky.gitrepo)),('Patch\'s path:', patch.path)])
- def test_mbox_format(self):
- if self.unidiff_parse_error:
- self.fail('Series has malformed diff lines. Create the series again using git-format-patch and ensure it applies using git am',
- data=[('Diff line',self.unidiff_parse_error)])
- def test_commit_message_presence(self):
- for commit in self.commits:
- if not commit.commit_message.strip():
- self.fail('Please include a commit message on your patch explaining the change', commit=commit)
- # This may incorrectly report a failure if something such as a
- # Python decorator is included in the commit message, but this
- # scenario is much less common than the username case it is written
- # to protect against
- def test_commit_message_user_tags(self):
- for commit in self.commits:
- if patchtest_patterns.mbox_github_username.search_string(commit.commit_message):
- self.fail('Mbox includes one or more GitHub-style username tags. Ensure that any "@" symbols are stripped out of usernames', commit=commit)
- def test_bugzilla_entry_format(self):
- for commit in self.commits:
- if not patchtest_patterns.mbox_bugzilla.search_string(commit.commit_message):
- self.skip("No bug ID found")
- elif not patchtest_patterns.mbox_bugzilla_validation.search_string(
- commit.commit_message
- ):
- self.fail(
- 'Bugzilla issue ID is not correctly formatted - specify it with format: "[YOCTO #<bugzilla ID>]"',
- commit=commit,
- )
- def test_author_valid(self):
- for commit in self.commits:
- for invalid in patchtest_patterns.invalid_submitters:
- if invalid.search_string(commit.author):
- self.fail('Invalid author %s. Resend the series with a valid patch author' % commit.author, commit=commit)
- def test_non_auh_upgrade(self):
- for commit in self.commits:
- if patchtest_patterns.auh_email in commit.commit_message:
- self.fail(
- "Invalid author %s. Resend the series with a valid patch author"
- % patchtest_patterns.auh_email,
- commit=commit,
- )
|