]> git.proxmox.com Git - mirror_edk2.git/blobdiff - BaseTools/Scripts/PatchCheck.py
BaseTools: Remove CYGWIN_NT-5.1-i686 ref from Scripts/PatchCheck.py
[mirror_edk2.git] / BaseTools / Scripts / PatchCheck.py
index 174d442aa70fbc22f6d1ed1f85d40236700402a8..fcdabfc8aceaad567fee8523da1209040201d97b 100755 (executable)
@@ -1,8 +1,9 @@
 ## @file\r
 #  Check a patch for various format issues\r
 #\r
-#  Copyright (c) 2015 - 2019, Intel Corporation. All rights reserved.<BR>\r
+#  Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.<BR>\r
 #  Copyright (C) 2020, Red Hat, Inc.<BR>\r
+#  Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>\r
 #\r
 #  SPDX-License-Identifier: BSD-2-Clause-Patent\r
 #\r
@@ -19,6 +20,8 @@ import re
 import subprocess\r
 import sys\r
 \r
+import email.header\r
+\r
 class Verbose:\r
     SILENT, ONELINE, NORMAL = range(3)\r
     level = NORMAL\r
@@ -26,18 +29,22 @@ class Verbose:
 class EmailAddressCheck:\r
     """Checks an email address."""\r
 \r
-    def __init__(self, email):\r
+    def __init__(self, email, description):\r
         self.ok = True\r
 \r
         if email is None:\r
             self.error('Email address is missing!')\r
             return\r
+        if description is None:\r
+            self.error('Email description is missing!')\r
+            return\r
 \r
+        self.description = "'" + description + "'"\r
         self.check_email_address(email)\r
 \r
     def error(self, *err):\r
         if self.ok and Verbose.level > Verbose.ONELINE:\r
-            print('The email address is not valid:')\r
+            print('The ' + self.description + ' email address is not valid:')\r
         self.ok = False\r
         if Verbose.level < Verbose.NORMAL:\r
             return\r
@@ -75,23 +82,35 @@ class EmailAddressCheck:
             self.error("The email address cannot contain a space: " +\r
                        mo.group(3))\r
 \r
+        if ' via Groups.Io' in name and mo.group(3).endswith('@groups.io'):\r
+            self.error("Email rewritten by lists DMARC / DKIM / SPF: " +\r
+                       email)\r
+\r
 class CommitMessageCheck:\r
     """Checks the contents of a git commit message."""\r
 \r
-    def __init__(self, subject, message):\r
+    def __init__(self, subject, message, author_email):\r
         self.ok = True\r
 \r
         if subject is None and  message is None:\r
             self.error('Commit message is missing!')\r
             return\r
 \r
+        MergifyMerge = False\r
+        if "mergify[bot]@users.noreply.github.com" in author_email:\r
+            if "Merge branch" in subject:\r
+                MergifyMerge = True\r
+\r
         self.subject = subject\r
         self.msg = message\r
 \r
+        print (subject)\r
+\r
         self.check_contributed_under()\r
-        self.check_signed_off_by()\r
-        self.check_misc_signatures()\r
-        self.check_overall_format()\r
+        if not MergifyMerge:\r
+            self.check_signed_off_by()\r
+            self.check_misc_signatures()\r
+            self.check_overall_format()\r
         self.report_message_result()\r
 \r
     url = 'https://github.com/tianocore/tianocore.github.io/wiki/Commit-Message-Format'\r
@@ -174,7 +193,7 @@ class CommitMessageCheck:
             if s[2] != ' ':\r
                 self.error("There should be a space after '" + sig + ":'")\r
 \r
-            EmailAddressCheck(s[3])\r
+            EmailAddressCheck(s[3], sig)\r
 \r
         return sigs\r
 \r
@@ -204,6 +223,8 @@ class CommitMessageCheck:
         for sig in self.sig_types:\r
             self.find_signatures(sig)\r
 \r
+    cve_re = re.compile('CVE-[0-9]{4}-[0-9]{5}[^0-9]')\r
+\r
     def check_overall_format(self):\r
         lines = self.msg.splitlines()\r
 \r
@@ -221,9 +242,26 @@ class CommitMessageCheck:
             self.error('Empty commit message!')\r
             return\r
 \r
-        if count >= 1 and len(lines[0].rstrip()) >= 72:\r
-            self.error('First line of commit message (subject line) ' +\r
-                       'is too long.')\r
+        if count >= 1 and re.search(self.cve_re, lines[0]):\r
+            #\r
+            # If CVE-xxxx-xxxxx is present in subject line, then limit length of\r
+            # subject line to 92 characters\r
+            #\r
+            if len(lines[0].rstrip()) >= 93:\r
+                self.error(\r
+                    'First line of commit message (subject line) is too long (%d >= 93).' %\r
+                    (len(lines[0].rstrip()))\r
+                    )\r
+        else:\r
+            #\r
+            # If CVE-xxxx-xxxxx is not present in subject line, then limit\r
+            # length of subject line to 75 characters\r
+            #\r
+            if len(lines[0].rstrip()) >= 76:\r
+                self.error(\r
+                    'First line of commit message (subject line) is too long (%d >= 76).' %\r
+                    (len(lines[0].rstrip()))\r
+                    )\r
 \r
         if count >= 1 and len(lines[0].strip()) == 0:\r
             self.error('First line of commit message (subject line) ' +\r
@@ -236,8 +274,22 @@ class CommitMessageCheck:
         for i in range(2, count):\r
             if (len(lines[i]) >= 76 and\r
                 len(lines[i].split()) > 1 and\r
-                not lines[i].startswith('git-svn-id:')):\r
-                self.error('Line %d of commit message is too long.' % (i + 1))\r
+                not lines[i].startswith('git-svn-id:') and\r
+                not lines[i].startswith('Reviewed-by') and\r
+                not lines[i].startswith('Acked-by:') and\r
+                not lines[i].startswith('Tested-by:') and\r
+                not lines[i].startswith('Reported-by:') and\r
+                not lines[i].startswith('Suggested-by:') and\r
+                not lines[i].startswith('Signed-off-by:') and\r
+                not lines[i].startswith('Cc:')):\r
+                #\r
+                # Print a warning if body line is longer than 75 characters\r
+                #\r
+                print(\r
+                    'WARNING - Line %d of commit message is too long (%d >= 76).' %\r
+                    (i + 1, len(lines[i]))\r
+                    )\r
+                print(lines[i])\r
 \r
         last_sig_line = None\r
         for i in range(count - 1, 0, -1):\r
@@ -309,7 +361,30 @@ class GitDiffCheck:
                 self.state = PRE_PATCH\r
                 self.filename = line[13:].split(' ', 1)[0]\r
                 self.is_newfile = False\r
-                self.force_crlf = not self.filename.endswith('.sh')\r
+                self.force_crlf = True\r
+                self.force_notabs = True\r
+                if self.filename.endswith('.sh') or \\r
+                    self.filename.startswith('BaseTools/BinWrappers/PosixLike/') or \\r
+                    self.filename.startswith('BaseTools/BinPipWrappers/PosixLike/') or \\r
+                    self.filename == 'BaseTools/BuildEnv':\r
+                    #\r
+                    # Do not enforce CR/LF line endings for linux shell scripts.\r
+                    # Some linux shell scripts don't end with the ".sh" extension,\r
+                    # they are identified by their path.\r
+                    #\r
+                    self.force_crlf = False\r
+                if self.filename == '.gitmodules' or \\r
+                   self.filename == 'BaseTools/Conf/diff.order':\r
+                    #\r
+                    # .gitmodules and diff orderfiles are used internally by git\r
+                    # use tabs and LF line endings.  Do not enforce no tabs and\r
+                    # do not enforce CR/LF line endings.\r
+                    #\r
+                    self.force_crlf = False\r
+                    self.force_notabs = False\r
+                if os.path.basename(self.filename) == 'GNUmakefile' or \\r
+                   os.path.basename(self.filename) == 'Makefile':\r
+                    self.force_notabs = False\r
             elif len(line.rstrip()) != 0:\r
                 self.format_error("didn't find diff command")\r
             self.line_num += 1\r
@@ -323,6 +398,11 @@ class GitDiffCheck:
                 self.binary = True\r
                 if self.is_newfile:\r
                     self.new_bin.append(self.filename)\r
+            elif line.startswith('new file mode 160000'):\r
+                #\r
+                # New submodule.  Do not enforce CR/LF line endings\r
+                #\r
+                self.force_crlf = False\r
             else:\r
                 ok = False\r
                 self.is_newfile = self.newfile_prefix_re.match(line)\r
@@ -394,10 +474,10 @@ class GitDiffCheck:
 \r
         stripped = line.rstrip()\r
 \r
-        if self.force_crlf and eol != '\r\n':\r
+        if self.force_crlf and eol != '\r\n' and (line.find('Subproject commit') == -1):\r
             self.added_line_error('Line ending (%s) is not CRLF' % repr(eol),\r
                                   line)\r
-        if '\t' in line:\r
+        if self.force_notabs and '\t' in line:\r
             self.added_line_error('Tab character used', line)\r
         if len(stripped) < len(line):\r
             self.added_line_error('Trailing whitespace found', line)\r
@@ -447,7 +527,10 @@ class CheckOnePatch:
         self.patch = patch\r
         self.find_patch_pieces()\r
 \r
-        msg_check = CommitMessageCheck(self.commit_subject, self.commit_msg)\r
+        email_check = EmailAddressCheck(self.author_email, 'Author')\r
+        email_ok = email_check.ok\r
+\r
+        msg_check = CommitMessageCheck(self.commit_subject, self.commit_msg, self.author_email)\r
         msg_ok = msg_check.ok\r
 \r
         diff_ok = True\r
@@ -455,7 +538,7 @@ class CheckOnePatch:
             diff_check = GitDiffCheck(self.diff)\r
             diff_ok = diff_check.ok\r
 \r
-        self.ok = msg_ok and diff_ok\r
+        self.ok = email_ok and msg_ok and diff_ok\r
 \r
         if Verbose.level == Verbose.ONELINE:\r
             if self.ok:\r
@@ -528,11 +611,30 @@ class CheckOnePatch:
         else:\r
             self.stat = mo.group('stat')\r
             self.commit_msg = mo.group('commit_message')\r
+        #\r
+        # Parse subject line from email header.  The subject line may be\r
+        # composed of multiple parts with different encodings.  Decode and\r
+        # combine all the parts to produce a single string with the contents of\r
+        # the decoded subject line.\r
+        #\r
+        parts = email.header.decode_header(pmail.get('subject'))\r
+        subject = ''\r
+        for (part, encoding) in parts:\r
+            if encoding:\r
+                part = part.decode(encoding)\r
+            else:\r
+                try:\r
+                    part = part.decode()\r
+                except:\r
+                    pass\r
+            subject = subject + part\r
 \r
-        self.commit_subject = pmail['subject'].replace('\r\n', '')\r
+        self.commit_subject = subject.replace('\r\n', '')\r
         self.commit_subject = self.commit_subject.replace('\n', '')\r
         self.commit_subject = self.subject_prefix_re.sub('', self.commit_subject, 1)\r
 \r
+        self.author_email = pmail['from']\r
+\r
 class CheckGitCommits:\r
     """Reads patches from git based on the specified git revision range.\r
 \r
@@ -570,11 +672,13 @@ class CheckGitCommits:
 \r
     def read_patch_from_git(self, commit):\r
         # Run git to get the commit patch\r
-        return self.run_git('show', '--pretty=email', '--no-textconv', commit)\r
+        return self.run_git('show', '--pretty=email', '--no-textconv',\r
+                            '--no-use-mailmap', commit)\r
 \r
     def read_committer_email_address_from_git(self, commit):\r
         # Run git to get the committer email\r
-        return self.run_git('show', '--pretty=%cn <%ce>', '--no-patch', commit)\r
+        return self.run_git('show', '--pretty=%cn <%ce>', '--no-patch',\r
+                            '--no-use-mailmap', commit)\r
 \r
     def run_git(self, *args):\r
         cmd = [ 'git' ]\r