Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/135969/?format=api
https://patches.dpdk.org/api/patches/135969/?format=api", "web_url": "https://patches.dpdk.org/project/ci/patch/20240118234120.29256-2-ahassick@iol.unh.edu/", "project": { "id": 5, "url": "https://patches.dpdk.org/api/projects/5/?format=api", "name": "CI", "link_name": "ci", "list_id": "ci.dpdk.org", "list_email": "ci@dpdk.org", "web_url": "", "scm_url": "git://dpdk.org/tools/dpdk-ci", "webscm_url": "https://git.dpdk.org/tools/dpdk-ci/", "list_archive_url": "https://inbox.dpdk.org/ci", "list_archive_url_format": "https://inbox.dpdk.org/ci/{}", "commit_url_format": "" }, "msgid": "<20240118234120.29256-2-ahassick@iol.unh.edu>", "list_archive_url": "https://inbox.dpdk.org/ci/20240118234120.29256-2-ahassick@iol.unh.edu", "date": "2024-01-18T23:41:18", "name": "[v3,1/3] tools: Add script to create artifacts", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "2bd64d3ff49fbafba31300c0c69a8a2c61a38bb4", "submitter": { "id": 3127, "url": "https://patches.dpdk.org/api/people/3127/?format=api", "name": "Adam Hassick", "email": "ahassick@iol.unh.edu" }, "delegate": null, "mbox": "https://patches.dpdk.org/project/ci/patch/20240118234120.29256-2-ahassick@iol.unh.edu/mbox/", "series": [ { "id": 30842, "url": "https://patches.dpdk.org/api/series/30842/?format=api", "web_url": "https://patches.dpdk.org/project/ci/list/?series=30842", "date": "2024-01-18T23:41:17", "name": "Add a script to create series artifacts", "version": 3, "mbox": "https://patches.dpdk.org/series/30842/mbox/" } ], "comments": "https://patches.dpdk.org/api/patches/135969/comments/", "check": "pending", "checks": "https://patches.dpdk.org/api/patches/135969/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<ci-bounces@dpdk.org>", "X-Original-To": "patchwork@inbox.dpdk.org", "Delivered-To": "patchwork@inbox.dpdk.org", "Received": [ "from mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 59106438F8;\n\tFri, 19 Jan 2024 00:42:11 +0100 (CET)", "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 537C440EDF;\n\tFri, 19 Jan 2024 00:42:11 +0100 (CET)", "from mail-yw1-f179.google.com (mail-yw1-f179.google.com\n [209.85.128.179])\n by mails.dpdk.org (Postfix) with ESMTP id 4E83C40EDF\n for <ci@dpdk.org>; Fri, 19 Jan 2024 00:42:10 +0100 (CET)", "by mail-yw1-f179.google.com with SMTP id\n 00721157ae682-5ff7a8b5e61so1596517b3.2\n for <ci@dpdk.org>; Thu, 18 Jan 2024 15:42:10 -0800 (PST)", "from pogmachine2.loudonlune.net ([216.212.51.182])\n by smtp.gmail.com with ESMTPSA id\n oq6-20020a05620a610600b0078334ada139sm5625641qkn.7.2024.01.18.15.42.08\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Thu, 18 Jan 2024 15:42:08 -0800 (PST)" ], "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=iol.unh.edu; s=unh-iol; t=1705621329; x=1706226129; darn=dpdk.org;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n :message-id:reply-to;\n bh=nQENkoQAuKCQ6Wr3eWfryzCLhRU/VO+WItkY2Y5+yMM=;\n b=VHU7yipNnJIb98WJZaqPQQwsbIp7HIgWQdrzF8odaOnUVli/5SKdgUPVfB/nUFxhSr\n H75tEW6Z7fuQvJvrMzxXFrVHxLE31VwM4kWlIqPMAoAH5cWYivAbVWBEYvEzBeAEYrY9\n UfYCToaZ32N/wYDXGq0Uf9DgNd1wNge5/TU1s=", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20230601; t=1705621329; x=1706226129;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc\n :subject:date:message-id:reply-to;\n bh=nQENkoQAuKCQ6Wr3eWfryzCLhRU/VO+WItkY2Y5+yMM=;\n b=jjMTgMknenBE3/BskYA3h+mUmSjqAFl44ppNbTFyYGA+cJxAe61Yct/X30YHCw2j3G\n QRFy8tfcDlt5+DuNnSk/QvUw1I41UzGiaJSQmf6gnUBeJUuAiD/X5rexBMOTQQuWoiG6\n Lrz/0WRnshSP4/OIgDTvmoZNN2LsTKF0PdrQMYuZEJlpQ8dvWSJMKqN0zYAu0sFrlNL9\n Jilit7XouoOgb6QEj4z75ywgooyEBiLgAeMAbRXyYo1bGw14+SovJhzNVx3GIj7gPmCC\n x+ju6Ppd3RSSJwRgaA+kMUzDlIVtUfjqMGSlq/I5wOARRGcae2ISyjD93hGFtoA54kkT\n cQZA==", "X-Gm-Message-State": "AOJu0YyRlUk5NG5zGahLwWIaZb01PIPTatMRYbfotdHJ/NPjd32p6AlX\n kZKMt3Bb1V6O9ZIrSvWpCxzFnRiggih7iolKntp+KGlOoCTlegvYmDqMhXDCG7Cu22uX2MZy/FQ\n n4XYX08ZFKT2XAuhshqh4+GhUEPHHXVI8nueOWbLpyxa9fSWUHi/L6D8vHV5jDn6IzSKjMRaqHw\n copsWBxU/FQy5W6mZfv1UAoEToOw==", "X-Google-Smtp-Source": "\n AGHT+IF8bBevTBr+hi72B8EIQHRuo9gXYZ85j1WBQ1yIf0ifW1YrSrQjYFTQ9TgK1PwX1Eskyp0PhA==", "X-Received": "by 2002:a81:6c8f:0:b0:5fb:d2da:63d1 with SMTP id\n h137-20020a816c8f000000b005fbd2da63d1mr1414420ywc.36.1705621329192;\n Thu, 18 Jan 2024 15:42:09 -0800 (PST)", "From": "Adam Hassick <ahassick@iol.unh.edu>", "To": "ci@dpdk.org", "Cc": "Adam Hassick <ahassick@iol.unh.edu>", "Subject": "[PATCH v3 1/3] tools: Add script to create artifacts", "Date": "Thu, 18 Jan 2024 18:41:18 -0500", "Message-ID": "<20240118234120.29256-2-ahassick@iol.unh.edu>", "X-Mailer": "git-send-email 2.43.0", "In-Reply-To": "<20240118234120.29256-1-ahassick@iol.unh.edu>", "References": "<20240118234120.29256-1-ahassick@iol.unh.edu>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "X-BeenThere": "ci@dpdk.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "DPDK CI discussions <ci.dpdk.org>", "List-Unsubscribe": "<https://mails.dpdk.org/options/ci>,\n <mailto:ci-request@dpdk.org?subject=unsubscribe>", "List-Archive": "<http://mails.dpdk.org/archives/ci/>", "List-Post": "<mailto:ci@dpdk.org>", "List-Help": "<mailto:ci-request@dpdk.org?subject=help>", "List-Subscribe": "<https://mails.dpdk.org/listinfo/ci>,\n <mailto:ci-request@dpdk.org?subject=subscribe>", "Errors-To": "ci-bounces@dpdk.org" }, "content": "This script takes in a URL to a series on Patchwork and emits a\ntarball which may be used for running tests.\n\nSigned-off-by: Adam Hassick <ahassick@iol.unh.edu>\n---\n tools/create_series_artifact.py | 468 ++++++++++++++++++++++++++++++++\n 1 file changed, 468 insertions(+)\n create mode 100755 tools/create_series_artifact.py", "diff": "diff --git a/tools/create_series_artifact.py b/tools/create_series_artifact.py\nnew file mode 100755\nindex 0000000..235896c\n--- /dev/null\n+++ b/tools/create_series_artifact.py\n@@ -0,0 +1,468 @@\n+#!/usr/bin/env python3\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright (c) 2024 University of New Hampshire\n+\n+import argparse\n+from dataclasses import dataclass\n+\n+import os\n+from git_pw import api as pw_api\n+import pathlib\n+import pygit2\n+import requests\n+import shutil\n+import subprocess\n+import tarfile\n+from typing import Any, Dict, List, Optional\n+import yaml\n+\n+HELP = \"\"\"This script will create an artifact given a URL to a Patchwork series.\n+Much of the information provided is acquired by this script through the use of a configuration file.\n+This configuration file can be found with the other script configs in the config directory of the CI repo.\n+This default file is located at \"config/artifacts.yml\" in the dpdk-ci repository.\n+\n+More detail and examples can be found in the doc for this script.\n+\n+Example usage:\n+\n+./create_series_artifact.py ../configs/artifacts.yml https://patches.dpdk.org/api/1.3/series/12345/\n+\n+\"\"\"\n+\n+# Map the outputs of pw_maintainers_cli to the names of branches on the\n+# GitHub mirror. This is temporary, and should be moved elsewhere in\n+# the future.\n+BRANCH_NAME_MAP = {\n+ \"next-baseband\": \"next-baseband-for-main\",\n+ \"next-crypto\": \"next-crypto-for-main\",\n+ \"next-eventdev\": \"next-eventdev-for-main\",\n+ \"next-net\": \"next-net-for-main\",\n+ \"next-net-intel\": \"next-net-intel-for-next-net\",\n+ \"next-net-brcm\": \"next-net-brcm-for-next-net\",\n+ \"next-net-mlx\": \"next-net-mlx-for-next-net\",\n+ \"next-net-mrvl\": \"next-net-mrvl-for-main\",\n+ \"next-virtio\": \"next-virtio-for-next-net\",\n+}\n+\n+\n+@dataclass\n+class CreateSeriesParameters(object):\n+ pw_server: str\n+ pw_project: str\n+ pw_token: str\n+ git_user: str\n+ git_email: str\n+ series_url: str\n+ patch_ids: List[int]\n+ labels: List[str]\n+ config: Dict\n+ series: Dict\n+ pw_mcli_script: pathlib.Path\n+ patch_parser_script: pathlib.Path\n+ patch_parser_cfg: pathlib.Path\n+ lzma: bool\n+ output_tarball: pathlib.Path\n+ output_properties: pathlib.Path\n+ no_depends: bool\n+ docs_only: bool\n+\n+\n+class ProjectTree(object):\n+ artifact_path: pathlib.Path\n+ tree: str\n+ commit_id: str\n+ path: pathlib.Path\n+ log_file_path: pathlib.Path\n+ props_file_path: pathlib.Path\n+ data: CreateSeriesParameters\n+ repo: pygit2.Repository\n+ log_buf: List[str]\n+ properties: Dict[str, Any]\n+\n+ def log(self, msg: str):\n+ print(msg)\n+ self.log_buf.append(msg)\n+\n+ def write_log(self):\n+ with open(self.log_file_path, \"w\") as log_file:\n+ log_file.write(\"\\n\".join([msg for msg in self.log_buf]))\n+\n+ def write_properties(self):\n+ with open(self.props_file_path, \"w\") as prop_file:\n+ for key, value in self.properties.items():\n+ prop_file.write(f\"{key}={value}\\n\")\n+\n+ def move_logs(self):\n+ shutil.move(self.log_file_path, pathlib.Path(os.getcwd(), \"log.txt\"))\n+ shutil.move(\n+ self.props_file_path, pathlib.Path(os.getcwd(), self.data.output_properties)\n+ )\n+\n+ def __init__(self, data: CreateSeriesParameters):\n+ self.data = data\n+ self.path = pathlib.Path(os.curdir, \"dpdk\").absolute()\n+ self.log_buf = []\n+ self.log_file_path = pathlib.Path(self.path, \"log.txt\")\n+ self.props_file_path = pathlib.Path(self.path, data.output_properties)\n+ self.tree = \"main\"\n+ self.properties = {}\n+ self.artifact_path = data.output_tarball\n+\n+ # Set properties related to the patch data.\n+ self.set_properties(\n+ patchset_range=f\"{data.patch_ids[0]}-{data.patch_ids[-1]}\",\n+ tags=\" \".join(data.labels),\n+ is_docs_only=str(data.docs_only),\n+ )\n+\n+ if not self.path.exists():\n+ # Find the URL to clone from based on the tree name.\n+ repo_url = self.data.config[\"repo_url\"]\n+\n+ # Pull down the git repo we found.\n+ for i in range(1, 4):\n+ self.log(f\"Cloning the DPDK mirror at: {repo_url} (Attempt {i} of 3)\")\n+ try:\n+ repo = pygit2.clone_repository(repo_url, self.path)\n+ break\n+ except pygit2.GitError as e:\n+ self.log(f\"Failed! Reason: {e}\")\n+ else:\n+ self.log(\"Failed to clone from the upstream repository.\")\n+ exit(1)\n+ else:\n+ # Fetch any new changes.\n+ repo = pygit2.Repository(self.path)\n+ origin = repo.remotes[\"origin\"]\n+ origin.fetch()\n+\n+ self.log(\"Cleaning repository state...\")\n+ repo.state_cleanup()\n+\n+ # Initially, check out to main.\n+ self.repo = repo\n+ self.checkout(\"main\")\n+\n+ self.log(f\"Done: {self.tree} commit {self.commit_id}\")\n+\n+ def checkout(self, branch: str) -> Optional[str]:\n+ \"\"\"\n+ Check out to some branch.\n+ Returns true if successful, false otherwise.\n+ \"\"\"\n+\n+ git_branch = self.repo.lookup_branch(\n+ f\"origin/{branch}\", pygit2.GIT_BRANCH_REMOTE\n+ )\n+\n+ if not git_branch:\n+ self.log(f\"Tried to checkout to non-existant branch: {branch}\")\n+ return None\n+\n+ self.log(f\"Trying to checkout branch: {git_branch.branch_name}\")\n+ reference = self.repo.resolve_refish(git_branch.branch_name)\n+ self.commit_id = str(reference[0].id)\n+ self.repo.reset(reference[0].id, pygit2.GIT_RESET_HARD)\n+ self.repo.checkout(reference[1])\n+ self.tree = branch\n+\n+ self.log(f\"Checked out to {branch} ({self.commit_id})\")\n+\n+ return branch\n+\n+ def guess_git_tree(self) -> Optional[str]:\n+ \"\"\"\n+ Run pw_maintainers_cli to guess the git tree of the patch series we are applying.\n+ Returns None if the pw_maintainers_cli failed.\n+ \"\"\"\n+\n+ if \"id\" not in self.data.series:\n+ raise Exception(\"ID was not found in the series JSON\")\n+\n+ result = subprocess.run(\n+ [\n+ self.data.pw_mcli_script,\n+ \"--type\",\n+ \"series\",\n+ \"--pw-server\",\n+ self.data.pw_server,\n+ \"--pw-project\",\n+ self.data.pw_project,\n+ \"list-trees\",\n+ str(self.data.series[\"id\"]),\n+ ],\n+ stdout=subprocess.PIPE,\n+ stderr=subprocess.PIPE,\n+ cwd=self.path,\n+ env={\n+ \"MAINTAINERS_FILE_PATH\": \"MAINTAINERS\",\n+ \"PW_TOKEN\": self.data.pw_token,\n+ },\n+ )\n+\n+ if result.returncode == 0:\n+ branch = result.stdout.decode().strip()\n+\n+ if branch in [\"main\", \"dpdk\"]:\n+ branch = \"main\"\n+ else:\n+ if branch.startswith(\"dpdk-\"):\n+ branch = branch[5:]\n+\n+ branch = BRANCH_NAME_MAP.get(branch)\n+\n+ return self.checkout(branch)\n+ else:\n+ self.log(\"Failed to guess git tree. Output from pw_maintainers_cli:\")\n+ self.log(result.stdout.decode())\n+ self.log(result.stderr.decode())\n+ return None\n+\n+ def set_properties(self, **kwargs):\n+ for key, value in kwargs.items():\n+ self.properties[key] = value\n+\n+ def apply_patch_series(self) -> bool:\n+ self.set_properties(applied_commit_id=self.commit_id, tree=self.tree)\n+ # Run git-pw to apply the series.\n+\n+ # Configure the tree to point at the given patchwork server and project\n+ self.repo.config[\"pw.server\"] = self.data.pw_server\n+ self.repo.config[\"pw.project\"] = self.data.pw_project\n+ self.repo.config[\"user.email\"] = self.data.git_email\n+ self.repo.config[\"user.name\"] = self.data.git_user\n+\n+ result = subprocess.run(\n+ [\"git\", \"pw\", \"series\", \"apply\", str(self.data.series[\"id\"])],\n+ cwd=self.path,\n+ stdout=subprocess.PIPE,\n+ stderr=subprocess.PIPE,\n+ )\n+\n+ # Write the log from the apply process to disk.\n+ self.log(\"Applying patch...\")\n+ self.log(result.stdout.decode())\n+ self.log(result.stderr.decode())\n+\n+ # Store whether there was an error, and return the flag.\n+ error = result.returncode != 0\n+ self.set_properties(apply_error=error)\n+ return not error\n+\n+ def test_build(self) -> bool:\n+ ninja_result: Optional[subprocess.CompletedProcess] = None\n+ meson_result: subprocess.CompletedProcess = subprocess.run(\n+ [\"meson\", \"setup\", \"build\"],\n+ cwd=self.path,\n+ stdout=subprocess.PIPE,\n+ stderr=subprocess.PIPE,\n+ )\n+\n+ build_error = meson_result.returncode != 0\n+\n+ self.log(\"Running test build...\")\n+ self.log(meson_result.stdout.decode())\n+\n+ if not build_error:\n+ ninja_result = subprocess.run(\n+ [\"ninja\", \"-C\", \"build\"],\n+ cwd=self.path,\n+ stdout=subprocess.PIPE,\n+ stderr=subprocess.PIPE,\n+ )\n+\n+ build_error = build_error or ninja_result.returncode != 0\n+ shutil.rmtree(pathlib.Path(self.path, \"build\"))\n+\n+ self.log(ninja_result.stdout.decode())\n+ self.log(ninja_result.stderr.decode())\n+\n+ self.log(meson_result.stderr.decode())\n+\n+ if build_error:\n+ self.log(\"Test build failed.\")\n+\n+ self.set_properties(build_error=build_error)\n+ return not build_error\n+\n+ def create_tarball(self):\n+ # Copy the logs into the artifact tarball.\n+ self.write_log()\n+ self.write_properties()\n+\n+ # Create a tar archive containing the DPDK sources.\n+ with tarfile.open(\n+ self.artifact_path, mode=\"w:xz\" if self.data.lzma else \"w:gz\"\n+ ) as tar_file:\n+ tar_file.add(self.path, \"dpdk\", recursive=True)\n+\n+ # Move the log file out of the working directory.\n+ self.move_logs()\n+\n+ return True\n+\n+\n+def get_tags(\n+ patch_parser_script: pathlib.Path,\n+ patch_parser_cfg: pathlib.Path,\n+ series: Dict,\n+) -> List[str]:\n+ series_filename = f\"{series['id']}.patch\"\n+\n+ # Pull down the patch series as a single file.\n+ pw_api.download(series[\"mbox\"], None, series_filename)\n+\n+ # Call the patch parser script to obtain the tags\n+ parse_result = subprocess.run(\n+ [patch_parser_script, patch_parser_cfg, series_filename],\n+ stdout=subprocess.PIPE,\n+ stderr=subprocess.PIPE,\n+ )\n+\n+ # Assert that patch parser succeeded.\n+ parse_result.check_returncode()\n+\n+ # Return the output\n+ return parse_result.stdout.decode().splitlines()\n+\n+\n+def parse_args() -> argparse.Namespace:\n+ \"\"\"\n+ Parses the arguments and returns an instance of a dataclass containing parameters\n+ and some derived information.\n+ \"\"\"\n+ parser = argparse.ArgumentParser(\n+ formatter_class=argparse.RawDescriptionHelpFormatter,\n+ description=HELP,\n+ )\n+ parser.add_argument(\n+ \"config\",\n+ type=argparse.FileType(),\n+ help=\"The config file to load. Must be a path to a YAML file.\",\n+ )\n+ parser.add_argument(\"series_url\", type=str, help=\"The URL to a Patchwork series.\")\n+ parser.add_argument(\n+ \"-t\",\n+ \"--pw-token\",\n+ dest=\"pw_token\",\n+ type=str,\n+ help=\"The Patchwork token to use\",\n+ )\n+ parser.add_argument(\n+ \"-l\",\n+ \"--lzma\",\n+ action=\"store_true\",\n+ help=\"When set, use LZMA compression rather than GNU zip compression.\",\n+ )\n+ parser.add_argument(\n+ \"-nd\",\n+ \"--no-depends\",\n+ action=\"store_true\",\n+ help=\"When set, does not acknowledge the Depends-on label.\",\n+ )\n+\n+ return parser.parse_args()\n+\n+\n+def collect_series_info(args: argparse.Namespace) -> CreateSeriesParameters:\n+ # Read the configuration file.\n+ with args.config as config_file:\n+ config = yaml.safe_load(config_file)\n+\n+ pw_server = config[\"patchwork\"][\"server\"]\n+ pw_project = config[\"patchwork\"][\"project\"]\n+ pw_token = args.pw_token or config[\"patchwork\"].get(\"token\")\n+\n+ if not pw_token:\n+ print(\"Failed to obtain the Patchworks token.\")\n+ exit(1)\n+\n+ pw_mcli_script = pathlib.Path(config[\"pw_maintainers_cli\"][\"path\"]).absolute()\n+\n+ git_user = config[\"git\"][\"user\"]\n+ git_email = config[\"git\"][\"email\"]\n+\n+ patch_parser_script = pathlib.Path(config[\"patch_parser\"][\"path\"]).absolute()\n+ patch_parser_cfg = pathlib.Path(config[\"patch_parser\"][\"config\"]).absolute()\n+\n+ if args.lzma:\n+ tarball_name = \"dpdk.tar.xz\"\n+ else:\n+ tarball_name = \"dpdk.tar.gz\"\n+\n+ output_tarball = pathlib.Path(tarball_name)\n+ output_properties = pathlib.Path(f\"{tarball_name}.properties\")\n+\n+ # Pull the series JSON down.\n+ resp = requests.get(args.series_url)\n+ resp.raise_for_status()\n+ series = resp.json()\n+\n+ # Get the labels using the patch parser.\n+ labels = get_tags(patch_parser_script, patch_parser_cfg, series)\n+\n+ # See if this is a documentation-only patch.\n+ docs_only = len(labels) == 1 and labels[0] == \"documentation\"\n+\n+ # Get the patch ids in this patch series.\n+ patch_ids = list(map(lambda x: int(x[\"id\"]), series[\"patches\"]))\n+ patch_ids.sort()\n+\n+ return CreateSeriesParameters(\n+ pw_server=pw_server,\n+ pw_project=pw_project,\n+ pw_token=pw_token,\n+ git_user=git_user,\n+ git_email=git_email,\n+ series_url=args.series_url,\n+ patch_ids=patch_ids,\n+ labels=labels,\n+ config=config,\n+ series=series,\n+ pw_mcli_script=pw_mcli_script,\n+ patch_parser_script=patch_parser_script,\n+ patch_parser_cfg=patch_parser_cfg,\n+ lzma=args.lzma,\n+ output_tarball=output_tarball,\n+ output_properties=output_properties,\n+ no_depends=args.no_depends,\n+ docs_only=docs_only,\n+ )\n+\n+\n+def try_to_apply(tree: ProjectTree) -> bool:\n+ return tree.apply_patch_series() and tree.test_build() and tree.create_tarball()\n+\n+\n+def main() -> int:\n+ data = collect_series_info(parse_args())\n+\n+ # Pull down the DPDK mirror.\n+ tree = ProjectTree(data)\n+\n+ # Try to guess the Git tree for this patchset.\n+ guessed_tree = tree.guess_git_tree()\n+\n+ if not guessed_tree:\n+ print(\"Failed to guess git tree.\")\n+ return 1\n+\n+ # Try to apply this patch.\n+ if not (\n+ try_to_apply(tree) # First, try to apply on the guessed tree.\n+ or guessed_tree != \"main\" # If that fails, and the guessed tree was not main\n+ and tree.checkout(\"main\") # Checkout to main, then\n+ and try_to_apply(tree) # Try to apply on main\n+ ):\n+ tree.write_log()\n+ tree.write_properties()\n+ tree.move_logs()\n+\n+ print(\"FAILURE\")\n+\n+ return 1\n+ return 0\n+\n+\n+if __name__ == \"__main__\":\n+ exit(main())\n", "prefixes": [ "v3", "1/3" ] }{ "id": 135969, "url": "