aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Burgess IV <gbiv@google.com>2024-04-02 17:35:47 -0600
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2024-04-08 15:01:40 +0000
commitf710649ffb8b4fd514cc990169e875db8424f22c (patch)
tree9ca5d1494842e659c81dfdb2809b6fea500b6213
parent226bf89b8994bcaf17f7dd48b667d3c2e8e054f3 (diff)
downloadtoolchain-utils-f710649ffb8b4fd514cc990169e875db8424f22c.tar.gz
afdo_tools: move git utilities into cros_utils
These will be useful soon for an llvm patch cleanup script I'm working on. Factor them out to reduce maintenance. BUG=b:332589934 TEST=Unittests & new script Change-Id: I69114cc8df0a94a8edc9525571d1a7d45d04d369 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/5417086 Reviewed-by: Jordan Abrahams-Whitehead <ajordanr@google.com> Commit-Queue: George Burgess <gbiv@chromium.org> Tested-by: George Burgess <gbiv@chromium.org>
-rwxr-xr-xafdo_tools/update_kernel_afdo.py79
-rwxr-xr-xafdo_tools/update_kernel_afdo_test.py54
-rw-r--r--cros_utils/git_utils.py107
-rwxr-xr-xcros_utils/git_utils_test.py73
4 files changed, 193 insertions, 120 deletions
diff --git a/afdo_tools/update_kernel_afdo.py b/afdo_tools/update_kernel_afdo.py
index fd7ae16c..a7e40763 100755
--- a/afdo_tools/update_kernel_afdo.py
+++ b/afdo_tools/update_kernel_afdo.py
@@ -24,11 +24,11 @@ import sys
import tempfile
from typing import Dict, Generator, Iterable, List, Optional, Tuple
+from cros_utils import git_utils
+
# Folks who should be on the R-line of any CLs that get uploaded.
-# Note that `c-compiler-chrome@` is managed by gwsq - it'll replace
-# `R=c-compiler-chrome` with the current detective.
-CL_REVIEWERS = ("c-compiler-chrome@google.com",)
+CL_REVIEWERS = (git_utils.REVIEWER_DETECTIVE,)
# Folks who should be on the CC-line of any CLs that get uploaded.
CL_CC = (
@@ -737,55 +737,24 @@ def commit_new_profiles(
)
-def parse_cl_from_upload_output(upload_output: str) -> str:
- """Returns the CL number in the given upload output."""
- id_regex = re.compile(
- r"^remote:\s+https://chromium-review\S+/\+/(\d+)\s", re.MULTILINE
- )
-
- results = id_regex.findall(upload_output)
- if len(results) != 1:
- raise ValueError(
- f"Wanted exactly one match for {id_regex} in {upload_output!r}; "
- f"found {len(results)}"
- )
- return results[0]
-
-
def upload_head_to_gerrit(
toolchain_utils: Path,
chromeos_tree: Optional[Path],
branch: GitBranch,
):
"""Uploads HEAD to gerrit as a CL, and sets reviewers/CCs."""
- option_list = [f"r={x}" for x in CL_REVIEWERS]
- option_list += (f"cc={x}" for x in CL_CC)
- options = ",".join(option_list)
- run_result = subprocess.run(
- [
- "git",
- "push",
- branch.remote,
- # https://gerrit-review.googlesource.com/Documentation/user-upload.html#reviewers
- # for more info on the `%` params.
- f"HEAD:refs/for/{branch.branch_name}%{options}",
- ],
- cwd=toolchain_utils,
- check=False,
- stdin=subprocess.DEVNULL,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT,
- encoding="utf-8",
- )
-
- logging.info(
- "`git push`ing to %s had this output:\n%s",
+ cl_ids = git_utils.upload_to_gerrit(
+ toolchain_utils,
+ branch.remote,
branch.branch_name,
- run_result.stdout,
+ CL_REVIEWERS,
+ CL_CC,
)
- run_result.check_returncode()
- cl_id = parse_cl_from_upload_output(run_result.stdout)
+ if len(cl_ids) > 1:
+ raise ValueError(f"Unexpected: wanted just one CL upload; got {cl_ids}")
+
+ cl_id = cl_ids[0]
logging.info("Uploaded CL http://crrev.com/c/%s successfully.", cl_id)
if chromeos_tree is None:
@@ -795,29 +764,7 @@ def upload_head_to_gerrit(
)
return
- # To make the life of the reviewers marginally easier, click buttons
- # automatically.
- gerrit_commands = (
- ["gerrit", "label-as", cl_id, "1"],
- ["gerrit", "label-cq", cl_id, "1"],
- ["gerrit", "label-v", cl_id, "1"],
- )
- for cmd in gerrit_commands:
- # Run the gerrit commands inside of toolchain_utils, since `gerrit`
- # needs to be run inside of a ChromeOS tree to work. While
- # `toolchain-utils` can be checked out on its own, that's not how this
- # script is expeted to be used.
- return_code = subprocess.run(
- cmd,
- cwd=chromeos_tree,
- check=False,
- stdin=subprocess.DEVNULL,
- ).returncode
- if return_code:
- logging.warning(
- "Failed to run gerrit command %s. Ignoring.",
- shlex.join(cmd),
- )
+ git_utils.try_set_autosubmit_labels(chromeos_tree, cl_id)
def find_chromeos_tree_root(a_dir: Path) -> Optional[Path]:
diff --git a/afdo_tools/update_kernel_afdo_test.py b/afdo_tools/update_kernel_afdo_test.py
index 79ed9d1c..1f365959 100755
--- a/afdo_tools/update_kernel_afdo_test.py
+++ b/afdo_tools/update_kernel_afdo_test.py
@@ -17,44 +17,6 @@ from unittest import mock
import update_kernel_afdo
-GERRIT_OUTPUT_WITH_ONE_CL = """
-Enumerating objects: 4, done.
-Counting objects: 100% (4/4), done.
-Delta compression using up to 128 threads
-Compressing objects: 100% (2/2), done.
-Writing objects: 100% (3/3), 320 bytes | 106.00 KiB/s, done.
-Total 3 (delta 1), reused 1 (delta 0), pack-reused 0 (from 0)
-remote: Resolving deltas: 100% (1/1)
-remote: Processing changes: refs: 1, new: 1, done
-remote:
-remote: SUCCESS
-remote:
-remote: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/5375204 DO NOT COMMIT [WIP] [NEW]
-remote:
-To https://chromium.googlesource.com/chromiumos/third_party/toolchain-utils
- * [new reference] HEAD -> refs/for/main
-"""
-
-GERRIT_OUTPUT_WITH_TWO_CLS = """
-Enumerating objects: 4, done.
-Counting objects: 100% (4/4), done.
-Delta compression using up to 128 threads
-Compressing objects: 100% (2/2), done.
-Writing objects: 100% (3/3), 320 bytes | 106.00 KiB/s, done.
-Total 3 (delta 1), reused 1 (delta 0), pack-reused 0 (from 0)
-remote: Resolving deltas: 100% (1/1)
-remote: Processing changes: refs: 1, new: 1, done
-remote:
-remote: SUCCESS
-remote:
-remote: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/5375204 DO NOT COMMIT [WIP] [NEW]
-remote: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/5375205 DO NOT COMMIT [WIP] [NEW]
-remote:
-To https://chromium.googlesource.com/chromiumos/third_party/toolchain-utils
- * [new reference] HEAD -> refs/for/main
-"""
-
-
class Test(unittest.TestCase):
"""Tests for update_kernel_afdo."""
@@ -321,22 +283,6 @@ TOTAL: 2 objects, 1234 bytes (1.1KiB)
update_kernel_afdo.write_afdo_descriptor_file(file_path, contents)
)
- def test_cl_parsing_from_gerrit_output(self):
- self.assertEqual(
- update_kernel_afdo.parse_cl_from_upload_output(
- GERRIT_OUTPUT_WITH_ONE_CL
- ),
- "5375204",
- )
-
- with self.assertRaisesRegex(ValueError, ".*; found 0"):
- update_kernel_afdo.parse_cl_from_upload_output("")
-
- with self.assertRaisesRegex(ValueError, ".*; found 2"):
- update_kernel_afdo.parse_cl_from_upload_output(
- GERRIT_OUTPUT_WITH_TWO_CLS
- )
-
def test_repo_autodetects_nothing_if_no_repo_dir(self):
self.assertIsNone(
update_kernel_afdo.find_chromeos_tree_root(
diff --git a/cros_utils/git_utils.py b/cros_utils/git_utils.py
new file mode 100644
index 00000000..1ae02ae0
--- /dev/null
+++ b/cros_utils/git_utils.py
@@ -0,0 +1,107 @@
+# Copyright 2024 The ChromiumOS Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Shared utilities for working with git."""
+
+import logging
+from pathlib import Path
+import re
+import shlex
+import subprocess
+from typing import Iterable, List
+
+
+# Email address used to tag the detective as a reviewer.
+REVIEWER_DETECTIVE = "c-compiler-chrome@google.com"
+
+
+def _parse_cls_from_upload_output(upload_output: str) -> List[int]:
+ """Returns the CL number in the given upload output."""
+ id_regex = re.compile(
+ r"^remote:\s+https://chromium-review\S+/\+/(\d+)\s", re.MULTILINE
+ )
+
+ results = id_regex.findall(upload_output)
+ if not results:
+ raise ValueError(
+ f"Wanted at least one match for {id_regex} in {upload_output!r}; "
+ "found 0"
+ )
+ return [int(x) for x in results]
+
+
+def upload_to_gerrit(
+ git_repo: Path,
+ remote: str,
+ branch: str,
+ reviewers: Iterable[str] = (),
+ cc: Iterable[str] = (),
+ ref: str = "HEAD",
+) -> List[int]:
+ """Uploads `ref` to gerrit, optionally adding reviewers/CCs."""
+ # https://gerrit-review.googlesource.com/Documentation/user-upload.html#reviewers
+ # for more info on the `%` params.
+ option_list = [f"r={x}" for x in reviewers]
+ option_list += (f"cc={x}" for x in cc)
+ if option_list:
+ trailing_options = "%" + ",".join(option_list)
+ else:
+ trailing_options = ""
+
+ run_result = subprocess.run(
+ [
+ "git",
+ "push",
+ remote,
+ # https://gerrit-review.googlesource.com/Documentation/user-upload.html#reviewers
+ # for more info on the `%` params.
+ f"{ref}:refs/for/{branch}{trailing_options}",
+ ],
+ cwd=git_repo,
+ check=False,
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ encoding="utf-8",
+ )
+
+ logging.info(
+ "`git push`ing %s to %s/%s had this output:\n%s",
+ ref,
+ remote,
+ branch,
+ run_result.stdout,
+ )
+ run_result.check_returncode()
+ return _parse_cls_from_upload_output(run_result.stdout)
+
+
+def try_set_autosubmit_labels(chromeos_tree: Path, cl_id: int) -> None:
+ """Sets autosubmit on a CL. Logs - not raises - on failure.
+
+ This sets a series of convenience labels on the given cl_number, so landing
+ it (e.g., for the detective) is as easy as possible.
+ """
+ gerrit_cl_id = str(cl_id)
+ gerrit_commands = (
+ ["gerrit", "label-as", gerrit_cl_id, "1"],
+ ["gerrit", "label-cq", gerrit_cl_id, "1"],
+ ["gerrit", "label-v", gerrit_cl_id, "1"],
+ )
+ for cmd in gerrit_commands:
+ # Run the gerrit commands inside of toolchain_utils, since `gerrit`
+ # needs to be run inside of a ChromeOS tree to work. While
+ # `toolchain-utils` can be checked out on its own, that's not how this
+ # script is expeted to be used.
+ return_code = subprocess.run(
+ cmd,
+ cwd=chromeos_tree,
+ check=False,
+ stdin=subprocess.DEVNULL,
+ ).returncode
+ if return_code:
+ logging.warning(
+ "Failed to run gerrit command %s. Ignoring.",
+ shlex.join(cmd),
+ )
diff --git a/cros_utils/git_utils_test.py b/cros_utils/git_utils_test.py
new file mode 100755
index 00000000..2edf1591
--- /dev/null
+++ b/cros_utils/git_utils_test.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python3
+# Copyright 2024 The ChromiumOS Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Tests for git_utils."""
+
+import unittest
+
+from cros_utils import git_utils
+
+
+# pylint: disable=protected-access
+
+GERRIT_OUTPUT_WITH_ONE_CL = """
+Enumerating objects: 4, done.
+Counting objects: 100% (4/4), done.
+Delta compression using up to 128 threads
+Compressing objects: 100% (2/2), done.
+Writing objects: 100% (3/3), 320 bytes | 106.00 KiB/s, done.
+Total 3 (delta 1), reused 1 (delta 0), pack-reused 0 (from 0)
+remote: Resolving deltas: 100% (1/1)
+remote: Processing changes: refs: 1, new: 1, done
+remote:
+remote: SUCCESS
+remote:
+remote: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/5375204 DO NOT COMMIT [WIP] [NEW]
+remote:
+To https://chromium.googlesource.com/chromiumos/third_party/toolchain-utils
+ * [new reference] HEAD -> refs/for/main
+"""
+
+GERRIT_OUTPUT_WITH_TWO_CLS = """
+Enumerating objects: 4, done.
+Counting objects: 100% (4/4), done.
+Delta compression using up to 128 threads
+Compressing objects: 100% (2/2), done.
+Writing objects: 100% (3/3), 320 bytes | 106.00 KiB/s, done.
+Total 3 (delta 1), reused 1 (delta 0), pack-reused 0 (from 0)
+remote: Resolving deltas: 100% (1/1)
+remote: Processing changes: refs: 1, new: 1, done
+remote:
+remote: SUCCESS
+remote:
+remote: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/5375204 DO NOT COMMIT [WIP] [NEW]
+remote: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/5375205 DO NOT COMMIT [WIP] [NEW]
+remote:
+To https://chromium.googlesource.com/chromiumos/third_party/toolchain-utils
+ * [new reference] HEAD -> refs/for/main
+"""
+
+
+class Test(unittest.TestCase):
+ """Tests for git_utils."""
+
+ def test_cl_parsing_complains_if_no_output(self):
+ with self.assertRaisesRegex(ValueError, ".*; found 0"):
+ git_utils._parse_cls_from_upload_output("")
+
+ def test_cl_parsing_works_with_one_cl(self):
+ self.assertEqual(
+ git_utils._parse_cls_from_upload_output(GERRIT_OUTPUT_WITH_ONE_CL),
+ [5375204],
+ )
+
+ def test_cl_parsing_works_with_two_cls(self):
+ self.assertEqual(
+ git_utils._parse_cls_from_upload_output(GERRIT_OUTPUT_WITH_TWO_CLS),
+ [5375204, 5375205],
+ )
+
+
+if __name__ == "__main__":
+ unittest.main()