Whamcloud - gitweb
LU-6353 contrib: Remove wireshark plugin
[fs/lustre-release.git] / contrib / scripts / gerrit_checkpatch.py
index 9649978..28b0073 100755 (executable)
@@ -57,6 +57,7 @@ GERRIT_HOST = os.getenv('GERRIT_HOST', 'review.whamcloud.com')
 GERRIT_PROJECT = os.getenv('GERRIT_PROJECT', 'fs/lustre-release')
 GERRIT_BRANCH = os.getenv('GERRIT_BRANCH', 'master')
 GERRIT_AUTH_PATH = os.getenv('GERRIT_AUTH_PATH', 'GERRIT_AUTH')
+GERRIT_CHANGE_NUMBER = os.getenv('GERRIT_CHANGE_NUMBER', None)
 
 # GERRIT_AUTH should contain a single JSON dictionary of the form:
 # {
@@ -70,16 +71,18 @@ GERRIT_AUTH_PATH = os.getenv('GERRIT_AUTH_PATH', 'GERRIT_AUTH')
 # }
 
 CHECKPATCH_PATHS = _getenv_list('CHECKPATCH_PATHS', ['checkpatch.pl'])
+CHECKPATCH_ARGS = os.getenv('CHECKPATCH_ARGS','--show-types -').split(' ')
 CHECKPATCH_IGNORED_FILES = _getenv_list('CHECKPATCH_IGNORED_FILES', [
-        'lustre/contrib/wireshark/packet-lustre.c',
         'lustre/ptlrpc/wiretest.c',
         'lustre/utils/wiretest.c',
         '*.patch'])
 CHECKPATCH_IGNORED_KINDS = _getenv_list('CHECKPATCH_IGNORED_KINDS', [
+        'LASSERT',
+        'LCONSOLE',
         'LEADING_SPACE'])
 REVIEW_HISTORY_PATH = os.getenv('REVIEW_HISTORY_PATH', 'REVIEW_HISTORY')
 STYLE_LINK = os.getenv('STYLE_LINK',
-        'https://wiki.hpdd.intel.com/display/PUB/Coding+Guidelines')
+        'http://wiki.lustre.org/Lustre_Coding_Style_Guidelines')
 
 USE_CODE_REVIEW_SCORE = False
 
@@ -103,7 +106,7 @@ def parse_checkpatch_output(out, path_line_comments, warning_count):
 
         path_comments = path_line_comments.setdefault(path, {})
         line_comments = path_comments.setdefault(line, [])
-        line_comments.append('(style) ' + message)
+        line_comments.append('(style) %s\n' % message)
         warning_count[0] += 1
 
     level = None # 'ERROR', 'WARNING'
@@ -176,14 +179,16 @@ def review_input_and_score(path_line_comments, warning_count):
             'labels': {
                 'Code-Review': code_review_score
                 },
-            'comments': review_comments
+            'comments': review_comments,
+            'notify': 'OWNER',
             }, score
     else:
         return {
             'message': 'Looks good to me.',
             'labels': {
                 'Code-Review': code_review_score
-                }
+                },
+            'notify': 'NONE',
             }, score
 
 
@@ -213,6 +218,7 @@ class Reviewer(object):
         self.post_enabled = True
         self.post_interval = 10
         self.update_interval = 300
+        self.request_timeout = 60
 
     def _debug(self, msg, *args):
         """_"""
@@ -232,8 +238,9 @@ class Reviewer(object):
         """
         url = self._url(path)
         try:
-            res = requests.get(url, auth=self.auth)
-        except requests.exceptions.RequestException as exc:
+            res = requests.get(url, auth=self.auth,
+                               timeout=self.request_timeout)
+        except Exception as exc:
             self._error("cannot GET '%s': exception = %s", url, str(exc))
             return None
 
@@ -257,8 +264,8 @@ class Reviewer(object):
         try:
             res = requests.post(url, data=data,
                                 headers={'Content-Type': 'application/json'},
-                                auth=self.auth)
-        except requests.exceptions.RequestException as exc:
+                                auth=self.auth, timeout=self.request_timeout)
+        except Exception as exc:
             self._error("cannot POST '%s': exception = %s", url, str(exc))
             return False
 
@@ -312,6 +319,20 @@ class Reviewer(object):
         """
         return change_id + ' ' + revision in self.history
 
+    def get_change_by_id(self, change_id):
+        """
+        GET one change by id.
+        """
+        path = ('/changes/' + urllib.quote(self.project, safe='') + '~' +
+                urllib.quote(self.branch, safe='') + '~' + change_id +
+                '?o=CURRENT_REVISION')
+        res = self._get(path)
+        if not res:
+            return None
+
+        # Gerrit uses " )]}'" to guard against XSSI.
+        return json.loads(res.content[5:])
+
     def get_changes(self, query):
         """
         GET a list of ChangeInfo()s for all changes matching query.
@@ -330,7 +351,7 @@ class Reviewer(object):
                 '&o=CURRENT_REVISION')
         res = self._get(path)
         if not res:
-            return None
+            return []
 
         # Gerrit uses " )]}'" to guard against XSSI.
         return json.loads(res.content[5:])
@@ -374,12 +395,12 @@ class Reviewer(object):
 
         return self.decode_patch(res.content)
 
-    def set_review(self, change, revision, review_input):
+    def post_review(self, change, revision, review_input):
         """
         POST review_input for the given revision of change.
         """
         path = '/changes/' + change['id'] + '/revisions/' + revision + '/review'
-        self._debug("set_review: path = '%s'", path)
+        self._debug("post_review: path = '%s'", path)
         return self._post(path, review_input)
 
     def check_patch(self, patch):
@@ -391,7 +412,7 @@ class Reviewer(object):
         warning_count = [0]
 
         for path in CHECKPATCH_PATHS:
-            pipe = subprocess.Popen([path, '--show-types', '-'],
+            pipe = subprocess.Popen([path] + CHECKPATCH_ARGS,
                                     stdin=subprocess.PIPE,
                                     stdout=subprocess.PIPE,
                                     stderr=subprocess.PIPE)
@@ -402,45 +423,54 @@ class Reviewer(object):
 
         return review_input_and_score(path_line_comments, warning_count)
 
-    def review_change(self, change, force=False):
+    def change_needs_review(self, change):
         """
-        Review the current revision of change.
         * Bail if the change isn't open (status is not 'NEW').
-        * GET the current revision from gerrit.
-        * Bail if we've already reviewed it (unless force is True).
-        * Pipe the patch through checkpatch(es).
-        * Save results to review history.
-        * POST review to gerrit.
+        * Bail if we've already reviewed the current revision.
         """
-        self._debug("review_change: change = %s, subject = '%s'",
-                    change['id'], change.get('subject', ''))
-
         status = change.get('status')
         if status != 'NEW':
-            self._debug("review_change: status = %s", status)
+            self._debug("change_needs_review: status = %s", status)
             return False
 
         current_revision = change.get('current_revision')
-        self._debug("review_change: current_revision = '%s'", current_revision)
+        self._debug("change_needs_review: current_revision = '%s'",
+                    current_revision)
         if not current_revision:
             return False
 
         # Have we already checked this revision?
-        if self.in_history(change['id'], current_revision) and not force:
-            self._debug("review_change: already reviewed")
+        if self.in_history(change['id'], current_revision):
+            self._debug("change_needs_review: already reviewed")
             return False
 
+        return True
+
+    def review_change(self, change):
+        """
+        Review the current revision of change.
+        * Pipe the patch through checkpatch(es).
+        * Save results to review history.
+        * POST review to gerrit.
+        """
+        self._debug("review_change: change = %s, subject = '%s'",
+                    change['id'], change.get('subject', ''))
+
+        current_revision = change.get('current_revision')
+        self._debug("change_needs_review: current_revision = '%s'",
+                    current_revision)
+        if not current_revision:
+            return
+
         patch = self.get_patch(change, current_revision)
         if not patch:
             self._debug("review_change: no patch")
-            return False
+            return
 
         review_input, score = self.check_patch(patch)
         self._debug("review_change: score = %d", score)
         self.write_history(change['id'], current_revision, score)
-        self.set_review(change, current_revision, review_input)
-        # Don't POST more than every post_interval seconds.
-        time.sleep(self.post_interval)
+        self.post_review(change, current_revision, review_input)
 
     def update(self):
         """
@@ -455,11 +485,26 @@ class Reviewer(object):
         self._debug("update: got %d open_changes", len(open_changes))
 
         for change in open_changes:
-            self.review_change(change)
+            if self.change_needs_review(change):
+                self.review_change(change)
+                # Don't POST more than every post_interval seconds.
+                time.sleep(self.post_interval)
 
         self.timestamp = new_timestamp
         self.write_history('-', '-', 0)
 
+    def update_single_change(self, change):
+
+        self.load_history()
+
+        open_changes = self.get_changes({'status':'open',
+                                         'change':change})
+        self._debug("update: got %d open_changes", len(open_changes))
+
+        for change in open_changes:
+            if self.change_needs_review(change):
+                self.review_change(change)
+
     def run(self):
         """
         * Load review history.
@@ -476,7 +521,7 @@ class Reviewer(object):
 
 def main():
     """_"""
-    logging.basicConfig(level=logging.DEBUG)
+    logging.basicConfig(format='%(asctime)s %(message)s', level=logging.DEBUG)
 
     with open(GERRIT_AUTH_PATH) as auth_file:
         auth = json.load(auth_file)
@@ -485,7 +530,11 @@ def main():
 
     reviewer = Reviewer(GERRIT_HOST, GERRIT_PROJECT, GERRIT_BRANCH,
                         username, password, REVIEW_HISTORY_PATH)
-    reviewer.run()
+
+    if GERRIT_CHANGE_NUMBER:
+        reviewer.update_single_change(GERRIT_CHANGE_NUMBER)
+    else:
+        reviewer.run()
 
 
 if __name__ == "__main__":