1*760c253cSXin Li#!/usr/bin/env python3 2*760c253cSXin Li# Copyright 2019 The ChromiumOS Authors 3*760c253cSXin Li# Use of this source code is governed by a BSD-style license that can be 4*760c253cSXin Li# found in the LICENSE file. 5*760c253cSXin Li 6*760c253cSXin Li"""Modifies a tryjob based off of arguments.""" 7*760c253cSXin Li 8*760c253cSXin Liimport argparse 9*760c253cSXin Liimport enum 10*760c253cSXin Liimport json 11*760c253cSXin Liimport os 12*760c253cSXin Lifrom pathlib import Path 13*760c253cSXin Liimport sys 14*760c253cSXin Lifrom typing import Dict, Iterable, List, Union 15*760c253cSXin Li 16*760c253cSXin Liimport chroot 17*760c253cSXin Liimport failure_modes 18*760c253cSXin Liimport get_llvm_hash 19*760c253cSXin Liimport git 20*760c253cSXin Liimport update_chromeos_llvm_hash 21*760c253cSXin Liimport update_packages_and_run_tests 22*760c253cSXin Liimport update_tryjob_status 23*760c253cSXin Li 24*760c253cSXin Li 25*760c253cSXin Liclass ModifyTryjob(enum.Enum): 26*760c253cSXin Li """Options to modify a tryjob.""" 27*760c253cSXin Li 28*760c253cSXin Li REMOVE = "remove" 29*760c253cSXin Li RELAUNCH = "relaunch" 30*760c253cSXin Li ADD = "add" 31*760c253cSXin Li 32*760c253cSXin Li 33*760c253cSXin Lidef GetCommandLineArgs() -> argparse.Namespace: 34*760c253cSXin Li """Parses the command line for the command line arguments.""" 35*760c253cSXin Li 36*760c253cSXin Li # Default path to the chroot if a path is not specified. 37*760c253cSXin Li cros_root = os.path.expanduser("~") 38*760c253cSXin Li cros_root = os.path.join(cros_root, "chromiumos") 39*760c253cSXin Li 40*760c253cSXin Li # Create parser and add optional command-line arguments. 41*760c253cSXin Li parser = argparse.ArgumentParser( 42*760c253cSXin Li description="Removes, relaunches, or adds a tryjob." 43*760c253cSXin Li ) 44*760c253cSXin Li 45*760c253cSXin Li # Add argument for the JSON file to use for the update of a tryjob. 46*760c253cSXin Li parser.add_argument( 47*760c253cSXin Li "--status_file", 48*760c253cSXin Li required=True, 49*760c253cSXin Li help="The absolute path to the JSON file that contains the tryjobs " 50*760c253cSXin Li "used for bisecting LLVM.", 51*760c253cSXin Li ) 52*760c253cSXin Li 53*760c253cSXin Li # Add argument that determines what action to take on the revision 54*760c253cSXin Li # specified. 55*760c253cSXin Li parser.add_argument( 56*760c253cSXin Li "--modify_tryjob", 57*760c253cSXin Li required=True, 58*760c253cSXin Li choices=[modify_tryjob.value for modify_tryjob in ModifyTryjob], 59*760c253cSXin Li help="What action to perform on the tryjob.", 60*760c253cSXin Li ) 61*760c253cSXin Li 62*760c253cSXin Li # Add argument that determines which revision to search for in the list of 63*760c253cSXin Li # tryjobs. 64*760c253cSXin Li parser.add_argument( 65*760c253cSXin Li "--revision", 66*760c253cSXin Li required=True, 67*760c253cSXin Li type=int, 68*760c253cSXin Li help="The revision to either remove or relaunch.", 69*760c253cSXin Li ) 70*760c253cSXin Li 71*760c253cSXin Li # Add argument for other change lists that want to run alongside the 72*760c253cSXin Li # tryjob. 73*760c253cSXin Li parser.add_argument( 74*760c253cSXin Li "--extra_change_lists", 75*760c253cSXin Li type=int, 76*760c253cSXin Li nargs="+", 77*760c253cSXin Li help="change lists that would like to be run alongside the change list " 78*760c253cSXin Li "of updating the packages", 79*760c253cSXin Li ) 80*760c253cSXin Li 81*760c253cSXin Li # Add argument for custom options for the tryjob. 82*760c253cSXin Li parser.add_argument( 83*760c253cSXin Li "--options", 84*760c253cSXin Li required=False, 85*760c253cSXin Li nargs="+", 86*760c253cSXin Li help="options to use for the tryjob testing", 87*760c253cSXin Li ) 88*760c253cSXin Li 89*760c253cSXin Li # Add argument for the builder to use for the tryjob. 90*760c253cSXin Li parser.add_argument( 91*760c253cSXin Li "--builder", help="builder to use for the tryjob testing" 92*760c253cSXin Li ) 93*760c253cSXin Li 94*760c253cSXin Li # Add argument for a specific chroot path. 95*760c253cSXin Li parser.add_argument( 96*760c253cSXin Li "--chromeos_path", 97*760c253cSXin Li default=cros_root, 98*760c253cSXin Li help="the path to the chroot (default: %(default)s)", 99*760c253cSXin Li ) 100*760c253cSXin Li 101*760c253cSXin Li args_output = parser.parse_args() 102*760c253cSXin Li 103*760c253cSXin Li if not os.path.isfile( 104*760c253cSXin Li args_output.status_file 105*760c253cSXin Li ) or not args_output.status_file.endswith(".json"): 106*760c253cSXin Li raise ValueError( 107*760c253cSXin Li 'File does not exist or does not ending in ".json" ' 108*760c253cSXin Li ": %s" % args_output.status_file 109*760c253cSXin Li ) 110*760c253cSXin Li 111*760c253cSXin Li if ( 112*760c253cSXin Li args_output.modify_tryjob == ModifyTryjob.ADD.value 113*760c253cSXin Li and not args_output.builder 114*760c253cSXin Li ): 115*760c253cSXin Li raise ValueError("A builder is required for adding a tryjob.") 116*760c253cSXin Li elif ( 117*760c253cSXin Li args_output.modify_tryjob != ModifyTryjob.ADD.value 118*760c253cSXin Li and args_output.builder 119*760c253cSXin Li ): 120*760c253cSXin Li raise ValueError( 121*760c253cSXin Li "Specifying a builder is only available when adding a " "tryjob." 122*760c253cSXin Li ) 123*760c253cSXin Li 124*760c253cSXin Li return args_output 125*760c253cSXin Li 126*760c253cSXin Li 127*760c253cSXin Lidef GetCLAfterUpdatingPackages( 128*760c253cSXin Li packages: Iterable[str], 129*760c253cSXin Li git_hash: str, 130*760c253cSXin Li svn_version: int, 131*760c253cSXin Li chromeos_path: Union[Path, str], 132*760c253cSXin Li svn_option: Union[int, str], 133*760c253cSXin Li) -> git.CommitContents: 134*760c253cSXin Li """Updates the packages' LLVM_NEXT.""" 135*760c253cSXin Li 136*760c253cSXin Li change_list = update_chromeos_llvm_hash.UpdatePackages( 137*760c253cSXin Li packages=packages, 138*760c253cSXin Li manifest_packages=[], 139*760c253cSXin Li llvm_variant=update_chromeos_llvm_hash.LLVMVariant.next, 140*760c253cSXin Li git_hash=git_hash, 141*760c253cSXin Li svn_version=svn_version, 142*760c253cSXin Li chroot_opts=update_chromeos_llvm_hash.ChrootOpts(Path(chromeos_path)), 143*760c253cSXin Li mode=failure_modes.FailureModes.DISABLE_PATCHES, 144*760c253cSXin Li git_hash_source=svn_option, 145*760c253cSXin Li extra_commit_msg_lines=None, 146*760c253cSXin Li ) 147*760c253cSXin Li 148*760c253cSXin Li # We are calling UpdatePackages with upload_changes=True, in 149*760c253cSXin Li # which case it should always return a git.CommitContents value. 150*760c253cSXin Li assert change_list is not None 151*760c253cSXin Li print("\nSuccessfully updated packages to %d" % svn_version) 152*760c253cSXin Li print("Gerrit URL: %s" % change_list.url) 153*760c253cSXin Li print("Change list number: %d" % change_list.cl_number) 154*760c253cSXin Li 155*760c253cSXin Li return change_list 156*760c253cSXin Li 157*760c253cSXin Li 158*760c253cSXin Lidef CreateNewTryjobEntryForBisection( 159*760c253cSXin Li cl: int, 160*760c253cSXin Li extra_cls: List[int], 161*760c253cSXin Li options: List[str], 162*760c253cSXin Li builder: str, 163*760c253cSXin Li chromeos_path: Union[Path, str], 164*760c253cSXin Li cl_url: str, 165*760c253cSXin Li revision, 166*760c253cSXin Li) -> Dict: 167*760c253cSXin Li """Submits a tryjob and adds additional information.""" 168*760c253cSXin Li 169*760c253cSXin Li # Get the tryjob results after submitting the tryjob. 170*760c253cSXin Li # Format of 'tryjob_results': 171*760c253cSXin Li # [ 172*760c253cSXin Li # { 173*760c253cSXin Li # 'link' : [TRYJOB_LINK], 174*760c253cSXin Li # 'buildbucket_id' : [BUILDBUCKET_ID], 175*760c253cSXin Li # 'extra_cls' : [EXTRA_CLS_LIST], 176*760c253cSXin Li # 'options' : [EXTRA_OPTIONS_LIST], 177*760c253cSXin Li # 'builder' : [BUILDER_AS_A_LIST] 178*760c253cSXin Li # } 179*760c253cSXin Li # ] 180*760c253cSXin Li tryjob_results = update_packages_and_run_tests.RunTryJobs( 181*760c253cSXin Li cl, extra_cls, options, [builder], chromeos_path 182*760c253cSXin Li ) 183*760c253cSXin Li print("\nTryjob:") 184*760c253cSXin Li print(tryjob_results[0]) 185*760c253cSXin Li 186*760c253cSXin Li # Add necessary information about the tryjob. 187*760c253cSXin Li tryjob_results[0]["url"] = cl_url 188*760c253cSXin Li tryjob_results[0]["rev"] = revision 189*760c253cSXin Li tryjob_results[0][ 190*760c253cSXin Li "status" 191*760c253cSXin Li ] = update_tryjob_status.TryjobStatus.PENDING.value 192*760c253cSXin Li tryjob_results[0]["cl"] = cl 193*760c253cSXin Li 194*760c253cSXin Li return tryjob_results[0] 195*760c253cSXin Li 196*760c253cSXin Li 197*760c253cSXin Lidef AddTryjob( 198*760c253cSXin Li packages: Iterable[str], 199*760c253cSXin Li git_hash: str, 200*760c253cSXin Li revision: int, 201*760c253cSXin Li chromeos_path: Union[Path, str], 202*760c253cSXin Li extra_cls: List[int], 203*760c253cSXin Li options: List[str], 204*760c253cSXin Li builder: str, 205*760c253cSXin Li svn_option: Union[int, str], 206*760c253cSXin Li): 207*760c253cSXin Li """Submits a tryjob.""" 208*760c253cSXin Li 209*760c253cSXin Li change_list = GetCLAfterUpdatingPackages( 210*760c253cSXin Li packages, 211*760c253cSXin Li git_hash, 212*760c253cSXin Li revision, 213*760c253cSXin Li chromeos_path, 214*760c253cSXin Li svn_option, 215*760c253cSXin Li ) 216*760c253cSXin Li 217*760c253cSXin Li tryjob_dict = CreateNewTryjobEntryForBisection( 218*760c253cSXin Li change_list.cl_number, 219*760c253cSXin Li extra_cls, 220*760c253cSXin Li options, 221*760c253cSXin Li builder, 222*760c253cSXin Li chromeos_path, 223*760c253cSXin Li change_list.url, 224*760c253cSXin Li revision, 225*760c253cSXin Li ) 226*760c253cSXin Li 227*760c253cSXin Li return tryjob_dict 228*760c253cSXin Li 229*760c253cSXin Li 230*760c253cSXin Lidef PerformTryjobModification( 231*760c253cSXin Li revision: int, 232*760c253cSXin Li modify_tryjob: ModifyTryjob, 233*760c253cSXin Li status_file: Union[Path, str], 234*760c253cSXin Li extra_cls: List[int], 235*760c253cSXin Li options: List[str], 236*760c253cSXin Li builder: str, 237*760c253cSXin Li chromeos_path: Union[Path, str], 238*760c253cSXin Li) -> None: 239*760c253cSXin Li """Removes, relaunches, or adds a tryjob. 240*760c253cSXin Li 241*760c253cSXin Li Args: 242*760c253cSXin Li revision: The revision associated with the tryjob. 243*760c253cSXin Li modify_tryjob: What action to take on the tryjob. 244*760c253cSXin Li Ex: ModifyTryjob.REMOVE, ModifyTryjob.RELAUNCH, ModifyTryjob.ADD 245*760c253cSXin Li status_file: The .JSON file that contains the tryjobs. 246*760c253cSXin Li extra_cls: Extra change lists to be run alongside tryjob 247*760c253cSXin Li options: Extra options to pass into 'cros tryjob'. 248*760c253cSXin Li builder: The builder to use for 'cros tryjob'. 249*760c253cSXin Li chromeos_path: The absolute path to the chromeos checkout. 250*760c253cSXin Li """ 251*760c253cSXin Li 252*760c253cSXin Li # Format of 'bisect_contents': 253*760c253cSXin Li # { 254*760c253cSXin Li # 'start': [START_REVISION_OF_BISECTION] 255*760c253cSXin Li # 'end': [END_REVISION_OF_BISECTION] 256*760c253cSXin Li # 'jobs' : [ 257*760c253cSXin Li # {[TRYJOB_INFORMATION]}, 258*760c253cSXin Li # {[TRYJOB_INFORMATION]}, 259*760c253cSXin Li # ..., 260*760c253cSXin Li # {[TRYJOB_INFORMATION]} 261*760c253cSXin Li # ] 262*760c253cSXin Li # } 263*760c253cSXin Li with open(status_file, encoding="utf-8") as tryjobs: 264*760c253cSXin Li bisect_contents = json.load(tryjobs) 265*760c253cSXin Li 266*760c253cSXin Li if not bisect_contents["jobs"] and modify_tryjob != ModifyTryjob.ADD: 267*760c253cSXin Li sys.exit("No tryjobs in %s" % status_file) 268*760c253cSXin Li 269*760c253cSXin Li tryjob_index = update_tryjob_status.FindTryjobIndex( 270*760c253cSXin Li revision, bisect_contents["jobs"] 271*760c253cSXin Li ) 272*760c253cSXin Li 273*760c253cSXin Li # 'FindTryjobIndex()' returns None if the tryjob was not found. 274*760c253cSXin Li if tryjob_index is None and modify_tryjob != ModifyTryjob.ADD: 275*760c253cSXin Li raise ValueError( 276*760c253cSXin Li "Unable to find tryjob for %d in %s" % (revision, status_file) 277*760c253cSXin Li ) 278*760c253cSXin Li 279*760c253cSXin Li # Determine the action to take based off of 'modify_tryjob'. 280*760c253cSXin Li if modify_tryjob == ModifyTryjob.REMOVE: 281*760c253cSXin Li del bisect_contents["jobs"][tryjob_index] 282*760c253cSXin Li 283*760c253cSXin Li print("Successfully deleted the tryjob of revision %d" % revision) 284*760c253cSXin Li elif modify_tryjob == ModifyTryjob.RELAUNCH: 285*760c253cSXin Li # Need to update the tryjob link and buildbucket ID. 286*760c253cSXin Li tryjob_results = update_packages_and_run_tests.RunTryJobs( 287*760c253cSXin Li bisect_contents["jobs"][tryjob_index]["cl"], 288*760c253cSXin Li bisect_contents["jobs"][tryjob_index]["extra_cls"], 289*760c253cSXin Li bisect_contents["jobs"][tryjob_index]["options"], 290*760c253cSXin Li bisect_contents["jobs"][tryjob_index]["builder"], 291*760c253cSXin Li chromeos_path, 292*760c253cSXin Li ) 293*760c253cSXin Li 294*760c253cSXin Li bisect_contents["jobs"][tryjob_index][ 295*760c253cSXin Li "status" 296*760c253cSXin Li ] = update_tryjob_status.TryjobStatus.PENDING.value 297*760c253cSXin Li bisect_contents["jobs"][tryjob_index]["link"] = tryjob_results[0][ 298*760c253cSXin Li "link" 299*760c253cSXin Li ] 300*760c253cSXin Li bisect_contents["jobs"][tryjob_index][ 301*760c253cSXin Li "buildbucket_id" 302*760c253cSXin Li ] = tryjob_results[0]["buildbucket_id"] 303*760c253cSXin Li 304*760c253cSXin Li print( 305*760c253cSXin Li "Successfully relaunched the tryjob for revision %d and updated " 306*760c253cSXin Li "the tryjob link to %s" % (revision, tryjob_results[0]["link"]) 307*760c253cSXin Li ) 308*760c253cSXin Li elif modify_tryjob == ModifyTryjob.ADD: 309*760c253cSXin Li # Tryjob exists already. 310*760c253cSXin Li if tryjob_index is not None: 311*760c253cSXin Li raise ValueError( 312*760c253cSXin Li "Tryjob already exists (index is %d) in %s." 313*760c253cSXin Li % (tryjob_index, status_file) 314*760c253cSXin Li ) 315*760c253cSXin Li 316*760c253cSXin Li # Make sure the revision is within the bounds of the start and end of 317*760c253cSXin Li # the bisection. 318*760c253cSXin Li elif bisect_contents["start"] < revision < bisect_contents["end"]: 319*760c253cSXin Li ( 320*760c253cSXin Li git_hash, 321*760c253cSXin Li revision, 322*760c253cSXin Li ) = get_llvm_hash.GetLLVMHashAndVersionFromSVNOption(revision) 323*760c253cSXin Li 324*760c253cSXin Li tryjob_dict = AddTryjob( 325*760c253cSXin Li update_chromeos_llvm_hash.DEFAULT_PACKAGES, 326*760c253cSXin Li git_hash, 327*760c253cSXin Li revision, 328*760c253cSXin Li chromeos_path, 329*760c253cSXin Li extra_cls, 330*760c253cSXin Li options, 331*760c253cSXin Li builder, 332*760c253cSXin Li revision, 333*760c253cSXin Li ) 334*760c253cSXin Li 335*760c253cSXin Li bisect_contents["jobs"].append(tryjob_dict) 336*760c253cSXin Li 337*760c253cSXin Li print("Successfully added tryjob of revision %d" % revision) 338*760c253cSXin Li else: 339*760c253cSXin Li raise ValueError("Failed to add tryjob to %s" % status_file) 340*760c253cSXin Li else: 341*760c253cSXin Li raise ValueError( 342*760c253cSXin Li 'Invalid "modify_tryjob" option provided: %s' % modify_tryjob 343*760c253cSXin Li ) 344*760c253cSXin Li 345*760c253cSXin Li with open(status_file, "w", encoding="utf-8") as update_tryjobs: 346*760c253cSXin Li json.dump( 347*760c253cSXin Li bisect_contents, update_tryjobs, indent=4, separators=(",", ": ") 348*760c253cSXin Li ) 349*760c253cSXin Li 350*760c253cSXin Li 351*760c253cSXin Lidef main() -> None: 352*760c253cSXin Li """Removes, relaunches, or adds a tryjob.""" 353*760c253cSXin Li 354*760c253cSXin Li chroot.VerifyOutsideChroot() 355*760c253cSXin Li 356*760c253cSXin Li args_output = GetCommandLineArgs() 357*760c253cSXin Li 358*760c253cSXin Li chroot.VerifyChromeOSRoot(args_output.chromeos_path) 359*760c253cSXin Li 360*760c253cSXin Li PerformTryjobModification( 361*760c253cSXin Li args_output.revision, 362*760c253cSXin Li ModifyTryjob(args_output.modify_tryjob), 363*760c253cSXin Li args_output.status_file, 364*760c253cSXin Li args_output.extra_change_lists, 365*760c253cSXin Li args_output.options, 366*760c253cSXin Li args_output.builder, 367*760c253cSXin Li args_output.chromeos_path, 368*760c253cSXin Li ) 369*760c253cSXin Li 370*760c253cSXin Li 371*760c253cSXin Liif __name__ == "__main__": 372*760c253cSXin Li main() 373