1*bb4ee6a4SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*bb4ee6a4SAndroid Build Coastguard Worker# Copyright 2022 The ChromiumOS Authors 3*bb4ee6a4SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 4*bb4ee6a4SAndroid Build Coastguard Worker# found in the LICENSE file. 5*bb4ee6a4SAndroid Build Coastguard Worker 6*bb4ee6a4SAndroid Build Coastguard Worker# This script is used by the CI system to regularly update the merge and dry run changes. 7*bb4ee6a4SAndroid Build Coastguard Worker# 8*bb4ee6a4SAndroid Build Coastguard Worker# It can be run locally as well, however some permissions are only given to the bot's service 9*bb4ee6a4SAndroid Build Coastguard Worker# account (and are enabled with --is-bot). 10*bb4ee6a4SAndroid Build Coastguard Worker# 11*bb4ee6a4SAndroid Build Coastguard Worker# See `./tools/chromeos/merge_bot -h` for details. 12*bb4ee6a4SAndroid Build Coastguard Worker# 13*bb4ee6a4SAndroid Build Coastguard Worker# When testing this script locally, use MERGE_BOT_TEST=1 ./tools/chromeos/merge_bot 14*bb4ee6a4SAndroid Build Coastguard Worker# to use different tags and prevent emails from being sent or the CQ from being triggered. 15*bb4ee6a4SAndroid Build Coastguard Worker 16*bb4ee6a4SAndroid Build Coastguard Workerfrom contextlib import contextmanager 17*bb4ee6a4SAndroid Build Coastguard Workerimport os 18*bb4ee6a4SAndroid Build Coastguard Workerfrom pathlib import Path 19*bb4ee6a4SAndroid Build Coastguard Workerimport sys 20*bb4ee6a4SAndroid Build Coastguard Workerfrom datetime import date 21*bb4ee6a4SAndroid Build Coastguard Workerfrom typing import List 22*bb4ee6a4SAndroid Build Coastguard Workerimport random 23*bb4ee6a4SAndroid Build Coastguard Workerimport string 24*bb4ee6a4SAndroid Build Coastguard Worker 25*bb4ee6a4SAndroid Build Coastguard Workersys.path.append(os.path.dirname(sys.path[0])) 26*bb4ee6a4SAndroid Build Coastguard Worker 27*bb4ee6a4SAndroid Build Coastguard Workerimport re 28*bb4ee6a4SAndroid Build Coastguard Worker 29*bb4ee6a4SAndroid Build Coastguard Workerfrom impl.common import CROSVM_ROOT, batched, cmd, quoted, run_commands, GerritChange, GERRIT_URL 30*bb4ee6a4SAndroid Build Coastguard Worker 31*bb4ee6a4SAndroid Build Coastguard Workergit = cmd("git") 32*bb4ee6a4SAndroid Build Coastguard Workergit_log = git("log --decorate=no --color=never") 33*bb4ee6a4SAndroid Build Coastguard Workercurl = cmd("curl --silent --fail") 34*bb4ee6a4SAndroid Build Coastguard Workerchmod = cmd("chmod") 35*bb4ee6a4SAndroid Build Coastguard Workerdev_container = cmd("tools/dev_container") 36*bb4ee6a4SAndroid Build Coastguard Workermkdir = cmd("mkdir -p") 37*bb4ee6a4SAndroid Build Coastguard Worker 38*bb4ee6a4SAndroid Build Coastguard WorkerUPSTREAM_URL = "https://chromium.googlesource.com/crosvm/crosvm" 39*bb4ee6a4SAndroid Build Coastguard WorkerCROS_URL = "https://chromium.googlesource.com/chromiumos/platform/crosvm" 40*bb4ee6a4SAndroid Build Coastguard Worker 41*bb4ee6a4SAndroid Build Coastguard Worker# Gerrit tags used to identify bot changes. 42*bb4ee6a4SAndroid Build Coastguard WorkerTESTING = "MERGE_BOT_TEST" in os.environ 43*bb4ee6a4SAndroid Build Coastguard Workerif TESTING: 44*bb4ee6a4SAndroid Build Coastguard Worker MERGE_TAG = "testing-crosvm-merge" 45*bb4ee6a4SAndroid Build Coastguard Worker DRY_RUN_TAG = "testing-crosvm-merge-dry-run" 46*bb4ee6a4SAndroid Build Coastguard Workerelse: 47*bb4ee6a4SAndroid Build Coastguard Worker MERGE_TAG = "crosvm-merge" # type: ignore 48*bb4ee6a4SAndroid Build Coastguard Worker DRY_RUN_TAG = "crosvm-merge-dry-run" # type: ignore 49*bb4ee6a4SAndroid Build Coastguard Worker 50*bb4ee6a4SAndroid Build Coastguard Worker# This is the email of the account that posts CQ messages. 51*bb4ee6a4SAndroid Build Coastguard WorkerLUCI_EMAIL = "[email protected]" 52*bb4ee6a4SAndroid Build Coastguard Worker 53*bb4ee6a4SAndroid Build Coastguard Worker# Do not create more dry runs than this within a 24h timespan 54*bb4ee6a4SAndroid Build Coastguard WorkerMAX_DRY_RUNS_PER_DAY = 2 55*bb4ee6a4SAndroid Build Coastguard Worker 56*bb4ee6a4SAndroid Build Coastguard Worker 57*bb4ee6a4SAndroid Build Coastguard Workerdef list_active_merges(): 58*bb4ee6a4SAndroid Build Coastguard Worker return GerritChange.query( 59*bb4ee6a4SAndroid Build Coastguard Worker "project:chromiumos/platform/crosvm", 60*bb4ee6a4SAndroid Build Coastguard Worker "branch:chromeos", 61*bb4ee6a4SAndroid Build Coastguard Worker "status:open", 62*bb4ee6a4SAndroid Build Coastguard Worker f"hashtag:{MERGE_TAG}", 63*bb4ee6a4SAndroid Build Coastguard Worker ) 64*bb4ee6a4SAndroid Build Coastguard Worker 65*bb4ee6a4SAndroid Build Coastguard Worker 66*bb4ee6a4SAndroid Build Coastguard Workerdef list_active_dry_runs(): 67*bb4ee6a4SAndroid Build Coastguard Worker return GerritChange.query( 68*bb4ee6a4SAndroid Build Coastguard Worker "project:chromiumos/platform/crosvm", 69*bb4ee6a4SAndroid Build Coastguard Worker "branch:chromeos", 70*bb4ee6a4SAndroid Build Coastguard Worker "status:open", 71*bb4ee6a4SAndroid Build Coastguard Worker f"hashtag:{DRY_RUN_TAG}", 72*bb4ee6a4SAndroid Build Coastguard Worker ) 73*bb4ee6a4SAndroid Build Coastguard Worker 74*bb4ee6a4SAndroid Build Coastguard Worker 75*bb4ee6a4SAndroid Build Coastguard Workerdef list_recent_dry_runs(age: str): 76*bb4ee6a4SAndroid Build Coastguard Worker return GerritChange.query( 77*bb4ee6a4SAndroid Build Coastguard Worker "project:chromiumos/platform/crosvm", 78*bb4ee6a4SAndroid Build Coastguard Worker "branch:chromeos", 79*bb4ee6a4SAndroid Build Coastguard Worker f"-age:{age}", 80*bb4ee6a4SAndroid Build Coastguard Worker f"hashtag:{DRY_RUN_TAG}", 81*bb4ee6a4SAndroid Build Coastguard Worker ) 82*bb4ee6a4SAndroid Build Coastguard Worker 83*bb4ee6a4SAndroid Build Coastguard Worker 84*bb4ee6a4SAndroid Build Coastguard Workerdef bug_notes(commit_range: str): 85*bb4ee6a4SAndroid Build Coastguard Worker "Returns a string with all BUG=... lines of the specified commit range." 86*bb4ee6a4SAndroid Build Coastguard Worker return "\n".join( 87*bb4ee6a4SAndroid Build Coastguard Worker set( 88*bb4ee6a4SAndroid Build Coastguard Worker line 89*bb4ee6a4SAndroid Build Coastguard Worker for line in git_log(commit_range, "--pretty=%b").lines() 90*bb4ee6a4SAndroid Build Coastguard Worker if re.match(r"^BUG=", line, re.I) and not re.match(r"^BUG=None", line, re.I) 91*bb4ee6a4SAndroid Build Coastguard Worker ) 92*bb4ee6a4SAndroid Build Coastguard Worker ) 93*bb4ee6a4SAndroid Build Coastguard Worker 94*bb4ee6a4SAndroid Build Coastguard Worker 95*bb4ee6a4SAndroid Build Coastguard Workerdef setup_tracking_branch(branch_name: str, tracking: str): 96*bb4ee6a4SAndroid Build Coastguard Worker "Create and checkout `branch_name` tracking `tracking`. Overwrites existing branch." 97*bb4ee6a4SAndroid Build Coastguard Worker git("fetch -q cros", tracking).fg() 98*bb4ee6a4SAndroid Build Coastguard Worker git("checkout", f"cros/{tracking}").fg(quiet=True) 99*bb4ee6a4SAndroid Build Coastguard Worker git("branch -D", branch_name).fg(quiet=True, check=False) 100*bb4ee6a4SAndroid Build Coastguard Worker git("checkout -b", branch_name, "--track", f"cros/{tracking}").fg() 101*bb4ee6a4SAndroid Build Coastguard Worker 102*bb4ee6a4SAndroid Build Coastguard Worker 103*bb4ee6a4SAndroid Build Coastguard Worker@contextmanager 104*bb4ee6a4SAndroid Build Coastguard Workerdef tracking_branch_context(branch_name: str, tracking: str): 105*bb4ee6a4SAndroid Build Coastguard Worker "Switches to a tracking branch and back after the context is exited." 106*bb4ee6a4SAndroid Build Coastguard Worker # Remember old head. Prefer branch name if available, otherwise revision of detached head. 107*bb4ee6a4SAndroid Build Coastguard Worker old_head = git("symbolic-ref -q --short HEAD").stdout(check=False) 108*bb4ee6a4SAndroid Build Coastguard Worker if not old_head: 109*bb4ee6a4SAndroid Build Coastguard Worker old_head = git("rev-parse HEAD").stdout() 110*bb4ee6a4SAndroid Build Coastguard Worker setup_tracking_branch(branch_name, tracking) 111*bb4ee6a4SAndroid Build Coastguard Worker yield 112*bb4ee6a4SAndroid Build Coastguard Worker git("checkout", old_head).fg() 113*bb4ee6a4SAndroid Build Coastguard Worker 114*bb4ee6a4SAndroid Build Coastguard Worker 115*bb4ee6a4SAndroid Build Coastguard Workerdef gerrit_prerequisites(): 116*bb4ee6a4SAndroid Build Coastguard Worker "Make sure we can upload to gerrit." 117*bb4ee6a4SAndroid Build Coastguard Worker 118*bb4ee6a4SAndroid Build Coastguard Worker # Setup cros remote which we are merging into 119*bb4ee6a4SAndroid Build Coastguard Worker if git("remote get-url cros").fg(check=False) != 0: 120*bb4ee6a4SAndroid Build Coastguard Worker print("Setting up remote: cros") 121*bb4ee6a4SAndroid Build Coastguard Worker git("remote add cros", CROS_URL).fg() 122*bb4ee6a4SAndroid Build Coastguard Worker actual_remote = git("remote get-url cros").stdout() 123*bb4ee6a4SAndroid Build Coastguard Worker if actual_remote != CROS_URL: 124*bb4ee6a4SAndroid Build Coastguard Worker print(f"WARNING: Your remote 'cros' is {actual_remote} and does not match {CROS_URL}") 125*bb4ee6a4SAndroid Build Coastguard Worker 126*bb4ee6a4SAndroid Build Coastguard Worker # Install gerrit Change-Id hook 127*bb4ee6a4SAndroid Build Coastguard Worker hook_path = CROSVM_ROOT / ".git/hooks/commit-msg" 128*bb4ee6a4SAndroid Build Coastguard Worker if not hook_path.exists(): 129*bb4ee6a4SAndroid Build Coastguard Worker hook_path.parent.mkdir(exist_ok=True) 130*bb4ee6a4SAndroid Build Coastguard Worker curl(f"{GERRIT_URL}/tools/hooks/commit-msg").write_to(hook_path) 131*bb4ee6a4SAndroid Build Coastguard Worker chmod("+x", hook_path).fg() 132*bb4ee6a4SAndroid Build Coastguard Worker 133*bb4ee6a4SAndroid Build Coastguard Worker 134*bb4ee6a4SAndroid Build Coastguard Workerdef upload_to_gerrit(target_branch: str, *extra_params: str): 135*bb4ee6a4SAndroid Build Coastguard Worker if not TESTING: 136*bb4ee6a4SAndroid Build Coastguard Worker extra_params = ("[email protected]", *extra_params) 137*bb4ee6a4SAndroid Build Coastguard Worker for i in range(3): 138*bb4ee6a4SAndroid Build Coastguard Worker try: 139*bb4ee6a4SAndroid Build Coastguard Worker print(f"Uploading to gerrit (Attempt {i})") 140*bb4ee6a4SAndroid Build Coastguard Worker git(f"push cros HEAD:refs/for/{target_branch}%{','.join(extra_params)}").fg() 141*bb4ee6a4SAndroid Build Coastguard Worker return 142*bb4ee6a4SAndroid Build Coastguard Worker except: 143*bb4ee6a4SAndroid Build Coastguard Worker continue 144*bb4ee6a4SAndroid Build Coastguard Worker raise Exception("Could not upload changes to gerrit.") 145*bb4ee6a4SAndroid Build Coastguard Worker 146*bb4ee6a4SAndroid Build Coastguard Worker 147*bb4ee6a4SAndroid Build Coastguard Workerdef rename_files_to_random(dir_path: str): 148*bb4ee6a4SAndroid Build Coastguard Worker "Rename all files in a folder to random file names with extension kept" 149*bb4ee6a4SAndroid Build Coastguard Worker print("Renaming all files in " + dir_path) 150*bb4ee6a4SAndroid Build Coastguard Worker file_names = os.listdir(dir_path) 151*bb4ee6a4SAndroid Build Coastguard Worker for file_name in filter(os.path.isfile, map(lambda x: os.path.join(dir_path, x), file_names)): 152*bb4ee6a4SAndroid Build Coastguard Worker file_extension = os.path.splitext(file_name)[1] 153*bb4ee6a4SAndroid Build Coastguard Worker new_name_stem = "".join( 154*bb4ee6a4SAndroid Build Coastguard Worker random.choice(string.ascii_lowercase + string.digits) for _ in range(16) 155*bb4ee6a4SAndroid Build Coastguard Worker ) 156*bb4ee6a4SAndroid Build Coastguard Worker new_path = os.path.join(dir_path, new_name_stem + file_extension) 157*bb4ee6a4SAndroid Build Coastguard Worker print(f"Renaming {file_name} to {new_path}") 158*bb4ee6a4SAndroid Build Coastguard Worker os.rename(file_name, new_path) 159*bb4ee6a4SAndroid Build Coastguard Worker 160*bb4ee6a4SAndroid Build Coastguard Worker 161*bb4ee6a4SAndroid Build Coastguard Workerdef create_pgo_profile(): 162*bb4ee6a4SAndroid Build Coastguard Worker "Create PGO profile matching HEAD at merge." 163*bb4ee6a4SAndroid Build Coastguard Worker has_kvm = os.path.exists("/dev/kvm") 164*bb4ee6a4SAndroid Build Coastguard Worker if not has_kvm: 165*bb4ee6a4SAndroid Build Coastguard Worker return 166*bb4ee6a4SAndroid Build Coastguard Worker os.chdir(CROSVM_ROOT) 167*bb4ee6a4SAndroid Build Coastguard Worker tmpdirname = "target/pgotmp/" + "".join( 168*bb4ee6a4SAndroid Build Coastguard Worker random.choice(string.ascii_lowercase + string.digits) for _ in range(16) 169*bb4ee6a4SAndroid Build Coastguard Worker ) 170*bb4ee6a4SAndroid Build Coastguard Worker mkdir(tmpdirname).fg() 171*bb4ee6a4SAndroid Build Coastguard Worker benchmark_list = list( 172*bb4ee6a4SAndroid Build Coastguard Worker map( 173*bb4ee6a4SAndroid Build Coastguard Worker lambda x: os.path.splitext(x)[0], 174*bb4ee6a4SAndroid Build Coastguard Worker filter(lambda x: x.endswith(".rs"), os.listdir("e2e_tests/benches")), 175*bb4ee6a4SAndroid Build Coastguard Worker ) 176*bb4ee6a4SAndroid Build Coastguard Worker ) 177*bb4ee6a4SAndroid Build Coastguard Worker print(f"Building instrumented binary, perf data will be saved to {tmpdirname}") 178*bb4ee6a4SAndroid Build Coastguard Worker dev_container( 179*bb4ee6a4SAndroid Build Coastguard Worker "./tools/build_release --build-profile release --profile-generate /workspace/" + tmpdirname 180*bb4ee6a4SAndroid Build Coastguard Worker ).fg() 181*bb4ee6a4SAndroid Build Coastguard Worker print() 182*bb4ee6a4SAndroid Build Coastguard Worker print("List of benchmarks to run:") 183*bb4ee6a4SAndroid Build Coastguard Worker for bench_name in benchmark_list: 184*bb4ee6a4SAndroid Build Coastguard Worker print(bench_name) 185*bb4ee6a4SAndroid Build Coastguard Worker print() 186*bb4ee6a4SAndroid Build Coastguard Worker dev_container("mkdir -p /var/empty").fg() 187*bb4ee6a4SAndroid Build Coastguard Worker for bench_name in benchmark_list: 188*bb4ee6a4SAndroid Build Coastguard Worker print(f"Running bechmark: {bench_name}") 189*bb4ee6a4SAndroid Build Coastguard Worker dev_container(f"./tools/bench {bench_name}").fg() 190*bb4ee6a4SAndroid Build Coastguard Worker # Instrumented binary always give same file name to generated .profraw files, rename to avoid 191*bb4ee6a4SAndroid Build Coastguard Worker # overwriting profile from previous bench suite 192*bb4ee6a4SAndroid Build Coastguard Worker rename_files_to_random(tmpdirname) 193*bb4ee6a4SAndroid Build Coastguard Worker mkdir("profiles").fg() 194*bb4ee6a4SAndroid Build Coastguard Worker dev_container( 195*bb4ee6a4SAndroid Build Coastguard Worker f"cargo profdata -- merge -o /workspace/profiles/benchmarks.profdata /workspace/{tmpdirname}" 196*bb4ee6a4SAndroid Build Coastguard Worker ).fg() 197*bb4ee6a4SAndroid Build Coastguard Worker dev_container("xz -f -9e -T 0 /workspace/profiles/benchmarks.profdata").fg() 198*bb4ee6a4SAndroid Build Coastguard Worker 199*bb4ee6a4SAndroid Build Coastguard Worker 200*bb4ee6a4SAndroid Build Coastguard Worker#################################################################################################### 201*bb4ee6a4SAndroid Build Coastguard Worker# The functions below are callable via the command line 202*bb4ee6a4SAndroid Build Coastguard Worker 203*bb4ee6a4SAndroid Build Coastguard Worker 204*bb4ee6a4SAndroid Build Coastguard Workerdef create_merge_commits( 205*bb4ee6a4SAndroid Build Coastguard Worker revision: str, max_size: int = 0, create_dry_run: bool = False, force_pgo: bool = False 206*bb4ee6a4SAndroid Build Coastguard Worker): 207*bb4ee6a4SAndroid Build Coastguard Worker "Merges `revision` into HEAD, creating merge commits including at most `max-size` commits." 208*bb4ee6a4SAndroid Build Coastguard Worker os.chdir(CROSVM_ROOT) 209*bb4ee6a4SAndroid Build Coastguard Worker 210*bb4ee6a4SAndroid Build Coastguard Worker # Find list of commits to merge, then batch them into smaller merges. 211*bb4ee6a4SAndroid Build Coastguard Worker commits = git_log(f"HEAD..{revision}", "--pretty=%H").lines() 212*bb4ee6a4SAndroid Build Coastguard Worker if not commits: 213*bb4ee6a4SAndroid Build Coastguard Worker print("Nothing to merge.") 214*bb4ee6a4SAndroid Build Coastguard Worker return (0, False) 215*bb4ee6a4SAndroid Build Coastguard Worker else: 216*bb4ee6a4SAndroid Build Coastguard Worker commit_authors = git_log(f"HEAD..{revision}", "--pretty=%an").lines() 217*bb4ee6a4SAndroid Build Coastguard Worker if all(map(lambda x: x == "recipe-roller", commit_authors)): 218*bb4ee6a4SAndroid Build Coastguard Worker print("All commits are from recipe roller, don't merge yet") 219*bb4ee6a4SAndroid Build Coastguard Worker return (0, False) 220*bb4ee6a4SAndroid Build Coastguard Worker 221*bb4ee6a4SAndroid Build Coastguard Worker # Create a merge commit for each batch 222*bb4ee6a4SAndroid Build Coastguard Worker batches = list(batched(commits, max_size)) if max_size > 0 else [commits] 223*bb4ee6a4SAndroid Build Coastguard Worker has_conflicts = False 224*bb4ee6a4SAndroid Build Coastguard Worker for i, batch in enumerate(reversed(batches)): 225*bb4ee6a4SAndroid Build Coastguard Worker target = batch[0] 226*bb4ee6a4SAndroid Build Coastguard Worker previous_rev = git(f"rev-parse {batch[-1]}^").stdout() 227*bb4ee6a4SAndroid Build Coastguard Worker commit_range = f"{previous_rev}..{batch[0]}" 228*bb4ee6a4SAndroid Build Coastguard Worker 229*bb4ee6a4SAndroid Build Coastguard Worker # Put together a message containing info about what's in the merge. 230*bb4ee6a4SAndroid Build Coastguard Worker batch_str = f"{i + 1}/{len(batches)}" if len(batches) > 1 else "" 231*bb4ee6a4SAndroid Build Coastguard Worker title = "Merge with upstream" if not create_dry_run else f"Merge dry run" 232*bb4ee6a4SAndroid Build Coastguard Worker message = "\n\n".join( 233*bb4ee6a4SAndroid Build Coastguard Worker [ 234*bb4ee6a4SAndroid Build Coastguard Worker f"{title} {date.today().isoformat()} {batch_str}", 235*bb4ee6a4SAndroid Build Coastguard Worker git_log(commit_range, "--oneline").stdout(), 236*bb4ee6a4SAndroid Build Coastguard Worker f"{UPSTREAM_URL}/+log/{commit_range}", 237*bb4ee6a4SAndroid Build Coastguard Worker *([bug_notes(commit_range)] if not create_dry_run else []), 238*bb4ee6a4SAndroid Build Coastguard Worker ] 239*bb4ee6a4SAndroid Build Coastguard Worker ) 240*bb4ee6a4SAndroid Build Coastguard Worker 241*bb4ee6a4SAndroid Build Coastguard Worker # git 'trailers' go into a separate paragraph to make sure they are properly separated. 242*bb4ee6a4SAndroid Build Coastguard Worker trailers = "Commit: False" if create_dry_run or TESTING else "" 243*bb4ee6a4SAndroid Build Coastguard Worker 244*bb4ee6a4SAndroid Build Coastguard Worker # Perfom merge 245*bb4ee6a4SAndroid Build Coastguard Worker code = git("merge --no-ff", target, "-m", quoted(message), "-m", quoted(trailers)).fg( 246*bb4ee6a4SAndroid Build Coastguard Worker check=False 247*bb4ee6a4SAndroid Build Coastguard Worker ) 248*bb4ee6a4SAndroid Build Coastguard Worker if code != 0: 249*bb4ee6a4SAndroid Build Coastguard Worker if not Path(".git/MERGE_HEAD").exists(): 250*bb4ee6a4SAndroid Build Coastguard Worker raise Exception("git merge failed for a reason other than merge conflicts.") 251*bb4ee6a4SAndroid Build Coastguard Worker print("Merge has conflicts. Creating commit with conflict markers.") 252*bb4ee6a4SAndroid Build Coastguard Worker git("add --update .").fg() 253*bb4ee6a4SAndroid Build Coastguard Worker message = f"(CONFLICT) {message}" 254*bb4ee6a4SAndroid Build Coastguard Worker git("commit", "-m", quoted(message), "-m", quoted(trailers)).fg() 255*bb4ee6a4SAndroid Build Coastguard Worker has_conflicts = True 256*bb4ee6a4SAndroid Build Coastguard Worker # Only uprev PGO profile on Monday to reduce impact on repo size 257*bb4ee6a4SAndroid Build Coastguard Worker # TODO: b/181105093 - Re-evaluate throttling strategy after sometime 258*bb4ee6a4SAndroid Build Coastguard Worker if date.today().weekday() == 0 or force_pgo: 259*bb4ee6a4SAndroid Build Coastguard Worker create_pgo_profile() 260*bb4ee6a4SAndroid Build Coastguard Worker git("add profiles/benchmarks.profdata.xz").fg() 261*bb4ee6a4SAndroid Build Coastguard Worker git("commit --amend --no-edit").fg() 262*bb4ee6a4SAndroid Build Coastguard Worker 263*bb4ee6a4SAndroid Build Coastguard Worker return (len(batches), has_conflicts) 264*bb4ee6a4SAndroid Build Coastguard Worker 265*bb4ee6a4SAndroid Build Coastguard Worker 266*bb4ee6a4SAndroid Build Coastguard Workerdef status(): 267*bb4ee6a4SAndroid Build Coastguard Worker "Shows the current status of pending merge and dry run changes in gerrit." 268*bb4ee6a4SAndroid Build Coastguard Worker print("Active dry runs:") 269*bb4ee6a4SAndroid Build Coastguard Worker for dry_run in list_active_dry_runs(): 270*bb4ee6a4SAndroid Build Coastguard Worker print(dry_run.pretty_info()) 271*bb4ee6a4SAndroid Build Coastguard Worker print() 272*bb4ee6a4SAndroid Build Coastguard Worker print("Active merges:") 273*bb4ee6a4SAndroid Build Coastguard Worker for merge in list_active_merges(): 274*bb4ee6a4SAndroid Build Coastguard Worker print(merge.pretty_info()) 275*bb4ee6a4SAndroid Build Coastguard Worker 276*bb4ee6a4SAndroid Build Coastguard Worker 277*bb4ee6a4SAndroid Build Coastguard Workerdef update_merges( 278*bb4ee6a4SAndroid Build Coastguard Worker revision: str, 279*bb4ee6a4SAndroid Build Coastguard Worker target_branch: str = "chromeos", 280*bb4ee6a4SAndroid Build Coastguard Worker max_size: int = 15, 281*bb4ee6a4SAndroid Build Coastguard Worker is_bot: bool = False, 282*bb4ee6a4SAndroid Build Coastguard Worker): 283*bb4ee6a4SAndroid Build Coastguard Worker """Uploads a new set of merge commits if the previous batch has been submitted.""" 284*bb4ee6a4SAndroid Build Coastguard Worker gerrit_prerequisites() 285*bb4ee6a4SAndroid Build Coastguard Worker parsed_revision = git("rev-parse", revision).stdout() 286*bb4ee6a4SAndroid Build Coastguard Worker 287*bb4ee6a4SAndroid Build Coastguard Worker active_merges = list_active_merges() 288*bb4ee6a4SAndroid Build Coastguard Worker if active_merges: 289*bb4ee6a4SAndroid Build Coastguard Worker print("Nothing to do. Previous merges are still pending:") 290*bb4ee6a4SAndroid Build Coastguard Worker for merge in active_merges: 291*bb4ee6a4SAndroid Build Coastguard Worker print(merge.pretty_info()) 292*bb4ee6a4SAndroid Build Coastguard Worker return 293*bb4ee6a4SAndroid Build Coastguard Worker else: 294*bb4ee6a4SAndroid Build Coastguard Worker print(f"Creating merge of {parsed_revision} into cros/{target_branch}") 295*bb4ee6a4SAndroid Build Coastguard Worker with tracking_branch_context("merge-bot-branch", target_branch): 296*bb4ee6a4SAndroid Build Coastguard Worker count, has_conflicts = create_merge_commits( 297*bb4ee6a4SAndroid Build Coastguard Worker parsed_revision, max_size, create_dry_run=False 298*bb4ee6a4SAndroid Build Coastguard Worker ) 299*bb4ee6a4SAndroid Build Coastguard Worker if count > 0: 300*bb4ee6a4SAndroid Build Coastguard Worker labels: List[str] = [] 301*bb4ee6a4SAndroid Build Coastguard Worker if not has_conflicts: 302*bb4ee6a4SAndroid Build Coastguard Worker if not TESTING: 303*bb4ee6a4SAndroid Build Coastguard Worker labels.append("l=Commit-Queue+1") 304*bb4ee6a4SAndroid Build Coastguard Worker if is_bot: 305*bb4ee6a4SAndroid Build Coastguard Worker labels.append("l=Bot-Commit+1") 306*bb4ee6a4SAndroid Build Coastguard Worker upload_to_gerrit(target_branch, f"hashtag={MERGE_TAG}", *labels) 307*bb4ee6a4SAndroid Build Coastguard Worker 308*bb4ee6a4SAndroid Build Coastguard Worker 309*bb4ee6a4SAndroid Build Coastguard Workerdef update_dry_runs( 310*bb4ee6a4SAndroid Build Coastguard Worker revision: str, 311*bb4ee6a4SAndroid Build Coastguard Worker target_branch: str = "chromeos", 312*bb4ee6a4SAndroid Build Coastguard Worker max_size: int = 0, 313*bb4ee6a4SAndroid Build Coastguard Worker is_bot: bool = False, 314*bb4ee6a4SAndroid Build Coastguard Worker): 315*bb4ee6a4SAndroid Build Coastguard Worker """ 316*bb4ee6a4SAndroid Build Coastguard Worker Maintains dry run changes in gerrit, usually run by the crosvm bot, but can be called by 317*bb4ee6a4SAndroid Build Coastguard Worker developers as well. 318*bb4ee6a4SAndroid Build Coastguard Worker """ 319*bb4ee6a4SAndroid Build Coastguard Worker gerrit_prerequisites() 320*bb4ee6a4SAndroid Build Coastguard Worker parsed_revision = git("rev-parse", revision).stdout() 321*bb4ee6a4SAndroid Build Coastguard Worker 322*bb4ee6a4SAndroid Build Coastguard Worker # Close active dry runs if they are done. 323*bb4ee6a4SAndroid Build Coastguard Worker print("Checking active dry runs") 324*bb4ee6a4SAndroid Build Coastguard Worker for dry_run in list_active_dry_runs(): 325*bb4ee6a4SAndroid Build Coastguard Worker cq_votes = dry_run.get_votes("Commit-Queue") 326*bb4ee6a4SAndroid Build Coastguard Worker if not cq_votes or max(cq_votes) > 0: 327*bb4ee6a4SAndroid Build Coastguard Worker print(dry_run, "CQ is still running.") 328*bb4ee6a4SAndroid Build Coastguard Worker continue 329*bb4ee6a4SAndroid Build Coastguard Worker 330*bb4ee6a4SAndroid Build Coastguard Worker # Check for luci results and add V+-1 votes to make it easier to identify failed dry runs. 331*bb4ee6a4SAndroid Build Coastguard Worker luci_messages = dry_run.get_messages_by(LUCI_EMAIL) 332*bb4ee6a4SAndroid Build Coastguard Worker if not luci_messages: 333*bb4ee6a4SAndroid Build Coastguard Worker print(dry_run, "No luci messages yet.") 334*bb4ee6a4SAndroid Build Coastguard Worker continue 335*bb4ee6a4SAndroid Build Coastguard Worker 336*bb4ee6a4SAndroid Build Coastguard Worker last_luci_message = luci_messages[-1] 337*bb4ee6a4SAndroid Build Coastguard Worker if "This CL passed the CQ dry run" in last_luci_message or ( 338*bb4ee6a4SAndroid Build Coastguard Worker "This CL has passed the run" in last_luci_message 339*bb4ee6a4SAndroid Build Coastguard Worker ): 340*bb4ee6a4SAndroid Build Coastguard Worker dry_run.review( 341*bb4ee6a4SAndroid Build Coastguard Worker "I think this dry run was SUCCESSFUL.", 342*bb4ee6a4SAndroid Build Coastguard Worker { 343*bb4ee6a4SAndroid Build Coastguard Worker "Verified": 1, 344*bb4ee6a4SAndroid Build Coastguard Worker "Bot-Commit": 0, 345*bb4ee6a4SAndroid Build Coastguard Worker }, 346*bb4ee6a4SAndroid Build Coastguard Worker ) 347*bb4ee6a4SAndroid Build Coastguard Worker elif "Failed builds" in last_luci_message or ( 348*bb4ee6a4SAndroid Build Coastguard Worker "This CL has failed the run. Reason:" in last_luci_message 349*bb4ee6a4SAndroid Build Coastguard Worker ): 350*bb4ee6a4SAndroid Build Coastguard Worker dry_run.review( 351*bb4ee6a4SAndroid Build Coastguard Worker "I think this dry run FAILED.", 352*bb4ee6a4SAndroid Build Coastguard Worker { 353*bb4ee6a4SAndroid Build Coastguard Worker "Verified": -1, 354*bb4ee6a4SAndroid Build Coastguard Worker "Bot-Commit": 0, 355*bb4ee6a4SAndroid Build Coastguard Worker }, 356*bb4ee6a4SAndroid Build Coastguard Worker ) 357*bb4ee6a4SAndroid Build Coastguard Worker 358*bb4ee6a4SAndroid Build Coastguard Worker dry_run.abandon("Dry completed.") 359*bb4ee6a4SAndroid Build Coastguard Worker 360*bb4ee6a4SAndroid Build Coastguard Worker active_dry_runs = list_active_dry_runs() 361*bb4ee6a4SAndroid Build Coastguard Worker if active_dry_runs: 362*bb4ee6a4SAndroid Build Coastguard Worker print("There are active dry runs, not creating a new one.") 363*bb4ee6a4SAndroid Build Coastguard Worker print("Active dry runs:") 364*bb4ee6a4SAndroid Build Coastguard Worker for dry_run in active_dry_runs: 365*bb4ee6a4SAndroid Build Coastguard Worker print(dry_run.pretty_info()) 366*bb4ee6a4SAndroid Build Coastguard Worker return 367*bb4ee6a4SAndroid Build Coastguard Worker 368*bb4ee6a4SAndroid Build Coastguard Worker num_dry_runs = len(list_recent_dry_runs("1d")) 369*bb4ee6a4SAndroid Build Coastguard Worker if num_dry_runs >= MAX_DRY_RUNS_PER_DAY: 370*bb4ee6a4SAndroid Build Coastguard Worker print(f"Already created {num_dry_runs} in the past 24h. Not creating another one.") 371*bb4ee6a4SAndroid Build Coastguard Worker return 372*bb4ee6a4SAndroid Build Coastguard Worker 373*bb4ee6a4SAndroid Build Coastguard Worker print(f"Creating dry run merge of {parsed_revision} into cros/{target_branch}") 374*bb4ee6a4SAndroid Build Coastguard Worker with tracking_branch_context("merge-bot-branch", target_branch): 375*bb4ee6a4SAndroid Build Coastguard Worker count, has_conflicts = create_merge_commits( 376*bb4ee6a4SAndroid Build Coastguard Worker parsed_revision, max_size, create_dry_run=True, force_pgo=True 377*bb4ee6a4SAndroid Build Coastguard Worker ) 378*bb4ee6a4SAndroid Build Coastguard Worker if count > 0 and not has_conflicts: 379*bb4ee6a4SAndroid Build Coastguard Worker upload_to_gerrit( 380*bb4ee6a4SAndroid Build Coastguard Worker target_branch, 381*bb4ee6a4SAndroid Build Coastguard Worker f"hashtag={DRY_RUN_TAG}", 382*bb4ee6a4SAndroid Build Coastguard Worker *(["l=Commit-Queue+1"] if not TESTING else []), 383*bb4ee6a4SAndroid Build Coastguard Worker *(["l=Bot-Commit+1"] if is_bot else []), 384*bb4ee6a4SAndroid Build Coastguard Worker ) 385*bb4ee6a4SAndroid Build Coastguard Worker else: 386*bb4ee6a4SAndroid Build Coastguard Worker if has_conflicts: 387*bb4ee6a4SAndroid Build Coastguard Worker print("Not uploading dry-run with conflicts.") 388*bb4ee6a4SAndroid Build Coastguard Worker else: 389*bb4ee6a4SAndroid Build Coastguard Worker print("Nothing to upload.") 390*bb4ee6a4SAndroid Build Coastguard Worker 391*bb4ee6a4SAndroid Build Coastguard Worker 392*bb4ee6a4SAndroid Build Coastguard Workerrun_commands( 393*bb4ee6a4SAndroid Build Coastguard Worker create_merge_commits, 394*bb4ee6a4SAndroid Build Coastguard Worker status, 395*bb4ee6a4SAndroid Build Coastguard Worker update_merges, 396*bb4ee6a4SAndroid Build Coastguard Worker update_dry_runs, 397*bb4ee6a4SAndroid Build Coastguard Worker gerrit_prerequisites, 398*bb4ee6a4SAndroid Build Coastguard Worker) 399