diff options
author | George Burgess IV <gbiv@google.com> | 2024-04-16 12:40:54 -0600 |
---|---|---|
committer | Chromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2024-04-18 18:59:24 +0000 |
commit | 3ea4177ab04f9ed248f5f90ed3e0a5ff82efc9c0 (patch) | |
tree | 09a53abc4300b7525ac536f42978612f5a5b8aeb | |
parent | 98826b0c2996fd795d5d991ed023d7846c5936a8 (diff) | |
download | toolchain-utils-3ea4177ab04f9ed248f5f90ed3e0a5ff82efc9c0.tar.gz |
llvm_tools: overhaul update_packages_and_run_tests
This is a full rewrite, dropping a lot of the no-longer-needed
functionality here on the floor.
BUG=b:333462347
TEST=crrev.com/i/7190043 and crrev.com/c/5459856
Change-Id: Ia997bbb986660aaa3a1bfe644959cb847c6da41b
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/5458719
Reviewed-by: Jordan Abrahams-Whitehead <ajordanr@google.com>
Commit-Queue: George Burgess <gbiv@chromium.org>
Tested-by: George Burgess <gbiv@chromium.org>
-rw-r--r-- | cros_utils/git_utils.py | 52 | ||||
-rw-r--r-- | llvm_tools/manifest_utils.py | 39 | ||||
-rwxr-xr-x | llvm_tools/update_packages_and_run_tests.py | 824 | ||||
-rwxr-xr-x | llvm_tools/update_packages_and_run_tests_unittest.py | 568 | ||||
-rwxr-xr-x | llvm_tools/upload_llvm_testing_helper_cl.py | 34 |
5 files changed, 426 insertions, 1091 deletions
diff --git a/cros_utils/git_utils.py b/cros_utils/git_utils.py index f249db90..cf299b77 100644 --- a/cros_utils/git_utils.py +++ b/cros_utils/git_utils.py @@ -17,6 +17,11 @@ from typing import Generator, Iterable, List # Email address used to tag the detective as a reviewer. REVIEWER_DETECTIVE = "c-compiler-chrome@google.com" +# Default git naming conventions throughout ChromeOS. +CROS_EXTERNAL_REMOTE = "cros" +CROS_INTERNAL_REMOTE = "cros-internal" +CROS_MAIN_BRANCH = "main" + def _parse_cls_from_upload_output(upload_output: str) -> List[int]: """Returns the CL number in the given upload output.""" @@ -157,3 +162,50 @@ def create_worktree(git_directory: Path) -> Generator[Path, None, None]: check=False, stdin=subprocess.DEVNULL, ) + + +def resolve_ref(git_dir: Path, ref: str) -> str: + """Resolves the given ref or SHA shorthand to a full SHA. + + Raises: + subprocess.CalledProcessError if resolution fails + """ + return subprocess.run( + ["git", "rev-parse", ref], + check=True, + cwd=git_dir, + stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, + encoding="utf-8", + ).stdout.strip() + + +def commit_all_changes(git_dir: Path, message: str) -> str: + """Commits all changes in `git_dir`, with the given commit message. + + This also commits any untracked files in `git_dir`. + + Args: + git_dir: Anywhere in the git directory in which changes should be + committed. + message: Message of the commit message. + + Returns: + The SHA of the committed change. + """ + # Explicitly add using `git add -A`, since that stages all unstaged changes + # & adds any files that aren't tracked. `git commit -a` skips adding + # untracked files. + subprocess.run( + ["git", "add", "-A"], + check=True, + cwd=git_dir, + stdin=subprocess.DEVNULL, + ) + subprocess.run( + ["git", "commit", "-m", message], + check=True, + cwd=git_dir, + stdin=subprocess.DEVNULL, + ) + return resolve_ref(git_dir, "HEAD") diff --git a/llvm_tools/manifest_utils.py b/llvm_tools/manifest_utils.py index e53afa6d..48159b0f 100644 --- a/llvm_tools/manifest_utils.py +++ b/llvm_tools/manifest_utils.py @@ -86,6 +86,20 @@ def extract_current_llvm_hash_from_xml(xmlroot: ElementTree.Element) -> str: return revision +def update_chromeos_manifest_in_manifest_dir( + revision: str, manifest_dir: Path +) -> Path: + """update_chromeos_manifest, taking the directory to manifest-interal.""" + manifest_path = get_chromeos_manifest_path_from_manifest_dir(manifest_dir) + parser = make_xmlparser() + xmltree = ElementTree.parse(manifest_path, parser) + update_chromeos_manifest_tree(revision, xmltree.getroot()) + with atomic_write_file.atomic_write(manifest_path, mode="wb") as f: + xmltree.write(f, encoding="utf-8") + format_manifest(manifest_path) + return manifest_path + + def update_chromeos_manifest(revision: str, src_tree: Path) -> Path: """Replaces the manifest project revision with 'revision'. @@ -107,19 +121,26 @@ def update_chromeos_manifest(revision: str, src_tree: Path) -> Path: UpdateManifestError: The manifest could not be changed. FormattingError: The manifest could not be reformatted. """ - manifest_path = get_chromeos_manifest_path(src_tree) - parser = make_xmlparser() - xmltree = ElementTree.parse(manifest_path, parser) - update_chromeos_manifest_tree(revision, xmltree.getroot()) - with atomic_write_file.atomic_write(manifest_path, mode="wb") as f: - xmltree.write(f, encoding="utf-8") - format_manifest(manifest_path) - return manifest_path + return update_chromeos_manifest_in_manifest_dir( + revision, get_manifest_internal_path(src_tree) + ) + + +def get_manifest_internal_path(src_tree: Path) -> Path: + """Return the path to manifest-internal inside of the CrOS tree.""" + return src_tree / "manifest-internal" def get_chromeos_manifest_path(src_tree: Path) -> Path: """Return the path to the toolchain manifest.""" - return src_tree / "manifest-internal" / "_toolchain.xml" + return get_chromeos_manifest_path_from_manifest_dir( + get_manifest_internal_path(src_tree) + ) + + +def get_chromeos_manifest_path_from_manifest_dir(manifest_dir: Path) -> Path: + """Return the path to the toolchain manifest in a manifest dir.""" + return manifest_dir / "_toolchain.xml" def update_chromeos_manifest_tree(revision: str, xmlroot: ElementTree.Element): diff --git a/llvm_tools/update_packages_and_run_tests.py b/llvm_tools/update_packages_and_run_tests.py index 34837630..7bb127eb 100755 --- a/llvm_tools/update_packages_and_run_tests.py +++ b/llvm_tools/update_packages_and_run_tests.py @@ -1,603 +1,359 @@ #!/usr/bin/env python3 -# Copyright 2019 The ChromiumOS Authors +# 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. -"""Runs a tryjob/tryjobs after updating the packages.""" +"""Uploads CLs necessary to run LLVM testing at an arbitrary SHA. + +Also has the ability to: +- kick off a CQ run +- keep track of the last SHA that testing was requested on, and skip + re-uploading if the SHA has not changed. +""" import argparse -import datetime +import dataclasses import json -import os +import logging from pathlib import Path import subprocess -from typing import Any, Dict, Iterable, List, Optional, Union +import sys +import textwrap +from typing import List, Optional +import atomic_write_file import chroot -import failure_modes +from cros_utils import git_utils import get_llvm_hash -import update_chromeos_llvm_hash +import llvm_next +import manifest_utils +import upload_llvm_testing_helper_cl -VALID_CQ_TRYBOTS = ("llvm", "llvm-next") +def resolve_llvm_sha(chromeos_tree: Path, sha_or_special: str) -> str: + """Resolves the `--sha` flag to an LLVM SHA.""" + if sha_or_special == "llvm-next": + return llvm_next.LLVM_NEXT_HASH + if sha_or_special == "google3": + return get_llvm_hash.LLVMHash().GetGoogle3LLVMHash() + if sha_or_special == "google3-unstable": + return get_llvm_hash.LLVMHash().GetGoogle3LLVMHash() + return git_utils.resolve_ref(chromeos_tree, sha_or_special) -def GetCommandLineArgs() -> argparse.Namespace: - """Parses the command line for the command line arguments. +def read_last_tried_sha(retry_state: Path) -> Optional[str]: + """Reads the last tried SHA from the state file.""" + try: + with retry_state.open(encoding="utf-8") as f: + return json.load(f)["last_tried_sha"] + except FileNotFoundError: + return None - Returns: - The log level to use when retrieving the LLVM hash or google3 LLVM - version, the chroot path to use for executing chroot commands, - a list of a package or packages to update their LLVM next hash, - and the LLVM version to use when retrieving the LLVM hash. - """ - # Default path to the chroot if a path is not specified. - cros_root = os.path.expanduser("~") - cros_root = os.path.join(cros_root, "chromiumos") +def write_last_tried_sha(retry_state: Path, sha: str): + """Writes the last tried SHA to the state file.""" + with atomic_write_file.atomic_write(retry_state) as f: + json.dump({"last_tried_sha": sha}, f) - # Create parser and add optional command-line arguments. - parser = argparse.ArgumentParser( - description="Update an LLVM hash of packages and run tests." - ) - # Add argument for other change lists that want to run alongside the tryjob - # which has a change list of updating a package's git hash. - parser.add_argument( - "--extra_change_lists", - type=int, - nargs="+", - default=[], - help="change lists that would like to be run alongside the change list " - "of updating the packages", - ) +@dataclasses.dataclass(frozen=True) +class UploadedCLs: + """Listing of CL numbers uploaded by a function.""" - # Add argument for a specific chroot path. - parser.add_argument( - "--chromeos_path", - default=cros_root, - help="the path to the ChromeOS tree (default: %(default)s)", - ) + internal: List[int] + external: List[int] - # Add argument for a specific chroot path. - parser.add_argument( - "--chroot_name", - default="chroot", - help=""" - the name of the chroot to use in the CrOS checkout. Defaults to - 'chroot'. - """, - ) - parser.add_argument( - "--chroot_out", - help=""" - the name of the chroot to use in the CrOS checkout. Defaults to - 'out' if the chroot's name is 'chroot'; otherwise, defaults to - '${chroot_name}_out'. - """, - ) +def upload_one_cl_to_main(git_dir: Path, sha: str, remote: str) -> int: + """Uploads exactly one SHA from `git_dir`. Returns the CL number. - # Add argument to choose between llvm and llvm-next. - parser.add_argument( - "--is_llvm_next", - action="store_true", - help="which llvm hash to update. Update LLVM_NEXT_HASH if specified. " - "Otherwise, update LLVM_HASH", + Raises: + AssertionError if more than one CL was uploaded. + """ + cl_ids = git_utils.upload_to_gerrit( + git_dir, + remote=remote, + branch=git_utils.CROS_MAIN_BRANCH, + ref=sha, ) + assert len(cl_ids) == 1, f"Expected to upload one CL; uploaded {cl_ids}" + return cl_ids[0] - # Add argument for the absolute path to the file that contains information - # on the previous tested svn version. - parser.add_argument( - "--last_tested", - help="the absolute path to the file that contains the last tested " - "arguments.", - ) - # Add argument for the LLVM version to use. - parser.add_argument( - "--llvm_version", - type=get_llvm_hash.IsSvnOption, - required=True, - help="which git hash of LLVM to find " - "{google3, ToT, <svn_version>} " - "(default: finds the git hash of the google3 LLVM " - "version)", - ) +def create_and_upload_test_helpers_cl( + chromeos_tree: Path, dry_run: bool +) -> int: + """Creates & uploads the LLVM 'test helper' CL. - # Add argument to add reviewers for the created CL. - parser.add_argument( - "--reviewers", - nargs="+", - default=[], - help="The reviewers for the package update changelist", + Returns: + The CL number of the test-helper CL, an int referencing an external CL. + If dry_run is passed, returns 0. + """ + chromiumos_overlay = ( + chromeos_tree / "src" / "third_party" / "chromiumos-overlay" ) - - subparsers = parser.add_subparsers(dest="subparser_name") - subparser_names = [] - # Testing with the tryjobs. - tryjob_subparser = subparsers.add_parser("tryjobs") - subparser_names.append("tryjobs") - tryjob_subparser.add_argument( - "--builders", - required=True, - nargs="+", - default=[], - help="builders to use for the tryjob testing", + sha = upload_llvm_testing_helper_cl.create_helper_cl_commit_in_worktree_of( + chromiumos_overlay ) - - # Add argument for custom options for the tryjob. - tryjob_subparser.add_argument( - "--options", - required=False, - nargs="+", - default=[], - help="options to use for the tryjob testing", + if dry_run: + logging.info( + "--dry-run passed; skipping upload of test-helpers CL %s", sha + ) + return 0 + return upload_one_cl_to_main( + chromiumos_overlay, sha, remote=git_utils.CROS_EXTERNAL_REMOTE ) - # Testing with the recipe builders - recipe_subparser = subparsers.add_parser("recipe") - subparser_names.append("recipe") - recipe_subparser.add_argument( - "--options", - required=False, - nargs="+", - default=[], - help="options passed to the recipe builders", - ) - recipe_subparser.add_argument( - "--builders", - required=True, - nargs="+", - default=[], - help="recipe builders to launch", - ) +def build_manifest_commit_message( + llvm_sha: str, + llvm_rev: int, + cq_depend_external: Optional[int], +) -> str: + msg = textwrap.dedent( + f"""\ + toolchain.xml: update llvm to {llvm_sha} (r{llvm_rev}) - # Testing with CQ. - cq_subparser = subparsers.add_parser("cq") - subparser_names.append("cq") - - # Add argument for specify a cq trybot to test along with other cq builders - # e.g. llvm, llvm-next or llvm-tot - cq_subparser.add_argument( - "--cq_trybot", - choices=VALID_CQ_TRYBOTS, - help="include the trybot to test together with other cq builders " - "available: %(choices)s", + BUG=None + TEST=CQ + """ ) - - args_output = parser.parse_args() - - if args_output.subparser_name not in subparser_names: - parser.error("one of %s must be specified" % subparser_names) - - if not args_output.chroot_out: - chroot_name = args_output.chroot_name - if chroot_name == "chroot": - out = "out" - else: - out = f"{chroot_name}_out" - args_output.chroot_out = out - - return args_output + if cq_depend_external: + msg += f"\n\nCq-Depend: chromium:{cq_depend_external}" + return msg -def UnchangedSinceLastRun( - last_tested_file: Optional[Union[Path, str]], - arg_dict: Dict, -) -> bool: - """Gets the arguments used for last run - - Args: - last_tested_file: The absolute path to the file that contains the - arguments for the last run. - arg_dict: The arguments used for this run. +def create_and_upload_manifest_cl( + chromeos_tree: Path, + llvm_sha: str, + llvm_rev: int, + cq_depend_external: Optional[int], + dry_run: bool, +) -> int: + """Creates & uploads the LLVM update manifest CL. Returns: - Return true if the arguments used for last run exist and are the - same as the arguments used for this run. Otherwise return false. - """ - - if not last_tested_file: - return False - - # Get the last tested svn version if the file exists. - last_arg_dict = None - try: - with open(last_tested_file, encoding="utf-8") as f: - last_arg_dict = json.load(f) - - except (IOError, ValueError): - return False - - return arg_dict == last_arg_dict - - -def AddReviewers( - cl: int, - reviewers: Iterable[str], - chromeos_path: Union[Path, str], -) -> None: - """Add reviewers for the created CL. - - Args: - cl: The CL number to add reviewers to. - reviewers: Email addresses of reviewers to add. - chromeos_path: The absolute path to the chromeos tree. - """ - - gerrit_abs_path = os.path.join(chromeos_path, "chromite/bin/gerrit") - for reviewer in reviewers: - cmd = [gerrit_abs_path, "reviewers", str(cl), reviewer] - - subprocess.check_output(cmd) - - -def AddLinksToCL( - tests: Iterable[Dict[str, Any]], - cl: int, - chromeos_path: Union[Path, str], -) -> None: - """Adds the test link(s) to the CL as a comment. - - Args: - tests: Links to the tests. - cl: The number of the CL to add the test links to. - chromeos_path: Absolute path to the chromeos tree. - """ - - # NOTE: Invoking `cros_sdk` does not make each tryjob link appear on its - # own line, so invoking the `gerrit` command directly instead of using - # `cros_sdk` to do it for us. - # - # FIXME: Need to figure out why `cros_sdk` does not add each tryjob link as - # a newline. - gerrit_abs_path = os.path.join(chromeos_path, "chromite/bin/gerrit") - - links = ["Started the following tests:"] - links.extend(test["link"] for test in tests) - - add_message_cmd = [gerrit_abs_path, "message", str(cl), "\n".join(links)] - - subprocess.check_output(add_message_cmd) - - -# Testing with tryjobs -def GetCurrentTimeInUTC() -> datetime.datetime: - """Returns the current time via `datetime.datetime.utcnow()`.""" - return datetime.datetime.utcnow() - - -def GetTryJobCommand( - change_list: int, - extra_change_lists: Iterable[int], - options: Iterable[str], - builder: str, -) -> List[str]: - """Constructs the 'tryjob' command. - - Args: - change_list: The CL obtained from updating the packages. - extra_change_lists: Extra change lists that would like to be run - alongside the change list of updating the packages. - options: Options to be passed into the tryjob command. - builder: The builder to be passed into the tryjob command. - - Returns: - The 'tryjob' command with the change list of updating the packages and - any extra information that was passed into the command line. - """ - - tryjob_cmd = ["cros", "tryjob", "--yes", "--json", "-g", "%d" % change_list] - - if extra_change_lists: - for extra_cl in extra_change_lists: - tryjob_cmd.extend(["-g", "%d" % extra_cl]) - - if options: - tryjob_cmd.extend("--%s" % option for option in options) - - tryjob_cmd.append(builder) - - return tryjob_cmd - - -def RunTryJobs( - cl_number: int, - extra_change_lists: List[int], - options: List[str], - builders: Iterable[str], - chromeos_path: Union[Path, str], -) -> List[Dict]: - """Runs a tryjob/tryjobs. - - Args: - cl_number: The CL created by updating the packages. - extra_change_lists: Any extra change lists that would run alongside the - CL that was created by updating the packages ('cl_number'). - options: Any options to be passed into the 'tryjob' command. - builders: All the builders to run the 'tryjob' with. - chromeos_path: The absolute path to the chromeos tree. - - Returns: - A list that contains stdout contents of each tryjob, where stdout is - information (a hashmap) about the tryjob. The hashmap also contains - stderr if there was an error when running a tryjob. - - Raises: - ValueError: Failed to submit a tryjob. + The CL number of the manifest CL, an int referencing an internal CL. If + dry_run is passed, returns `0`. """ - - # Contains the results of each builder. - tests = [] - - # Run tryjobs with the change list number obtained from updating the - # packages and append additional changes lists and options obtained from the - # command line. - for builder in builders: - cmd = GetTryJobCommand(cl_number, extra_change_lists, options, builder) - - out = subprocess.check_output(cmd, cwd=chromeos_path, encoding="utf-8") - - test_output = json.loads(out) - - buildbucket_id = int(test_output[0]["id"]) - - tests.append( - { - "launch_time": str(GetCurrentTimeInUTC()), - "link": "http://ci.chromium.org/b/%s" % buildbucket_id, - "buildbucket_id": buildbucket_id, - "extra_cls": extra_change_lists, - "options": options, - "builder": [builder], - } + manifest_internal = chromeos_tree / "manifest-internal" + with git_utils.create_worktree(manifest_internal) as worktree: + manifest_utils.update_chromeos_manifest_in_manifest_dir( + llvm_sha, worktree ) + commit_msg = build_manifest_commit_message( + llvm_sha, llvm_rev, cq_depend_external + ) + sha = git_utils.commit_all_changes(worktree, commit_msg) - AddLinksToCL(tests, cl_number, chromeos_path) - - return tests + if dry_run: + logging.info("--dry-run passed; skipping upload of manifest CL %s", sha) + return 0 + return upload_one_cl_to_main( + manifest_internal, + sha, + remote=git_utils.CROS_INTERNAL_REMOTE, + ) -def StartRecipeBuilders( - cl_number: int, - extra_change_lists: List[int], - options: List[str], - builders: List[str], - chromeos_path: Union[Path, str], -) -> List[Dict]: - """Launch recipe builders. - Args: - cl_number: The CL created by updating the packages. - extra_change_lists: Any extra change lists that would run alongside the - CL that was created by updating the packages ('cl_number'). - options: Any options to be passed into the 'tryjob' command. - builders: All the builders to run the 'tryjob' with. - chromeos_path: The absolute path to the chromeos tree. +def add_cl_comment( + chromeos_tree: Path, + cl_id: int, + internal: bool, + comment: str, +): + """Creates & uploads the LLVM update manifest CL. Returns: - A list that contains stdout contents of each builder, where stdout is - information (a hashmap) about the tryjob. The hashmap also contains - stderr if there was an error when running a tryjob. - - Raises: - ValueError: Failed to start a builder. + The CL number of the manifest CL, an int referencing an internal CL. """ + cmd = ["gerrit"] + if internal: + cmd.append("--internal") + cmd += ("message", str(cl_id), comment) + subprocess.run( + cmd, + check=True, + cwd=chromeos_tree, + stdin=subprocess.DEVNULL, + ) - # Contains the results of each builder. - tests = [] - - # Launch a builders with the change list number obtained from updating the - # packages and append additional changes lists and options obtained from the - # command line. - for builder in builders: - cmd = ["bb", "add", "-json"] - - if cl_number: - cmd.extend(["-cl", "crrev.com/c/%d" % cl_number]) - - if extra_change_lists: - for cl in extra_change_lists: - cmd.extend(["-cl", "crrev.com/c/%d" % cl]) - - if options: - cmd.extend(options) - - cmd.append(builder) - - out = subprocess.check_output(cmd, cwd=chromeos_path, encoding="utf-8") - - test_output = json.loads(out) - tests.append( - { - "launch_time": test_output["createTime"], - "link": "http://ci.chromium.org/b/%s" % test_output["id"], - "buildbucket_id": test_output["id"], - "extra_cls": extra_change_lists, - "options": options, - "builder": [builder], - } +def create_and_upload_cls( + chromeos_tree: Path, + llvm_sha: str, + llvm_rev: int, + include_test_helpers: bool, + dry_run: bool, +) -> UploadedCLs: + external_cls = [] + if include_test_helpers: + logging.info("Uploading test-helper CL...") + test_helper_cl = create_and_upload_test_helpers_cl( + chromeos_tree, dry_run ) + external_cls.append(test_helper_cl) + else: + test_helper_cl = None + logging.info("Creating LLVM update CL...") + manifest_cl = create_and_upload_manifest_cl( + chromeos_tree, + llvm_sha, + llvm_rev, + test_helper_cl, + dry_run, + ) + # Notably, this is meant to catch `test_helper_cl == 0` (dry_run) or + # `test_helper_cl == None` (if none was uploaded) + if test_helper_cl: + add_cl_comment( + chromeos_tree, + test_helper_cl, + internal=False, + comment=f"Corresponding Manifest update: crrev.com/i/{manifest_cl}", + ) + return UploadedCLs( + internal=[manifest_cl], + external=external_cls, + ) - AddLinksToCL(tests, cl_number, chromeos_path) - - return tests - - -# Testing with CQ -def GetCQDependString(dependent_cls: List[int]) -> Optional[str]: - """Get CQ dependency string e.g. `Cq-Depend: chromium:MM, chromium:NN`.""" - - if not dependent_cls: - return None - - # Cq-Depend must start a new paragraph prefixed with "Cq-Depend". - return "Cq-Depend: " + ", ".join(f"chromium:{x}" for x in dependent_cls) - - -def GetCQIncludeTrybotsString(trybot: Optional[str]) -> Optional[str]: - """Get Cq-Include-Trybots string, for more llvm testings""" - - if not trybot: - return None - - if trybot not in VALID_CQ_TRYBOTS: - raise ValueError("%s is not a valid llvm trybot" % trybot) - - # Cq-Include-Trybots must start a new paragraph prefixed - # with "Cq-Include-Trybots". - return "Cq-Include-Trybots:chromeos/cq:cq-%s-orchestrator" % trybot - - -def StartCQDryRun( - cl: int, - dependent_cls: List[int], - chromeos_path: Union[Path, str], -) -> None: - """Start CQ dry run for the changelist and dependencies.""" - - gerrit_abs_path = os.path.join(chromeos_path, "chromite/bin/gerrit") - - cl_list = [cl] - cl_list.extend(dependent_cls) - - for changes in cl_list: - cq_dry_run_cmd = [gerrit_abs_path, "label-cq", str(changes), "1"] - - subprocess.check_output(cq_dry_run_cmd) - - -def main(): - """Updates the packages' LLVM hash and run tests. - - Raises: - AssertionError: The script was run inside the chroot. - """ - chroot.VerifyOutsideChroot() +def make_gerrit_cq_dry_run_command(cls: List[int], internal: bool) -> List[str]: + assert cls, "Can't make a dry-run command with no CLs to dry-run." + cmd = ["gerrit"] + if internal: + cmd.append("--internal") + cmd.append("label-cq") + cmd += (str(x) for x in cls) + cmd.append("1") + return cmd + + +def cq_dry_run_cls(chromeos_tree: Path, cls: UploadedCLs): + """Sets CQ+1 on the given uploaded CL listing.""" + # At the time of writing, this is expected given the context of the script. + # Can easily refactor to make `cls.internal` optional, though. + gerrit_cmds = [] + assert cls.internal, "LLVM update without internal CLs?" + gerrit_cmds.append( + make_gerrit_cq_dry_run_command(cls.internal, internal=True) + ) + if cls.external: + gerrit_cmds.append( + make_gerrit_cq_dry_run_command(cls.external, internal=False) + ) + for cmd in gerrit_cmds: + subprocess.run( + cmd, + check=True, + cwd=chromeos_tree, + stdin=subprocess.DEVNULL, + ) - args_output = GetCommandLineArgs() - chroot.VerifyChromeOSRoot(args_output.chromeos_path) +def parse_opts(argv: List[str]) -> argparse.Namespace: + """Parse command-line options.""" + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter, + ) + parser.add_argument( + "--chromeos-tree", + type=Path, + help=""" + ChromeOS tree to make modifications in. Will be inferred if none + is passed. + """, + ) + parser.add_argument( + "--cq", + action="store_true", + help="After uploading, set CQ+1 on the CL(s) that were uploaded.", + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="If passed, only commit changes locally; don't upload them.", + ) + parser.add_argument( + "--include-llvm-test-helper-cls", + action="store_true", + help=""" + Also upload CL(s) meant to ease LLVM testing. Namely, this will include + logic to disable `-Werror` on packages, and logic to disable patches + that no longer apply to LLVM. + """, + ) + parser.add_argument( + "--retry-state", + type=Path, + help=""" + If passed, this will keep script state in the given file. At the + moment, this file is only used to ensure that subsequent runs of this + script don't trigger identical uploads. + """, + ) + parser.add_argument( + "--sha", + required=True, + help=""" + SHA to use. This can either be an LLVM SHA, or a special value: + `llvm-next`, `google3` or `google3-unstable`. + """, + ) + return parser.parse_args(argv) - svn_option = args_output.llvm_version - git_hash, svn_version = get_llvm_hash.GetLLVMHashAndVersionFromSVNOption( - svn_option +def main(argv: List[str]) -> None: + my_dir = Path(__file__).parent.resolve() + logging.basicConfig( + format=">> %(asctime)s: %(levelname)s: %(filename)s:%(lineno)d: " + "%(message)s", + level=logging.INFO, ) - # There is no need to run tryjobs when all the key parameters remain - # unchanged from last time. - - # If --last_tested is specified, check if the current run has the same - # arguments last time --last_tested is used. - if args_output.last_tested: - chroot_file_paths = chroot.GetChrootEbuildPaths( - args_output.chromeos_path, - update_chromeos_llvm_hash.DEFAULT_PACKAGES, - args_output.chroot_name, - args_output.chroot_out, - ) - arg_dict = { - "svn_version": svn_version, - "ebuilds": chroot_file_paths, - "extra_cls": args_output.extra_change_lists, - } - if args_output.subparser_name in ("tryjobs", "recipe"): - arg_dict["builders"] = args_output.builders - arg_dict["tryjob_options"] = args_output.options - if UnchangedSinceLastRun(args_output.last_tested, arg_dict): - print( - "svn version (%d) matches the last tested svn version in %s" - % (svn_version, args_output.last_tested) - ) + opts = parse_opts(argv) + dry_run = opts.dry_run + chromeos_tree = opts.chromeos_tree + if not chromeos_tree: + chromeos_tree = chroot.FindChromeOSRootAbove(my_dir) + + new_sha = resolve_llvm_sha(chromeos_tree, opts.sha) + logging.info("Using LLVM SHA %s...", new_sha) + if opts.retry_state: + last_tried_sha = read_last_tried_sha(opts.retry_state) + if last_tried_sha == new_sha: + logging.info("New SHA is the same as the last tried SHA; quit.") return + logging.info( + "New SHA is different than the last tried SHA (%s).", last_tried_sha + ) - llvm_variant = update_chromeos_llvm_hash.LLVMVariant.current - if args_output.is_llvm_next: - llvm_variant = update_chromeos_llvm_hash.LLVMVariant.next - - extra_commit_msg_lines = [] - if args_output.subparser_name == "cq": - footers = [] - cq_depend_msg = GetCQDependString(args_output.extra_change_lists) - if cq_depend_msg: - footers.append(cq_depend_msg) - cq_trybot_msg = GetCQIncludeTrybotsString(args_output.cq_trybot) - if cq_trybot_msg: - footers.append(cq_trybot_msg) - - # We want a single blank line before any of these, so Git properly - # interprets them as a footer. - if footers: - extra_commit_msg_lines.append("") - extra_commit_msg_lines += footers - - change_list = update_chromeos_llvm_hash.UpdatePackages( - packages=update_chromeos_llvm_hash.DEFAULT_PACKAGES, - manifest_packages=[], - llvm_variant=llvm_variant, - git_hash=git_hash, - svn_version=svn_version, - chroot_opts=update_chromeos_llvm_hash.ChrootOpts( - chromeos_root=Path(args_output.chromeos_path), - chroot_name=args_output.chroot_name, - out_name=args_output.chroot_out, - ), - mode=failure_modes.FailureModes.DISABLE_PATCHES, - git_hash_source=svn_option, - extra_commit_msg_lines=extra_commit_msg_lines, - # b/331607705: set WIP on these changes, so the code-review-nudger bot - # doesn't ping them. - wip=True, + logging.info("Getting LLVM revision for SHA %s...", new_sha) + new_rev = get_llvm_hash.GetVersionFrom( + get_llvm_hash.GetAndUpdateLLVMProjectInLLVMTools(), new_sha ) - - AddReviewers( - change_list.cl_number, args_output.reviewers, args_output.chromeos_path + logging.info("LLVM SHA %s == r%d", new_sha, new_rev) + uploaded_cls = create_and_upload_cls( + chromeos_tree, + new_sha, + new_rev, + opts.include_llvm_test_helper_cls, + dry_run, ) - print("Successfully updated packages to %d" % svn_version) - print("Gerrit URL: %s" % change_list.url) - print("Change list number: %d" % change_list.cl_number) - - if args_output.subparser_name == "tryjobs": - tests = RunTryJobs( - change_list.cl_number, - args_output.extra_change_lists, - args_output.options, - args_output.builders, - args_output.chromeos_path, - ) - print("Tests:") - for test in tests: - print(test) - elif args_output.subparser_name == "recipe": - tests = StartRecipeBuilders( - change_list.cl_number, - args_output.extra_change_lists, - args_output.options, - args_output.builders, - args_output.chromeos_path, - ) - print("Tests:") - for test in tests: - print(test) + if dry_run: + logging.info("--dry-run passed; exiting") + return - else: - StartCQDryRun( - change_list.cl_number, - args_output.extra_change_lists, - args_output.chromeos_path, - ) + if opts.cq: + logging.info("Setting CQ+1 on the CLs...") + cq_dry_run_cls(chromeos_tree, uploaded_cls) - # If --last_tested is specified, record the arguments used - if args_output.last_tested: - with open(args_output.last_tested, "w", encoding="utf-8") as f: - json.dump(arg_dict, f, indent=2) + if opts.retry_state: + write_last_tried_sha(opts.retry_state, new_sha) if __name__ == "__main__": - main() + main(sys.argv[1:]) diff --git a/llvm_tools/update_packages_and_run_tests_unittest.py b/llvm_tools/update_packages_and_run_tests_unittest.py index 9abdc199..8954893f 100755 --- a/llvm_tools/update_packages_and_run_tests_unittest.py +++ b/llvm_tools/update_packages_and_run_tests_unittest.py @@ -1,548 +1,82 @@ #!/usr/bin/env python3 -# Copyright 2019 The ChromiumOS Authors +# 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. -"""Unittests for running tests after updating packages.""" +"""Tests for update_packages_and_run_tests.py""" -import json +from pathlib import Path +import shutil import subprocess +import tempfile import unittest from unittest import mock -import chroot -import get_llvm_hash -import git -import test_helpers -import update_chromeos_llvm_hash import update_packages_and_run_tests -# Testing with tryjobs. -class UpdatePackagesAndRunTryjobsTest(unittest.TestCase): - """Unittests when running tryjobs after updating packages.""" +class Test(unittest.TestCase): + """Tests for update_packages_and_run_tests.py""" - def testNoLastTestedFile(self): - self.assertEqual( - update_packages_and_run_tests.UnchangedSinceLastRun(None, {}), False - ) + def make_tempdir(self) -> Path: + tempdir = tempfile.mkdtemp("run_llvm_tests_at_sha_test_") + self.addCleanup(shutil.rmtree, tempdir) + return Path(tempdir) - def testEmptyLastTestedFile(self): - with test_helpers.CreateTemporaryFile() as temp_file: - self.assertEqual( - update_packages_and_run_tests.UnchangedSinceLastRun( - temp_file, {} - ), - False, + def test_sha_state_file_handles_file_not_existing(self): + tempdir = self.make_tempdir() + self.assertIsNone( + update_packages_and_run_tests.read_last_tried_sha( + tempdir / "does-not-exist" ) - - def testLastTestedFileDoesNotExist(self): - # Simulate 'open()' on a lasted tested file that does not exist. - mock.mock_open(read_data="") - - self.assertEqual( - update_packages_and_run_tests.UnchangedSinceLastRun( - "/some/file/that/does/not/exist.txt", {} - ), - False, ) - def testMatchedLastTestedFile(self): - with test_helpers.CreateTemporaryFile() as last_tested_file: - arg_dict = { - "svn_version": 1234, - "ebuilds": [ - "/path/to/package1-r2.ebuild", - "/path/to/package2/package2-r3.ebuild", - ], - "builders": [ - "kevin-llvm-next-toolchain-tryjob", - "eve-llvm-next-toolchain-tryjob", - ], - "extra_cls": [10, 1], - "tryjob_options": ["latest-toolchain", "hwtest"], - } - - with open(last_tested_file, "w", encoding="utf-8") as f: - f.write(json.dumps(arg_dict, indent=2)) - - self.assertEqual( - update_packages_and_run_tests.UnchangedSinceLastRun( - last_tested_file, arg_dict - ), - True, - ) - - def testGetTryJobCommandWithNoExtraInformation(self): - change_list = 1234 - - builder = "nocturne" - - expected_cmd = [ - "cros", - "tryjob", - "--yes", - "--json", - "-g", - "%d" % change_list, - builder, - ] - + def test_sha_state_file_round_trips(self): + tempdir = self.make_tempdir() + state_file = tempdir / "state.json" + sha = "a" * 40 + update_packages_and_run_tests.write_last_tried_sha(state_file, sha) self.assertEqual( - update_packages_and_run_tests.GetTryJobCommand( - change_list, None, None, builder - ), - expected_cmd, + update_packages_and_run_tests.read_last_tried_sha(state_file), sha ) - def testGetTryJobCommandWithExtraInformation(self): - change_list = 4321 - extra_cls = [1000, 10] - options = ["option1", "option2"] - builder = "kevin" - - expected_cmd = [ - "cros", - "tryjob", - "--yes", - "--json", - "-g", - "%d" % change_list, - "-g", - "%d" % extra_cls[0], - "-g", - "%d" % extra_cls[1], - "--%s" % options[0], - "--%s" % options[1], - builder, - ] - - self.assertEqual( - update_packages_and_run_tests.GetTryJobCommand( - change_list, extra_cls, options, builder + @mock.patch.object(subprocess, "run") + def test_gerrit_cq_dry_run_runs_correct_gerrit_commands(self, mock_run): + chromeos_tree = self.make_tempdir() + update_packages_and_run_tests.cq_dry_run_cls( + chromeos_tree, + update_packages_and_run_tests.UploadedCLs( + internal=[123], + external=[456, 789], ), - expected_cmd, - ) - - @mock.patch.object( - update_packages_and_run_tests, - "GetCurrentTimeInUTC", - return_value="2019-09-09", - ) - @mock.patch.object(update_packages_and_run_tests, "AddLinksToCL") - @mock.patch.object(subprocess, "check_output") - def testSuccessfullySubmittedTryJob( - self, mock_cmd, mock_add_links_to_cl, mock_launch_time - ): - expected_cmd = [ - "cros", - "tryjob", - "--yes", - "--json", - "-g", - "%d" % 900, - "-g", - "%d" % 1200, - "--some_option", - "builder1", - ] - - bb_id = "1234" - url = "http://ci.chromium.org/b/%s" % bb_id - - mock_cmd.return_value = json.dumps([{"id": bb_id, "url": url}]) - - chromeos_path = "/some/path/to/chromeos" - cl = 900 - extra_cls = [1200] - options = ["some_option"] - builders = ["builder1"] - - tests = update_packages_and_run_tests.RunTryJobs( - cl, extra_cls, options, builders, chromeos_path - ) - - expected_tests = [ - { - "launch_time": mock_launch_time.return_value, - "link": url, - "buildbucket_id": int(bb_id), - "extra_cls": extra_cls, - "options": options, - "builder": builders, - } - ] - - self.assertEqual(tests, expected_tests) - - mock_cmd.assert_called_once_with( - expected_cmd, cwd=chromeos_path, encoding="utf-8" - ) - - mock_add_links_to_cl.assert_called_once() - - @mock.patch.object(update_packages_and_run_tests, "AddLinksToCL") - @mock.patch.object(subprocess, "check_output") - def testSuccessfullySubmittedRecipeBuilders( - self, mock_cmd, mock_add_links_to_cl - ): - expected_cmd = [ - "bb", - "add", - "-json", - "-cl", - "crrev.com/c/%s" % 900, - "-cl", - "crrev.com/c/%s" % 1200, - "some_option", - "builder1", - ] - - bb_id = "1234" - create_time = "2020-04-18T00:03:53.978767Z" - - mock_cmd.return_value = json.dumps( - {"id": bb_id, "createTime": create_time} - ) - - chromeos_path = "/some/path/to/chromeos" - cl = 900 - extra_cls = [1200] - options = ["some_option"] - builders = ["builder1"] - - tests = update_packages_and_run_tests.StartRecipeBuilders( - cl, extra_cls, options, builders, chromeos_path ) - - expected_tests = [ - { - "launch_time": create_time, - "link": "http://ci.chromium.org/b/%s" % bb_id, - "buildbucket_id": bb_id, - "extra_cls": extra_cls, - "options": options, - "builder": builders, - } - ] - - self.assertEqual(tests, expected_tests) - - mock_cmd.assert_called_once_with( - expected_cmd, cwd=chromeos_path, encoding="utf-8" + self.assertEqual(mock_run.call_count, 2) + mock_run.assert_any_call( + ["gerrit", "label-cq", "456", "789", "1"], + check=True, + cwd=chromeos_tree, + stdin=subprocess.DEVNULL, ) - - mock_add_links_to_cl.assert_called_once() - - @mock.patch.object(subprocess, "check_output", return_value=None) - def testSuccessfullyAddedTestLinkToCL(self, mock_exec_cmd): - chromeos_path = "/abs/path/to/chromeos" - - test_cl_number = 1000 - - tests = [{"link": "https://some_tryjob_link.com"}] - - update_packages_and_run_tests.AddLinksToCL( - tests, test_cl_number, chromeos_path + mock_run.assert_any_call( + ["gerrit", "--internal", "label-cq", "123", "1"], + check=True, + cwd=chromeos_tree, + stdin=subprocess.DEVNULL, ) - expected_gerrit_message = [ - "%s/chromite/bin/gerrit" % chromeos_path, - "message", - str(test_cl_number), - "Started the following tests:\n%s" % tests[0]["link"], - ] - - mock_exec_cmd.assert_called_once_with(expected_gerrit_message) - - @mock.patch.object(update_packages_and_run_tests, "RunTryJobs") - @mock.patch.object(update_chromeos_llvm_hash, "UpdatePackages") - @mock.patch.object(update_packages_and_run_tests, "GetCommandLineArgs") - @mock.patch.object(get_llvm_hash, "GetLLVMHashAndVersionFromSVNOption") - @mock.patch.object(chroot, "VerifyChromeOSRoot") - @mock.patch.object(chroot, "VerifyOutsideChroot", return_value=True) - @mock.patch.object(chroot, "GetChrootEbuildPaths") - def testUpdatedLastTestedFileWithNewTestedRevision( - self, - mock_get_chroot_build_paths, - mock_outside_chroot, - mock_chromeos_root, - mock_get_hash_and_version, - mock_get_commandline_args, - mock_update_packages, - mock_run_tryjobs, + @mock.patch.object(subprocess, "run") + def test_gerrit_cq_dry_run_only_runs_one_command_if_necessary( + self, mock_run ): - # Create a temporary file to simulate the last tested file that - # contains a revision. - with test_helpers.CreateTemporaryFile() as last_tested_file: - builders = [ - "kevin-llvm-next-toolchain-tryjob", - "eve-llvm-next-toolchain-tryjob", - ] - extra_cls = [10, 1] - tryjob_options = ["latest-toolchain", "hwtest"] - ebuilds = [ - "/path/to/package1/package1-r2.ebuild", - "/path/to/package2/package2-r3.ebuild", - ] - - arg_dict = { - "svn_version": 100, - "ebuilds": ebuilds, - "builders": builders, - "extra_cls": extra_cls, - "tryjob_options": tryjob_options, - } - # Parepared last tested file - with open(last_tested_file, "w", encoding="utf-8") as f: - json.dump(arg_dict, f, indent=2) - - # Call with a changed LLVM svn version - args_output = test_helpers.ArgsOutputTest() - args_output.chroot_name = "custom-chroot" - args_output.chroot_out = "custom-chroot_out" - args_output.is_llvm_next = True - args_output.extra_change_lists = extra_cls - args_output.last_tested = last_tested_file - args_output.reviewers = [] - - args_output.subparser_name = "tryjobs" - args_output.builders = builders - args_output.options = tryjob_options - - mock_get_commandline_args.return_value = args_output - - mock_get_chroot_build_paths.return_value = ebuilds - - mock_get_hash_and_version.return_value = ("a123testhash2", 200) - - mock_update_packages.return_value = git.CommitContents( - url="https://some_cl_url.com", cl_number=12345 - ) - - mock_run_tryjobs.return_value = [ - {"link": "https://some_tryjob_url.com", "buildbucket_id": 1234} - ] - - update_packages_and_run_tests.main() - - # Verify that the lasted tested file has been updated to the new - # LLVM revision. - with open(last_tested_file, encoding="utf-8") as f: - arg_dict = json.load(f) - - self.assertEqual(arg_dict["svn_version"], 200) - - mock_outside_chroot.assert_called_once() - - mock_chromeos_root.assert_called_once() - - mock_get_commandline_args.assert_called_once() - - mock_get_hash_and_version.assert_called_once() - - mock_run_tryjobs.assert_called_once() - - mock_update_packages.assert_called_once() - commit_msg_lines = mock_update_packages.call_args[1][ - "extra_commit_msg_lines" - ] - self.assertTrue( - isinstance(commit_msg_lines, list), repr(commit_msg_lines) - ) - - -class UpdatePackagesAndRunTestCQTest(unittest.TestCase): - """Unittests for CQ dry run after updating packages.""" - - def testGetCQDependString(self): - test_no_changelists = [] - test_single_changelist = [1234] - test_multiple_changelists = [1234, 5678] - - self.assertIsNone( - update_packages_and_run_tests.GetCQDependString(test_no_changelists) - ) - - self.assertEqual( - update_packages_and_run_tests.GetCQDependString( - test_single_changelist - ), - "Cq-Depend: chromium:1234", - ) - - self.assertEqual( - update_packages_and_run_tests.GetCQDependString( - test_multiple_changelists - ), - "Cq-Depend: chromium:1234, chromium:5678", - ) - - def testGetCQIncludeTrybotsString(self): - test_no_trybot = None - test_valid_trybot = "llvm-next" - test_invalid_trybot = "invalid-name" - - self.assertIsNone( - update_packages_and_run_tests.GetCQIncludeTrybotsString( - test_no_trybot - ) - ) - - self.assertEqual( - update_packages_and_run_tests.GetCQIncludeTrybotsString( - test_valid_trybot + chromeos_tree = self.make_tempdir() + update_packages_and_run_tests.cq_dry_run_cls( + chromeos_tree, + update_packages_and_run_tests.UploadedCLs( + internal=[123], + external=[], ), - "Cq-Include-Trybots:chromeos/cq:cq-llvm-next-orchestrator", - ) - - with self.assertRaises(ValueError) as context: - update_packages_and_run_tests.GetCQIncludeTrybotsString( - test_invalid_trybot - ) - - self.assertIn("is not a valid llvm trybot", str(context.exception)) - - @mock.patch.object(subprocess, "check_output", return_value=None) - def testStartCQDryRunNoDeps(self, mock_exec_cmd): - chromeos_path = "/abs/path/to/chromeos" - test_cl_number = 1000 - - # test with no deps cls. - extra_cls = [] - update_packages_and_run_tests.StartCQDryRun( - test_cl_number, extra_cls, chromeos_path - ) - - expected_gerrit_message = [ - "%s/chromite/bin/gerrit" % chromeos_path, - "label-cq", - str(test_cl_number), - "1", - ] - - mock_exec_cmd.assert_called_once_with(expected_gerrit_message) - - # Mock ExecCommandAndCaptureOutput for the gerrit command execution. - @mock.patch.object(subprocess, "check_output", return_value=None) - # test with a single deps cl. - def testStartCQDryRunSingleDep(self, mock_exec_cmd): - chromeos_path = "/abs/path/to/chromeos" - test_cl_number = 1000 - - extra_cls = [2000] - update_packages_and_run_tests.StartCQDryRun( - test_cl_number, extra_cls, chromeos_path - ) - - expected_gerrit_cmd_1 = [ - "%s/chromite/bin/gerrit" % chromeos_path, - "label-cq", - str(test_cl_number), - "1", - ] - expected_gerrit_cmd_2 = [ - "%s/chromite/bin/gerrit" % chromeos_path, - "label-cq", - str(2000), - "1", - ] - - self.assertEqual(mock_exec_cmd.call_count, 2) - self.assertEqual( - mock_exec_cmd.call_args_list[0], mock.call(expected_gerrit_cmd_1) - ) - self.assertEqual( - mock_exec_cmd.call_args_list[1], mock.call(expected_gerrit_cmd_2) - ) - - # Mock ExecCommandAndCaptureOutput for the gerrit command execution. - @mock.patch.object(subprocess, "check_output", return_value=None) - def testStartCQDryRunMultipleDep(self, mock_exec_cmd): - chromeos_path = "/abs/path/to/chromeos" - test_cl_number = 1000 - - # test with multiple deps cls. - extra_cls = [3000, 4000] - update_packages_and_run_tests.StartCQDryRun( - test_cl_number, extra_cls, chromeos_path - ) - - expected_gerrit_cmd_1 = [ - "%s/chromite/bin/gerrit" % chromeos_path, - "label-cq", - str(test_cl_number), - "1", - ] - expected_gerrit_cmd_2 = [ - "%s/chromite/bin/gerrit" % chromeos_path, - "label-cq", - str(3000), - "1", - ] - expected_gerrit_cmd_3 = [ - "%s/chromite/bin/gerrit" % chromeos_path, - "label-cq", - str(4000), - "1", - ] - - self.assertEqual(mock_exec_cmd.call_count, 3) - self.assertEqual( - mock_exec_cmd.call_args_list[0], mock.call(expected_gerrit_cmd_1) - ) - self.assertEqual( - mock_exec_cmd.call_args_list[1], mock.call(expected_gerrit_cmd_2) - ) - self.assertEqual( - mock_exec_cmd.call_args_list[2], mock.call(expected_gerrit_cmd_3) - ) - - # Mock ExecCommandAndCaptureOutput for the gerrit command execution. - @mock.patch.object(subprocess, "check_output", return_value=None) - # test with no reviewers. - def testAddReviewersNone(self, mock_exec_cmd): - chromeos_path = "/abs/path/to/chromeos" - reviewers = [] - test_cl_number = 1000 - - update_packages_and_run_tests.AddReviewers( - test_cl_number, reviewers, chromeos_path - ) - self.assertTrue(mock_exec_cmd.not_called) - - # Mock ExecCommandAndCaptureOutput for the gerrit command execution. - @mock.patch.object(subprocess, "check_output", return_value=None) - # test with multiple reviewers. - def testAddReviewersMultiple(self, mock_exec_cmd): - chromeos_path = "/abs/path/to/chromeos" - reviewers = ["none1@chromium.org", "none2@chromium.org"] - test_cl_number = 1000 - - update_packages_and_run_tests.AddReviewers( - test_cl_number, reviewers, chromeos_path - ) - - expected_gerrit_cmd_1 = [ - "%s/chromite/bin/gerrit" % chromeos_path, - "reviewers", - str(test_cl_number), - "none1@chromium.org", - ] - expected_gerrit_cmd_2 = [ - "%s/chromite/bin/gerrit" % chromeos_path, - "reviewers", - str(test_cl_number), - "none2@chromium.org", - ] - - self.assertEqual(mock_exec_cmd.call_count, 2) - self.assertEqual( - mock_exec_cmd.call_args_list[0], mock.call(expected_gerrit_cmd_1) - ) - self.assertEqual( - mock_exec_cmd.call_args_list[1], mock.call(expected_gerrit_cmd_2) ) + mock_run.assert_called_once() if __name__ == "__main__": diff --git a/llvm_tools/upload_llvm_testing_helper_cl.py b/llvm_tools/upload_llvm_testing_helper_cl.py index a66e3fbf..97fafa26 100755 --- a/llvm_tools/upload_llvm_testing_helper_cl.py +++ b/llvm_tools/upload_llvm_testing_helper_cl.py @@ -13,7 +13,6 @@ These CLs make the validation of LLVM easier, and do things like: import argparse import logging from pathlib import Path -import subprocess import sys from typing import List @@ -100,33 +99,6 @@ def add_disable_warnings_block(chromiumos_overlay: Path): f.write(DISABLE_WARNINGS_BLOCK) -def commit_all_changes(git_dir: Path, message: str) -> str: - """Commits all changes in `git_dir`, with the given commit message.""" - # Explicitly add using `git add -A`, since that stages all unstaged changes - # & adds any files that aren't tracked. `git commit -a` skips adding - # untracked files. - subprocess.run( - ["git", "add", "-A"], - check=True, - cwd=git_dir, - stdin=subprocess.DEVNULL, - ) - subprocess.run( - ["git", "commit", "-m", message], - check=True, - cwd=git_dir, - stdin=subprocess.DEVNULL, - ) - return subprocess.run( - ["git", "rev-parse", "HEAD"], - check=True, - cwd=git_dir, - stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - encoding="utf-8", - ).stdout.strip() - - def create_helper_cl_commit_in_worktree_of(chromiumos_overlay: Path) -> str: """Creates a commit containing the helper CL diff. Returns the SHA.commit""" with git_utils.create_worktree(chromiumos_overlay) as worktree: @@ -134,7 +106,7 @@ def create_helper_cl_commit_in_worktree_of(chromiumos_overlay: Path) -> str: add_force_rebuild_markers(worktree) add_use_force_block(worktree) add_disable_warnings_block(worktree) - return commit_all_changes(worktree, COMMIT_MESSAGE) + return git_utils.commit_all_changes(worktree, COMMIT_MESSAGE) def main(argv: List[str]) -> None: @@ -182,8 +154,8 @@ def main(argv: List[str]) -> None: # This logs the CL information, so no need to print anything after this. git_utils.upload_to_gerrit( git_repo=chromiumos_overlay, - remote="cros", - branch="main", + remote=git_utils.CROS_EXTERNAL_REMOTE, + branch=git_utils.CROS_MAIN_BRANCH, ref=helper_sha, ) |