DPDK CI discussions
 help / color / mirror / Atom feed
* [PATCH] tools: add branch rebase support to recheck script
@ 2025-06-11 20:58 Patrick Robb
  0 siblings, 0 replies; only message in thread
From: Patrick Robb @ 2025-06-11 20:58 UTC (permalink / raw)
  To: aconole; +Cc: ci, ahassick, zhoumin, shaibran, Patrick Robb

Adding support for key value parameters to the recheck framework. This is
being done specifically to support the branch rebase feature. With this
commit, the rerun_requests.json includes a new arguments section which
currently stores the rebase value, and can store future key value pairs
in the future. This commit does not add a requirement that the user uses
the rebase argument. It is optional.

There are also some small quality of life changes which have been added.

Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>
Signed-off-by: Patrick Robb <probb@iol.unh.edu>
---
 tools/get_reruns.py | 151 +++++++++++++++++++++++++-------------------
 1 file changed, 87 insertions(+), 64 deletions(-)

diff --git a/tools/get_reruns.py b/tools/get_reruns.py
index ab4d900..7c27650 100755
--- a/tools/get_reruns.py
+++ b/tools/get_reruns.py
@@ -7,17 +7,20 @@ import argparse
 import datetime
 import json
 import re
-import requests
-from typing import Dict, List, Optional, Set
+from json import JSONEncoder
+from typing import Dict, List, Set, Optional, Tuple
 
-DPDK_PATCHWORK_EVENTS_API_URL = "http://patches.dpdk.org/api/events/"
+import requests
 
 
-class JSONSetEncoder(json.JSONEncoder):
+class JSONSetEncoder(JSONEncoder):
     """Custom JSON encoder to handle sets.
 
     Pythons json module cannot serialize sets so this custom encoder converts
     them into lists.
+
+    Args:
+        JSONEncoder: JSON encoder from the json python module.
     """
 
     def default(self, input_object):
@@ -33,12 +36,10 @@ class RerunProcessor:
     The idea of this class is to use regex to find certain patterns that
     represent desired contexts to rerun.
 
-    Args:
+    Arguments:
         desired_contexts: List of all contexts to search for in the bodies of
             the comments
         time_since: Get all comments since this timestamp
-        pw_api_url: URL for events endpoint of the patchwork API to use for collecting
-            comments and comment data
 
     Attributes:
         collection_of_retests: A dictionary that maps patch series IDs to the
@@ -47,38 +48,45 @@ class RerunProcessor:
         last_comment_timestamp: timestamp of the most recent comment that was
             processed
     """
+    _VALID_ARGS: Set[str] = set(["rebase"])
 
     _desired_contexts: List[str]
     _time_since: str
-    _pw_api_url: str
     collection_of_retests: Dict[str, Dict[str, Set]] = {}
     last_comment_timestamp: Optional[str] = None
-    # The tag we search for in comments must appear at the start of the line
-    # and is case sensitive. After this tag we expect a comma separated list
-    # of valid DPDK patchwork contexts.
-    #
+    # ^ is start of line
+    # ((?:(?:[\\w-]+=)?[\\w-]+(?:, ?\n?)?)+) is a capture group that gets all
+    #   test labels and key-value pairs after "Recheck-request: "
+    #   (?:[\\w-]+=)? optionally grabs a key followed by an equals sign
+    #       (no space)
+    #       [\\w-] (expanded to "(:?[a-zA-Z0-9-_]+)" ) means 1 more of any
+    #           character in the ranges a-z, A-Z, 0-9, or the characters
+    #               '-' or '_'
+    #       (?:, ?\n?)? means 1 or none of this match group which expects
+    #           exactly 1 comma followed by 1 or no spaces followed by
+    #           1 or no newlines.
     # VALID MATCHES:
     #   Recheck-request: iol-unit-testing, iol-something-else, iol-one-more,
     #   Recheck-request: iol-unit-testing,iol-something-else, iol-one-more
     #   Recheck-request: iol-unit-testing, iol-example, iol-another-example,
     #   more-intel-testing
+    #   Recheck-request: x=y, rebase=latest, iol-unit-testing, iol-additional-example
     # INVALID MATCHES:
     #   Recheck-request: iol-unit-testing,  intel-example-testing
     #   Recheck-request: iol-unit-testing iol-something-else,iol-one-more,
+    #   Recheck-request: iol-unit-testing, rebase = latest
     #   Recheck-request: iol-unit-testing,iol-something-else,iol-one-more,
-    #
     #   more-intel-testing
-    regex: str = "^Recheck-request: ((?:[a-zA-Z0-9-_]+(?:, ?\n?)?)+)"
+    regex: str = "^Recheck-request: ((?:(?:[\\w-]+=)?[\\w-]+(?:, ?\n?)?)+)"
+    last_comment_timestamp: str
 
-    def __init__(
-        self, desired_contexts: List[str], time_since: str, pw_api_url: str
-    ) -> None:
+    def __init__(self, desired_contexts: List[str], time_since: str, multipage: bool) -> None:
         self._desired_contexts = desired_contexts
         self._time_since = time_since
-        self._pw_api_url = pw_api_url
+        self._multipage = multipage
 
     def process_reruns(self) -> None:
-        patchwork_url = f"{self._pw_api_url}?since={self._time_since}"
+        patchwork_url = f"http://patches.dpdk.org/api/events/?since={self._time_since}"
         comment_request_info = []
         for item in [
             "&category=cover-comment-created",
@@ -87,6 +95,12 @@ class RerunProcessor:
             response = requests.get(patchwork_url + item)
             response.raise_for_status()
             comment_request_info.extend(response.json())
+
+            while 'next' in response.links and self._multipage:
+                response = requests.get(response.links['next']['url'])
+                response.raise_for_status()
+                comment_request_info.extend(response.json())
+
         rerun_processor.process_comment_info(comment_request_info)
 
     def process_comment_info(self, list_of_comment_blobs: List[Dict]) -> None:
@@ -138,54 +152,69 @@ class RerunProcessor:
             comment_info.raise_for_status()
             content = comment_info.json()["content"]
 
-            labels_to_rerun = self.get_test_names(content)
+            (args, labels_to_rerun) = self.get_test_names_and_parameters(content)
+
+            # Accept either filtered labels or arguments.
+            if labels_to_rerun or (args and self._VALID_ARGS.issuperset(args.keys())):
+                # Get or insert a new retest request into the dict.
+                self.collection_of_retests[patch_id] = \
+                    self.collection_of_retests.get(
+                        patch_id, {"contexts": set(), "arguments": dict()}
+                    )
 
-            # appending to the list if it already exists, or creating it if it
-            # doesn't
-            if labels_to_rerun:
-                self.collection_of_retests[patch_id] = self.collection_of_retests.get(
-                    patch_id, {"contexts": set()}
-                )
-                self.collection_of_retests[patch_id]["contexts"].update(labels_to_rerun)
+                req = self.collection_of_retests[patch_id]
 
-    def get_test_names(self, email_body: str) -> Set[str]:
+                # Update the fields.
+                req["contexts"].update(labels_to_rerun)
+                req["arguments"].update(args)
+
+    def get_test_names_and_parameters(
+        self, email_body: str
+    ) -> Tuple[Dict[str, str], Set[str]]:
         """Uses the regex in the class to get the information from the email.
 
-        When it gets the test names from the email, it will all be in one
-        capture group. We expect a comma separated list of patchwork labels
-        to be retested.
+        When it gets the test names from the email, it will be split into two
+        capture groups. We expect a comma separated list of patchwork labels
+        to be retested, and another comma separated list of key-value pairs
+        which are arguments for the retest.
 
         Returns:
             A set of contexts found in the email that match your list of
             desired contexts to capture. We use a set here to avoid duplicate
             contexts.
         """
-        rerun_section = re.findall(self.regex, email_body, re.MULTILINE)
-        if not rerun_section:
-            return set()
-        rerun_list = list(map(str.strip, rerun_section[0].split(",")))
-        return set(filter(lambda x: x and x in self._desired_contexts, rerun_list))
+        rerun_list: Set[str] = set()
+        params_dict: Dict[str, str] = dict()
+
+        match: List[str] = re.findall(self.regex, email_body, re.MULTILINE)
+        if match:
+            items: List[str] = list(map(str.strip, match[0].split(",")))
+
+            for item in items:
+                if '=' in item:
+                    sides = item.split('=')
+                    params_dict[sides[0]] = sides[1]
+                else:
+                    rerun_list.add(item)
+
+        return (params_dict, set(filter(lambda x: x in self._desired_contexts, rerun_list)))
 
-    def write_output(self, file_name: str) -> None:
-        """Output class information.
+    def write_to_output_file(self, file_name: str) -> None:
+        """Write class information to a JSON file.
 
         Takes the collection_of_retests and last_comment_timestamp and outputs
-        them into either a json file or stdout.
+        them into a json file.
 
         Args:
-            file_name: Name of the file to write the output to. If this is set
-            to "-" then it will output to stdout.
+            file_name: Name of the file to write the output to.
         """
 
         output_dict = {
             "retests": self.collection_of_retests,
             "last_comment_timestamp": self.last_comment_timestamp,
         }
-        if file_name == "-":
-            print(json.dumps(output_dict, indent=4, cls=JSONSetEncoder))
-        else:
-            with open(file_name, "w") as file:
-                file.write(json.dumps(output_dict, indent=4, cls=JSONSetEncoder))
+        with open(file_name, "w") as file:
+            file.write(json.dumps(output_dict, indent=4, cls=JSONSetEncoder))
 
 
 if __name__ == "__main__":
@@ -195,39 +224,33 @@ if __name__ == "__main__":
         "--time-since",
         dest="time_since",
         required=True,
-        help='Get all patches since this timestamp (yyyy-mm-ddThh:mm:ss.SSSSSS).',
+        help="Get all patches since this many days ago (default: 5)",
     )
     parser.add_argument(
         "--contexts",
         dest="contexts_to_capture",
         nargs="*",
         required=True,
-        help='List of patchwork contexts you would like to capture.',
+        help="List of patchwork contexts you would like to capture",
     )
     parser.add_argument(
         "-o",
         "--out-file",
         dest="out_file",
         help=(
-            'Output file where the list of reruns and the timestamp of the '
-            'last comment in the list of comments is sent. If this is set '
-            'to "-" then it will output to stdout (default: -).'
+            "Output file where the list of reruns and the timestamp of the"
+            "last comment in the list of comments"
+            "(default: rerun_requests.json)."
         ),
-        default="-",
+        default="rerun_requests.json",
     )
     parser.add_argument(
-        "-u",
-        "--patchwork-url",
-        dest="pw_url",
-        help=(
-            'URL for the events endpoint of the patchwork API that will be used to '
-            f'collect retest requests (default: {DPDK_PATCHWORK_EVENTS_API_URL})'
-        ),
-        default=DPDK_PATCHWORK_EVENTS_API_URL
+        "-m",
+        "--multipage",
+        action="store_true",
+        help="When set, searches all pages of patch/cover comments in the query."
     )
     args = parser.parse_args()
-    rerun_processor = RerunProcessor(
-        args.contexts_to_capture, args.time_since, args.pw_url
-    )
+    rerun_processor = RerunProcessor(args.contexts_to_capture, args.time_since, args.multipage)
     rerun_processor.process_reruns()
-    rerun_processor.write_output(args.out_file)
+    rerun_processor.write_to_output_file(args.out_file)
-- 
2.49.0


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2025-06-11 21:04 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-06-11 20:58 [PATCH] tools: add branch rebase support to recheck script Patrick Robb

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).