11cb0ef41Sopenharmony_ci#!/usr/bin/env python3 21cb0ef41Sopenharmony_ci# Copyright 2013 the V8 project authors. All rights reserved. 31cb0ef41Sopenharmony_ci# Redistribution and use in source and binary forms, with or without 41cb0ef41Sopenharmony_ci# modification, are permitted provided that the following conditions are 51cb0ef41Sopenharmony_ci# met: 61cb0ef41Sopenharmony_ci# 71cb0ef41Sopenharmony_ci# * Redistributions of source code must retain the above copyright 81cb0ef41Sopenharmony_ci# notice, this list of conditions and the following disclaimer. 91cb0ef41Sopenharmony_ci# * Redistributions in binary form must reproduce the above 101cb0ef41Sopenharmony_ci# copyright notice, this list of conditions and the following 111cb0ef41Sopenharmony_ci# disclaimer in the documentation and/or other materials provided 121cb0ef41Sopenharmony_ci# with the distribution. 131cb0ef41Sopenharmony_ci# * Neither the name of Google Inc. nor the names of its 141cb0ef41Sopenharmony_ci# contributors may be used to endorse or promote products derived 151cb0ef41Sopenharmony_ci# from this software without specific prior written permission. 161cb0ef41Sopenharmony_ci# 171cb0ef41Sopenharmony_ci# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 181cb0ef41Sopenharmony_ci# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 191cb0ef41Sopenharmony_ci# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 201cb0ef41Sopenharmony_ci# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 211cb0ef41Sopenharmony_ci# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 221cb0ef41Sopenharmony_ci# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 231cb0ef41Sopenharmony_ci# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 241cb0ef41Sopenharmony_ci# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 251cb0ef41Sopenharmony_ci# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 261cb0ef41Sopenharmony_ci# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 271cb0ef41Sopenharmony_ci# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 281cb0ef41Sopenharmony_ci 291cb0ef41Sopenharmony_ciimport argparse 301cb0ef41Sopenharmony_ciimport datetime 311cb0ef41Sopenharmony_cifrom distutils.version import LooseVersion 321cb0ef41Sopenharmony_ciimport glob 331cb0ef41Sopenharmony_ciimport imp 341cb0ef41Sopenharmony_ciimport json 351cb0ef41Sopenharmony_ciimport os 361cb0ef41Sopenharmony_ciimport re 371cb0ef41Sopenharmony_ciimport shutil 381cb0ef41Sopenharmony_ciimport subprocess 391cb0ef41Sopenharmony_ciimport sys 401cb0ef41Sopenharmony_ciimport textwrap 411cb0ef41Sopenharmony_ciimport time 421cb0ef41Sopenharmony_ciimport urllib 431cb0ef41Sopenharmony_ci 441cb0ef41Sopenharmony_cifrom git_recipes import GitRecipesMixin 451cb0ef41Sopenharmony_cifrom git_recipes import GitFailedException 461cb0ef41Sopenharmony_ci 471cb0ef41Sopenharmony_ciimport http.client as httplib 481cb0ef41Sopenharmony_ciimport urllib.request as urllib2 491cb0ef41Sopenharmony_ci 501cb0ef41Sopenharmony_ci 511cb0ef41Sopenharmony_ciDAY_IN_SECONDS = 24 * 60 * 60 521cb0ef41Sopenharmony_ciPUSH_MSG_GIT_RE = re.compile(r".* \(based on (?P<git_rev>[a-fA-F0-9]+)\)$") 531cb0ef41Sopenharmony_ciPUSH_MSG_NEW_RE = re.compile(r"^Version \d+\.\d+\.\d+$") 541cb0ef41Sopenharmony_ciVERSION_FILE = os.path.join("include", "v8-version.h") 551cb0ef41Sopenharmony_ciWATCHLISTS_FILE = "WATCHLISTS" 561cb0ef41Sopenharmony_ciRELEASE_WORKDIR = "/tmp/v8-release-scripts-work-dir/" 571cb0ef41Sopenharmony_ci 581cb0ef41Sopenharmony_ci# V8 base directory. 591cb0ef41Sopenharmony_ciV8_BASE = os.path.dirname( 601cb0ef41Sopenharmony_ci os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 611cb0ef41Sopenharmony_ci 621cb0ef41Sopenharmony_ci# Add our copy of depot_tools to the PATH as many scripts use tools from there, 631cb0ef41Sopenharmony_ci# e.g. git-cl, fetch, git-new-branch etc, and we can not depend on depot_tools 641cb0ef41Sopenharmony_ci# being in the PATH on the LUCI bots. 651cb0ef41Sopenharmony_cipath_to_depot_tools = os.path.join(V8_BASE, 'third_party', 'depot_tools') 661cb0ef41Sopenharmony_cinew_path = path_to_depot_tools + os.pathsep + os.environ.get('PATH') 671cb0ef41Sopenharmony_cios.environ['PATH'] = new_path 681cb0ef41Sopenharmony_ci 691cb0ef41Sopenharmony_ci 701cb0ef41Sopenharmony_cidef TextToFile(text, file_name): 711cb0ef41Sopenharmony_ci with open(file_name, "w") as f: 721cb0ef41Sopenharmony_ci f.write(text) 731cb0ef41Sopenharmony_ci 741cb0ef41Sopenharmony_ci 751cb0ef41Sopenharmony_cidef AppendToFile(text, file_name): 761cb0ef41Sopenharmony_ci with open(file_name, "a") as f: 771cb0ef41Sopenharmony_ci f.write(text) 781cb0ef41Sopenharmony_ci 791cb0ef41Sopenharmony_ci 801cb0ef41Sopenharmony_cidef LinesInFile(file_name): 811cb0ef41Sopenharmony_ci with open(file_name) as f: 821cb0ef41Sopenharmony_ci for line in f: 831cb0ef41Sopenharmony_ci yield line 841cb0ef41Sopenharmony_ci 851cb0ef41Sopenharmony_ci 861cb0ef41Sopenharmony_cidef FileToText(file_name): 871cb0ef41Sopenharmony_ci with open(file_name) as f: 881cb0ef41Sopenharmony_ci return f.read() 891cb0ef41Sopenharmony_ci 901cb0ef41Sopenharmony_ci 911cb0ef41Sopenharmony_cidef MSub(rexp, replacement, text): 921cb0ef41Sopenharmony_ci return re.sub(rexp, replacement, text, flags=re.MULTILINE) 931cb0ef41Sopenharmony_ci 941cb0ef41Sopenharmony_ci 951cb0ef41Sopenharmony_ci# Some commands don't like the pipe, e.g. calling vi from within the script or 961cb0ef41Sopenharmony_ci# from subscripts like git cl upload. 971cb0ef41Sopenharmony_cidef Command(cmd, args="", prefix="", pipe=True, cwd=None): 981cb0ef41Sopenharmony_ci cwd = cwd or os.getcwd() 991cb0ef41Sopenharmony_ci # TODO(machenbach): Use timeout. 1001cb0ef41Sopenharmony_ci cmd_line = "%s %s %s" % (prefix, cmd, args) 1011cb0ef41Sopenharmony_ci print("Command: %s" % cmd_line) 1021cb0ef41Sopenharmony_ci print("in %s" % cwd) 1031cb0ef41Sopenharmony_ci sys.stdout.flush() 1041cb0ef41Sopenharmony_ci try: 1051cb0ef41Sopenharmony_ci if pipe: 1061cb0ef41Sopenharmony_ci return subprocess.check_output(cmd_line, shell=True, cwd=cwd).decode('utf-8') 1071cb0ef41Sopenharmony_ci else: 1081cb0ef41Sopenharmony_ci return subprocess.check_call(cmd_line, shell=True, cwd=cwd) 1091cb0ef41Sopenharmony_ci except subprocess.CalledProcessError: 1101cb0ef41Sopenharmony_ci return None 1111cb0ef41Sopenharmony_ci finally: 1121cb0ef41Sopenharmony_ci sys.stdout.flush() 1131cb0ef41Sopenharmony_ci sys.stderr.flush() 1141cb0ef41Sopenharmony_ci 1151cb0ef41Sopenharmony_ci 1161cb0ef41Sopenharmony_cidef SanitizeVersionTag(tag): 1171cb0ef41Sopenharmony_ci version_without_prefix = re.compile(r"^\d+\.\d+\.\d+(?:\.\d+)?$") 1181cb0ef41Sopenharmony_ci version_with_prefix = re.compile(r"^tags\/\d+\.\d+\.\d+(?:\.\d+)?$") 1191cb0ef41Sopenharmony_ci 1201cb0ef41Sopenharmony_ci if version_without_prefix.match(tag): 1211cb0ef41Sopenharmony_ci return tag 1221cb0ef41Sopenharmony_ci elif version_with_prefix.match(tag): 1231cb0ef41Sopenharmony_ci return tag[len("tags/"):] 1241cb0ef41Sopenharmony_ci else: 1251cb0ef41Sopenharmony_ci return None 1261cb0ef41Sopenharmony_ci 1271cb0ef41Sopenharmony_ci 1281cb0ef41Sopenharmony_cidef NormalizeVersionTags(version_tags): 1291cb0ef41Sopenharmony_ci normalized_version_tags = [] 1301cb0ef41Sopenharmony_ci 1311cb0ef41Sopenharmony_ci # Remove tags/ prefix because of packed refs. 1321cb0ef41Sopenharmony_ci for current_tag in version_tags: 1331cb0ef41Sopenharmony_ci version_tag = SanitizeVersionTag(current_tag) 1341cb0ef41Sopenharmony_ci if version_tag != None: 1351cb0ef41Sopenharmony_ci normalized_version_tags.append(version_tag) 1361cb0ef41Sopenharmony_ci 1371cb0ef41Sopenharmony_ci return normalized_version_tags 1381cb0ef41Sopenharmony_ci 1391cb0ef41Sopenharmony_ci 1401cb0ef41Sopenharmony_ci# Wrapper for side effects. 1411cb0ef41Sopenharmony_ciclass SideEffectHandler(object): # pragma: no cover 1421cb0ef41Sopenharmony_ci def Call(self, fun, *args, **kwargs): 1431cb0ef41Sopenharmony_ci return fun(*args, **kwargs) 1441cb0ef41Sopenharmony_ci 1451cb0ef41Sopenharmony_ci def Command(self, cmd, args="", prefix="", pipe=True, cwd=None): 1461cb0ef41Sopenharmony_ci return Command(cmd, args, prefix, pipe, cwd=cwd) 1471cb0ef41Sopenharmony_ci 1481cb0ef41Sopenharmony_ci def ReadLine(self): 1491cb0ef41Sopenharmony_ci return sys.stdin.readline().strip() 1501cb0ef41Sopenharmony_ci 1511cb0ef41Sopenharmony_ci def ReadURL(self, url, params=None): 1521cb0ef41Sopenharmony_ci # pylint: disable=E1121 1531cb0ef41Sopenharmony_ci url_fh = urllib2.urlopen(url, params, 60) 1541cb0ef41Sopenharmony_ci try: 1551cb0ef41Sopenharmony_ci return url_fh.read() 1561cb0ef41Sopenharmony_ci finally: 1571cb0ef41Sopenharmony_ci url_fh.close() 1581cb0ef41Sopenharmony_ci 1591cb0ef41Sopenharmony_ci def ReadClusterFuzzAPI(self, api_key, **params): 1601cb0ef41Sopenharmony_ci params["api_key"] = api_key.strip() 1611cb0ef41Sopenharmony_ci params = urllib.urlencode(params) 1621cb0ef41Sopenharmony_ci 1631cb0ef41Sopenharmony_ci headers = {"Content-type": "application/x-www-form-urlencoded"} 1641cb0ef41Sopenharmony_ci 1651cb0ef41Sopenharmony_ci conn = httplib.HTTPSConnection("backend-dot-cluster-fuzz.appspot.com") 1661cb0ef41Sopenharmony_ci conn.request("POST", "/_api/", params, headers) 1671cb0ef41Sopenharmony_ci 1681cb0ef41Sopenharmony_ci response = conn.getresponse() 1691cb0ef41Sopenharmony_ci data = response.read() 1701cb0ef41Sopenharmony_ci 1711cb0ef41Sopenharmony_ci try: 1721cb0ef41Sopenharmony_ci return json.loads(data) 1731cb0ef41Sopenharmony_ci except: 1741cb0ef41Sopenharmony_ci print(data) 1751cb0ef41Sopenharmony_ci print("ERROR: Could not read response. Is your key valid?") 1761cb0ef41Sopenharmony_ci raise 1771cb0ef41Sopenharmony_ci 1781cb0ef41Sopenharmony_ci def Sleep(self, seconds): 1791cb0ef41Sopenharmony_ci time.sleep(seconds) 1801cb0ef41Sopenharmony_ci 1811cb0ef41Sopenharmony_ci def GetUTCStamp(self): 1821cb0ef41Sopenharmony_ci return time.mktime(datetime.datetime.utcnow().timetuple()) 1831cb0ef41Sopenharmony_ci 1841cb0ef41Sopenharmony_ciDEFAULT_SIDE_EFFECT_HANDLER = SideEffectHandler() 1851cb0ef41Sopenharmony_ci 1861cb0ef41Sopenharmony_ci 1871cb0ef41Sopenharmony_ciclass NoRetryException(Exception): 1881cb0ef41Sopenharmony_ci pass 1891cb0ef41Sopenharmony_ci 1901cb0ef41Sopenharmony_ci 1911cb0ef41Sopenharmony_ciclass VCInterface(object): 1921cb0ef41Sopenharmony_ci def InjectStep(self, step): 1931cb0ef41Sopenharmony_ci self.step=step 1941cb0ef41Sopenharmony_ci 1951cb0ef41Sopenharmony_ci def Pull(self): 1961cb0ef41Sopenharmony_ci raise NotImplementedError() 1971cb0ef41Sopenharmony_ci 1981cb0ef41Sopenharmony_ci def Fetch(self): 1991cb0ef41Sopenharmony_ci raise NotImplementedError() 2001cb0ef41Sopenharmony_ci 2011cb0ef41Sopenharmony_ci def GetTags(self): 2021cb0ef41Sopenharmony_ci raise NotImplementedError() 2031cb0ef41Sopenharmony_ci 2041cb0ef41Sopenharmony_ci def GetBranches(self): 2051cb0ef41Sopenharmony_ci raise NotImplementedError() 2061cb0ef41Sopenharmony_ci 2071cb0ef41Sopenharmony_ci def MainBranch(self): 2081cb0ef41Sopenharmony_ci raise NotImplementedError() 2091cb0ef41Sopenharmony_ci 2101cb0ef41Sopenharmony_ci def CandidateBranch(self): 2111cb0ef41Sopenharmony_ci raise NotImplementedError() 2121cb0ef41Sopenharmony_ci 2131cb0ef41Sopenharmony_ci def RemoteMainBranch(self): 2141cb0ef41Sopenharmony_ci raise NotImplementedError() 2151cb0ef41Sopenharmony_ci 2161cb0ef41Sopenharmony_ci def RemoteCandidateBranch(self): 2171cb0ef41Sopenharmony_ci raise NotImplementedError() 2181cb0ef41Sopenharmony_ci 2191cb0ef41Sopenharmony_ci def RemoteBranch(self, name): 2201cb0ef41Sopenharmony_ci raise NotImplementedError() 2211cb0ef41Sopenharmony_ci 2221cb0ef41Sopenharmony_ci def CLLand(self): 2231cb0ef41Sopenharmony_ci raise NotImplementedError() 2241cb0ef41Sopenharmony_ci 2251cb0ef41Sopenharmony_ci def Tag(self, tag, remote, message): 2261cb0ef41Sopenharmony_ci """Sets a tag for the current commit. 2271cb0ef41Sopenharmony_ci 2281cb0ef41Sopenharmony_ci Assumptions: The commit already landed and the commit message is unique. 2291cb0ef41Sopenharmony_ci """ 2301cb0ef41Sopenharmony_ci raise NotImplementedError() 2311cb0ef41Sopenharmony_ci 2321cb0ef41Sopenharmony_ci 2331cb0ef41Sopenharmony_ciclass GitInterface(VCInterface): 2341cb0ef41Sopenharmony_ci def Pull(self): 2351cb0ef41Sopenharmony_ci self.step.GitPull() 2361cb0ef41Sopenharmony_ci 2371cb0ef41Sopenharmony_ci def Fetch(self): 2381cb0ef41Sopenharmony_ci self.step.Git("fetch") 2391cb0ef41Sopenharmony_ci 2401cb0ef41Sopenharmony_ci def GetTags(self): 2411cb0ef41Sopenharmony_ci return self.step.Git("tag").strip().splitlines() 2421cb0ef41Sopenharmony_ci 2431cb0ef41Sopenharmony_ci def GetBranches(self): 2441cb0ef41Sopenharmony_ci # Get relevant remote branches, e.g. "branch-heads/3.25". 2451cb0ef41Sopenharmony_ci branches = filter( 2461cb0ef41Sopenharmony_ci lambda s: re.match(r"^branch\-heads/\d+\.\d+$", s), 2471cb0ef41Sopenharmony_ci self.step.GitRemotes()) 2481cb0ef41Sopenharmony_ci # Remove 'branch-heads/' prefix. 2491cb0ef41Sopenharmony_ci return [b[13:] for b in branches] 2501cb0ef41Sopenharmony_ci 2511cb0ef41Sopenharmony_ci def MainBranch(self): 2521cb0ef41Sopenharmony_ci return "main" 2531cb0ef41Sopenharmony_ci 2541cb0ef41Sopenharmony_ci def CandidateBranch(self): 2551cb0ef41Sopenharmony_ci return "candidates" 2561cb0ef41Sopenharmony_ci 2571cb0ef41Sopenharmony_ci def RemoteMainBranch(self): 2581cb0ef41Sopenharmony_ci return "origin/main" 2591cb0ef41Sopenharmony_ci 2601cb0ef41Sopenharmony_ci def RemoteCandidateBranch(self): 2611cb0ef41Sopenharmony_ci return "origin/candidates" 2621cb0ef41Sopenharmony_ci 2631cb0ef41Sopenharmony_ci def RemoteBranch(self, name): 2641cb0ef41Sopenharmony_ci # Assume that if someone "fully qualified" the ref, they know what they 2651cb0ef41Sopenharmony_ci # want. 2661cb0ef41Sopenharmony_ci if name.startswith('refs/'): 2671cb0ef41Sopenharmony_ci return name 2681cb0ef41Sopenharmony_ci if name in ["candidates", "main"]: 2691cb0ef41Sopenharmony_ci return "refs/remotes/origin/%s" % name 2701cb0ef41Sopenharmony_ci try: 2711cb0ef41Sopenharmony_ci # Check if branch is in heads. 2721cb0ef41Sopenharmony_ci if self.step.Git("show-ref refs/remotes/origin/%s" % name).strip(): 2731cb0ef41Sopenharmony_ci return "refs/remotes/origin/%s" % name 2741cb0ef41Sopenharmony_ci except GitFailedException: 2751cb0ef41Sopenharmony_ci pass 2761cb0ef41Sopenharmony_ci try: 2771cb0ef41Sopenharmony_ci # Check if branch is in branch-heads. 2781cb0ef41Sopenharmony_ci if self.step.Git("show-ref refs/remotes/branch-heads/%s" % name).strip(): 2791cb0ef41Sopenharmony_ci return "refs/remotes/branch-heads/%s" % name 2801cb0ef41Sopenharmony_ci except GitFailedException: 2811cb0ef41Sopenharmony_ci pass 2821cb0ef41Sopenharmony_ci self.Die("Can't find remote of %s" % name) 2831cb0ef41Sopenharmony_ci 2841cb0ef41Sopenharmony_ci def Tag(self, tag, remote, message): 2851cb0ef41Sopenharmony_ci # Wait for the commit to appear. Assumes unique commit message titles (this 2861cb0ef41Sopenharmony_ci # is the case for all automated merge and push commits - also no title is 2871cb0ef41Sopenharmony_ci # the prefix of another title). 2881cb0ef41Sopenharmony_ci commit = None 2891cb0ef41Sopenharmony_ci for wait_interval in [10, 30, 60, 60, 60, 60, 60]: 2901cb0ef41Sopenharmony_ci self.step.Git("fetch") 2911cb0ef41Sopenharmony_ci commit = self.step.GitLog(n=1, format="%H", grep=message, branch=remote) 2921cb0ef41Sopenharmony_ci if commit: 2931cb0ef41Sopenharmony_ci break 2941cb0ef41Sopenharmony_ci print("The commit has not replicated to git. Waiting for %s seconds." % 2951cb0ef41Sopenharmony_ci wait_interval) 2961cb0ef41Sopenharmony_ci self.step._side_effect_handler.Sleep(wait_interval) 2971cb0ef41Sopenharmony_ci else: 2981cb0ef41Sopenharmony_ci self.step.Die("Couldn't determine commit for setting the tag. Maybe the " 2991cb0ef41Sopenharmony_ci "git updater is lagging behind?") 3001cb0ef41Sopenharmony_ci 3011cb0ef41Sopenharmony_ci self.step.Git("tag %s %s" % (tag, commit)) 3021cb0ef41Sopenharmony_ci self.step.Git("push origin refs/tags/%s:refs/tags/%s" % (tag, tag)) 3031cb0ef41Sopenharmony_ci 3041cb0ef41Sopenharmony_ci def CLLand(self): 3051cb0ef41Sopenharmony_ci self.step.GitCLLand() 3061cb0ef41Sopenharmony_ci 3071cb0ef41Sopenharmony_ci 3081cb0ef41Sopenharmony_ciclass Step(GitRecipesMixin): 3091cb0ef41Sopenharmony_ci def __init__(self, text, number, config, state, options, handler): 3101cb0ef41Sopenharmony_ci self._text = text 3111cb0ef41Sopenharmony_ci self._number = number 3121cb0ef41Sopenharmony_ci self._config = config 3131cb0ef41Sopenharmony_ci self._state = state 3141cb0ef41Sopenharmony_ci self._options = options 3151cb0ef41Sopenharmony_ci self._side_effect_handler = handler 3161cb0ef41Sopenharmony_ci self.vc = GitInterface() 3171cb0ef41Sopenharmony_ci self.vc.InjectStep(self) 3181cb0ef41Sopenharmony_ci 3191cb0ef41Sopenharmony_ci # The testing configuration might set a different default cwd. 3201cb0ef41Sopenharmony_ci self.default_cwd = (self._config.get("DEFAULT_CWD") or 3211cb0ef41Sopenharmony_ci os.path.join(self._options.work_dir, "v8")) 3221cb0ef41Sopenharmony_ci 3231cb0ef41Sopenharmony_ci assert self._number >= 0 3241cb0ef41Sopenharmony_ci assert self._config is not None 3251cb0ef41Sopenharmony_ci assert self._state is not None 3261cb0ef41Sopenharmony_ci assert self._side_effect_handler is not None 3271cb0ef41Sopenharmony_ci 3281cb0ef41Sopenharmony_ci def __getitem__(self, key): 3291cb0ef41Sopenharmony_ci # Convenience method to allow direct [] access on step classes for 3301cb0ef41Sopenharmony_ci # manipulating the backed state dict. 3311cb0ef41Sopenharmony_ci return self._state.get(key) 3321cb0ef41Sopenharmony_ci 3331cb0ef41Sopenharmony_ci def __setitem__(self, key, value): 3341cb0ef41Sopenharmony_ci # Convenience method to allow direct [] access on step classes for 3351cb0ef41Sopenharmony_ci # manipulating the backed state dict. 3361cb0ef41Sopenharmony_ci self._state[key] = value 3371cb0ef41Sopenharmony_ci 3381cb0ef41Sopenharmony_ci def Config(self, key): 3391cb0ef41Sopenharmony_ci return self._config[key] 3401cb0ef41Sopenharmony_ci 3411cb0ef41Sopenharmony_ci def Run(self): 3421cb0ef41Sopenharmony_ci # Restore state. 3431cb0ef41Sopenharmony_ci state_file = "%s-state.json" % self._config["PERSISTFILE_BASENAME"] 3441cb0ef41Sopenharmony_ci if not self._state and os.path.exists(state_file): 3451cb0ef41Sopenharmony_ci self._state.update(json.loads(FileToText(state_file))) 3461cb0ef41Sopenharmony_ci 3471cb0ef41Sopenharmony_ci print(">>> Step %d: %s" % (self._number, self._text)) 3481cb0ef41Sopenharmony_ci try: 3491cb0ef41Sopenharmony_ci return self.RunStep() 3501cb0ef41Sopenharmony_ci finally: 3511cb0ef41Sopenharmony_ci # Persist state. 3521cb0ef41Sopenharmony_ci TextToFile(json.dumps(self._state), state_file) 3531cb0ef41Sopenharmony_ci 3541cb0ef41Sopenharmony_ci def RunStep(self): # pragma: no cover 3551cb0ef41Sopenharmony_ci raise NotImplementedError 3561cb0ef41Sopenharmony_ci 3571cb0ef41Sopenharmony_ci def Retry(self, cb, retry_on=None, wait_plan=None): 3581cb0ef41Sopenharmony_ci """ Retry a function. 3591cb0ef41Sopenharmony_ci Params: 3601cb0ef41Sopenharmony_ci cb: The function to retry. 3611cb0ef41Sopenharmony_ci retry_on: A callback that takes the result of the function and returns 3621cb0ef41Sopenharmony_ci True if the function should be retried. A function throwing an 3631cb0ef41Sopenharmony_ci exception is always retried. 3641cb0ef41Sopenharmony_ci wait_plan: A list of waiting delays between retries in seconds. The 3651cb0ef41Sopenharmony_ci maximum number of retries is len(wait_plan). 3661cb0ef41Sopenharmony_ci """ 3671cb0ef41Sopenharmony_ci retry_on = retry_on or (lambda x: False) 3681cb0ef41Sopenharmony_ci wait_plan = list(wait_plan or []) 3691cb0ef41Sopenharmony_ci wait_plan.reverse() 3701cb0ef41Sopenharmony_ci while True: 3711cb0ef41Sopenharmony_ci got_exception = False 3721cb0ef41Sopenharmony_ci try: 3731cb0ef41Sopenharmony_ci result = cb() 3741cb0ef41Sopenharmony_ci except NoRetryException as e: 3751cb0ef41Sopenharmony_ci raise e 3761cb0ef41Sopenharmony_ci except Exception as e: 3771cb0ef41Sopenharmony_ci got_exception = e 3781cb0ef41Sopenharmony_ci if got_exception or retry_on(result): 3791cb0ef41Sopenharmony_ci if not wait_plan: # pragma: no cover 3801cb0ef41Sopenharmony_ci raise Exception("Retried too often. Giving up. Reason: %s" % 3811cb0ef41Sopenharmony_ci str(got_exception)) 3821cb0ef41Sopenharmony_ci wait_time = wait_plan.pop() 3831cb0ef41Sopenharmony_ci print("Waiting for %f seconds." % wait_time) 3841cb0ef41Sopenharmony_ci self._side_effect_handler.Sleep(wait_time) 3851cb0ef41Sopenharmony_ci print("Retrying...") 3861cb0ef41Sopenharmony_ci else: 3871cb0ef41Sopenharmony_ci return result 3881cb0ef41Sopenharmony_ci 3891cb0ef41Sopenharmony_ci def ReadLine(self, default=None): 3901cb0ef41Sopenharmony_ci # Don't prompt in forced mode. 3911cb0ef41Sopenharmony_ci if self._options.force_readline_defaults and default is not None: 3921cb0ef41Sopenharmony_ci print("%s (forced)" % default) 3931cb0ef41Sopenharmony_ci return default 3941cb0ef41Sopenharmony_ci else: 3951cb0ef41Sopenharmony_ci return self._side_effect_handler.ReadLine() 3961cb0ef41Sopenharmony_ci 3971cb0ef41Sopenharmony_ci def Command(self, name, args, cwd=None): 3981cb0ef41Sopenharmony_ci cmd = lambda: self._side_effect_handler.Command( 3991cb0ef41Sopenharmony_ci name, args, "", True, cwd=cwd or self.default_cwd) 4001cb0ef41Sopenharmony_ci return self.Retry(cmd, None, [5]) 4011cb0ef41Sopenharmony_ci 4021cb0ef41Sopenharmony_ci def Git(self, args="", prefix="", pipe=True, retry_on=None, cwd=None): 4031cb0ef41Sopenharmony_ci cmd = lambda: self._side_effect_handler.Command( 4041cb0ef41Sopenharmony_ci "git", args, prefix, pipe, cwd=cwd or self.default_cwd) 4051cb0ef41Sopenharmony_ci result = self.Retry(cmd, retry_on, [5, 30]) 4061cb0ef41Sopenharmony_ci if result is None: 4071cb0ef41Sopenharmony_ci raise GitFailedException("'git %s' failed." % args) 4081cb0ef41Sopenharmony_ci return result 4091cb0ef41Sopenharmony_ci 4101cb0ef41Sopenharmony_ci def Editor(self, args): 4111cb0ef41Sopenharmony_ci if self._options.requires_editor: 4121cb0ef41Sopenharmony_ci return self._side_effect_handler.Command( 4131cb0ef41Sopenharmony_ci os.environ["EDITOR"], 4141cb0ef41Sopenharmony_ci args, 4151cb0ef41Sopenharmony_ci pipe=False, 4161cb0ef41Sopenharmony_ci cwd=self.default_cwd) 4171cb0ef41Sopenharmony_ci 4181cb0ef41Sopenharmony_ci def ReadURL(self, url, params=None, retry_on=None, wait_plan=None): 4191cb0ef41Sopenharmony_ci wait_plan = wait_plan or [3, 60, 600] 4201cb0ef41Sopenharmony_ci cmd = lambda: self._side_effect_handler.ReadURL(url, params) 4211cb0ef41Sopenharmony_ci return self.Retry(cmd, retry_on, wait_plan) 4221cb0ef41Sopenharmony_ci 4231cb0ef41Sopenharmony_ci def Die(self, msg=""): 4241cb0ef41Sopenharmony_ci if msg != "": 4251cb0ef41Sopenharmony_ci print("Error: %s" % msg) 4261cb0ef41Sopenharmony_ci print("Exiting") 4271cb0ef41Sopenharmony_ci raise Exception(msg) 4281cb0ef41Sopenharmony_ci 4291cb0ef41Sopenharmony_ci def DieNoManualMode(self, msg=""): 4301cb0ef41Sopenharmony_ci if not self._options.manual: # pragma: no cover 4311cb0ef41Sopenharmony_ci msg = msg or "Only available in manual mode." 4321cb0ef41Sopenharmony_ci self.Die(msg) 4331cb0ef41Sopenharmony_ci 4341cb0ef41Sopenharmony_ci def Confirm(self, msg): 4351cb0ef41Sopenharmony_ci print("%s [Y/n] " % msg, end=' ') 4361cb0ef41Sopenharmony_ci answer = self.ReadLine(default="Y") 4371cb0ef41Sopenharmony_ci return answer == "" or answer == "Y" or answer == "y" 4381cb0ef41Sopenharmony_ci 4391cb0ef41Sopenharmony_ci def DeleteBranch(self, name, cwd=None): 4401cb0ef41Sopenharmony_ci for line in self.GitBranch(cwd=cwd).splitlines(): 4411cb0ef41Sopenharmony_ci if re.match(r"\*?\s*%s$" % re.escape(name), line): 4421cb0ef41Sopenharmony_ci msg = "Branch %s exists, do you want to delete it?" % name 4431cb0ef41Sopenharmony_ci if self.Confirm(msg): 4441cb0ef41Sopenharmony_ci self.GitDeleteBranch(name, cwd=cwd) 4451cb0ef41Sopenharmony_ci print("Branch %s deleted." % name) 4461cb0ef41Sopenharmony_ci else: 4471cb0ef41Sopenharmony_ci msg = "Can't continue. Please delete branch %s and try again." % name 4481cb0ef41Sopenharmony_ci self.Die(msg) 4491cb0ef41Sopenharmony_ci 4501cb0ef41Sopenharmony_ci def InitialEnvironmentChecks(self, cwd): 4511cb0ef41Sopenharmony_ci # Cancel if this is not a git checkout. 4521cb0ef41Sopenharmony_ci if not os.path.exists(os.path.join(cwd, ".git")): # pragma: no cover 4531cb0ef41Sopenharmony_ci self.Die("%s is not a git checkout. If you know what you're doing, try " 4541cb0ef41Sopenharmony_ci "deleting it and rerunning this script." % cwd) 4551cb0ef41Sopenharmony_ci 4561cb0ef41Sopenharmony_ci # Cancel if EDITOR is unset or not executable. 4571cb0ef41Sopenharmony_ci if (self._options.requires_editor and (not os.environ.get("EDITOR") or 4581cb0ef41Sopenharmony_ci self.Command( 4591cb0ef41Sopenharmony_ci "which", os.environ["EDITOR"]) is None)): # pragma: no cover 4601cb0ef41Sopenharmony_ci self.Die("Please set your EDITOR environment variable, you'll need it.") 4611cb0ef41Sopenharmony_ci 4621cb0ef41Sopenharmony_ci def CommonPrepare(self): 4631cb0ef41Sopenharmony_ci # Check for a clean workdir. 4641cb0ef41Sopenharmony_ci if not self.GitIsWorkdirClean(): # pragma: no cover 4651cb0ef41Sopenharmony_ci self.Die("Workspace is not clean. Please commit or undo your changes.") 4661cb0ef41Sopenharmony_ci 4671cb0ef41Sopenharmony_ci # Checkout main in case the script was left on a work branch. 4681cb0ef41Sopenharmony_ci self.GitCheckout('origin/main') 4691cb0ef41Sopenharmony_ci 4701cb0ef41Sopenharmony_ci # Fetch unfetched revisions. 4711cb0ef41Sopenharmony_ci self.vc.Fetch() 4721cb0ef41Sopenharmony_ci 4731cb0ef41Sopenharmony_ci def PrepareBranch(self): 4741cb0ef41Sopenharmony_ci # Delete the branch that will be created later if it exists already. 4751cb0ef41Sopenharmony_ci self.DeleteBranch(self._config["BRANCHNAME"]) 4761cb0ef41Sopenharmony_ci 4771cb0ef41Sopenharmony_ci def CommonCleanup(self): 4781cb0ef41Sopenharmony_ci self.GitCheckout('origin/main') 4791cb0ef41Sopenharmony_ci self.GitDeleteBranch(self._config["BRANCHNAME"]) 4801cb0ef41Sopenharmony_ci 4811cb0ef41Sopenharmony_ci # Clean up all temporary files. 4821cb0ef41Sopenharmony_ci for f in glob.iglob("%s*" % self._config["PERSISTFILE_BASENAME"]): 4831cb0ef41Sopenharmony_ci if os.path.isfile(f): 4841cb0ef41Sopenharmony_ci os.remove(f) 4851cb0ef41Sopenharmony_ci if os.path.isdir(f): 4861cb0ef41Sopenharmony_ci shutil.rmtree(f) 4871cb0ef41Sopenharmony_ci 4881cb0ef41Sopenharmony_ci def ReadAndPersistVersion(self, prefix=""): 4891cb0ef41Sopenharmony_ci def ReadAndPersist(var_name, def_name): 4901cb0ef41Sopenharmony_ci match = re.match(r"^#define %s\s+(\d*)" % def_name, line) 4911cb0ef41Sopenharmony_ci if match: 4921cb0ef41Sopenharmony_ci value = match.group(1) 4931cb0ef41Sopenharmony_ci self["%s%s" % (prefix, var_name)] = value 4941cb0ef41Sopenharmony_ci for line in LinesInFile(os.path.join(self.default_cwd, VERSION_FILE)): 4951cb0ef41Sopenharmony_ci for (var_name, def_name) in [("major", "V8_MAJOR_VERSION"), 4961cb0ef41Sopenharmony_ci ("minor", "V8_MINOR_VERSION"), 4971cb0ef41Sopenharmony_ci ("build", "V8_BUILD_NUMBER"), 4981cb0ef41Sopenharmony_ci ("patch", "V8_PATCH_LEVEL")]: 4991cb0ef41Sopenharmony_ci ReadAndPersist(var_name, def_name) 5001cb0ef41Sopenharmony_ci 5011cb0ef41Sopenharmony_ci def WaitForLGTM(self): 5021cb0ef41Sopenharmony_ci print ("Please wait for an LGTM, then type \"LGTM<Return>\" to commit " 5031cb0ef41Sopenharmony_ci "your change. (If you need to iterate on the patch or double check " 5041cb0ef41Sopenharmony_ci "that it's sensible, do so in another shell, but remember to not " 5051cb0ef41Sopenharmony_ci "change the headline of the uploaded CL.") 5061cb0ef41Sopenharmony_ci answer = "" 5071cb0ef41Sopenharmony_ci while answer != "LGTM": 5081cb0ef41Sopenharmony_ci print("> ", end=' ') 5091cb0ef41Sopenharmony_ci answer = self.ReadLine(None if self._options.wait_for_lgtm else "LGTM") 5101cb0ef41Sopenharmony_ci if answer != "LGTM": 5111cb0ef41Sopenharmony_ci print("That was not 'LGTM'.") 5121cb0ef41Sopenharmony_ci 5131cb0ef41Sopenharmony_ci def WaitForResolvingConflicts(self, patch_file): 5141cb0ef41Sopenharmony_ci print("Applying the patch \"%s\" failed. Either type \"ABORT<Return>\", " 5151cb0ef41Sopenharmony_ci "or resolve the conflicts, stage *all* touched files with " 5161cb0ef41Sopenharmony_ci "'git add', and type \"RESOLVED<Return>\"" % (patch_file)) 5171cb0ef41Sopenharmony_ci self.DieNoManualMode() 5181cb0ef41Sopenharmony_ci answer = "" 5191cb0ef41Sopenharmony_ci while answer != "RESOLVED": 5201cb0ef41Sopenharmony_ci if answer == "ABORT": 5211cb0ef41Sopenharmony_ci self.Die("Applying the patch failed.") 5221cb0ef41Sopenharmony_ci if answer != "": 5231cb0ef41Sopenharmony_ci print("That was not 'RESOLVED' or 'ABORT'.") 5241cb0ef41Sopenharmony_ci print("> ", end=' ') 5251cb0ef41Sopenharmony_ci answer = self.ReadLine() 5261cb0ef41Sopenharmony_ci 5271cb0ef41Sopenharmony_ci # Takes a file containing the patch to apply as first argument. 5281cb0ef41Sopenharmony_ci def ApplyPatch(self, patch_file, revert=False): 5291cb0ef41Sopenharmony_ci try: 5301cb0ef41Sopenharmony_ci self.GitApplyPatch(patch_file, revert) 5311cb0ef41Sopenharmony_ci except GitFailedException: 5321cb0ef41Sopenharmony_ci self.WaitForResolvingConflicts(patch_file) 5331cb0ef41Sopenharmony_ci 5341cb0ef41Sopenharmony_ci def GetVersionTag(self, revision): 5351cb0ef41Sopenharmony_ci tag = self.Git("describe --tags %s" % revision).strip() 5361cb0ef41Sopenharmony_ci return SanitizeVersionTag(tag) 5371cb0ef41Sopenharmony_ci 5381cb0ef41Sopenharmony_ci def GetRecentReleases(self, max_age): 5391cb0ef41Sopenharmony_ci # Make sure tags are fetched. 5401cb0ef41Sopenharmony_ci self.Git("fetch origin +refs/tags/*:refs/tags/*") 5411cb0ef41Sopenharmony_ci 5421cb0ef41Sopenharmony_ci # Current timestamp. 5431cb0ef41Sopenharmony_ci time_now = int(self._side_effect_handler.GetUTCStamp()) 5441cb0ef41Sopenharmony_ci 5451cb0ef41Sopenharmony_ci # List every tag from a given period. 5461cb0ef41Sopenharmony_ci revisions = self.Git("rev-list --max-age=%d --tags" % 5471cb0ef41Sopenharmony_ci int(time_now - max_age)).strip() 5481cb0ef41Sopenharmony_ci 5491cb0ef41Sopenharmony_ci # Filter out revisions who's tag is off by one or more commits. 5501cb0ef41Sopenharmony_ci return list(filter(self.GetVersionTag, revisions.splitlines())) 5511cb0ef41Sopenharmony_ci 5521cb0ef41Sopenharmony_ci def GetLatestVersion(self): 5531cb0ef41Sopenharmony_ci # Use cached version if available. 5541cb0ef41Sopenharmony_ci if self["latest_version"]: 5551cb0ef41Sopenharmony_ci return self["latest_version"] 5561cb0ef41Sopenharmony_ci 5571cb0ef41Sopenharmony_ci # Make sure tags are fetched. 5581cb0ef41Sopenharmony_ci self.Git("fetch origin +refs/tags/*:refs/tags/*") 5591cb0ef41Sopenharmony_ci 5601cb0ef41Sopenharmony_ci all_tags = self.vc.GetTags() 5611cb0ef41Sopenharmony_ci only_version_tags = NormalizeVersionTags(all_tags) 5621cb0ef41Sopenharmony_ci 5631cb0ef41Sopenharmony_ci version = sorted(only_version_tags, 5641cb0ef41Sopenharmony_ci key=LooseVersion, reverse=True)[0] 5651cb0ef41Sopenharmony_ci self["latest_version"] = version 5661cb0ef41Sopenharmony_ci return version 5671cb0ef41Sopenharmony_ci 5681cb0ef41Sopenharmony_ci def GetLatestRelease(self): 5691cb0ef41Sopenharmony_ci """The latest release is the git hash of the latest tagged version. 5701cb0ef41Sopenharmony_ci 5711cb0ef41Sopenharmony_ci This revision should be rolled into chromium. 5721cb0ef41Sopenharmony_ci """ 5731cb0ef41Sopenharmony_ci latest_version = self.GetLatestVersion() 5741cb0ef41Sopenharmony_ci 5751cb0ef41Sopenharmony_ci # The latest release. 5761cb0ef41Sopenharmony_ci latest_hash = self.GitLog(n=1, format="%H", branch=latest_version) 5771cb0ef41Sopenharmony_ci assert latest_hash 5781cb0ef41Sopenharmony_ci return latest_hash 5791cb0ef41Sopenharmony_ci 5801cb0ef41Sopenharmony_ci def GetLatestReleaseBase(self, version=None): 5811cb0ef41Sopenharmony_ci """The latest release base is the latest revision that is covered in the 5821cb0ef41Sopenharmony_ci last change log file. It doesn't include cherry-picked patches. 5831cb0ef41Sopenharmony_ci """ 5841cb0ef41Sopenharmony_ci latest_version = version or self.GetLatestVersion() 5851cb0ef41Sopenharmony_ci 5861cb0ef41Sopenharmony_ci # Strip patch level if it exists. 5871cb0ef41Sopenharmony_ci latest_version = ".".join(latest_version.split(".")[:3]) 5881cb0ef41Sopenharmony_ci 5891cb0ef41Sopenharmony_ci # The latest release base. 5901cb0ef41Sopenharmony_ci latest_hash = self.GitLog(n=1, format="%H", branch=latest_version) 5911cb0ef41Sopenharmony_ci assert latest_hash 5921cb0ef41Sopenharmony_ci 5931cb0ef41Sopenharmony_ci title = self.GitLog(n=1, format="%s", git_hash=latest_hash) 5941cb0ef41Sopenharmony_ci match = PUSH_MSG_GIT_RE.match(title) 5951cb0ef41Sopenharmony_ci if match: 5961cb0ef41Sopenharmony_ci # Legacy: In the old process there's one level of indirection. The 5971cb0ef41Sopenharmony_ci # version is on the candidates branch and points to the real release 5981cb0ef41Sopenharmony_ci # base on main through the commit message. 5991cb0ef41Sopenharmony_ci return match.group("git_rev") 6001cb0ef41Sopenharmony_ci match = PUSH_MSG_NEW_RE.match(title) 6011cb0ef41Sopenharmony_ci if match: 6021cb0ef41Sopenharmony_ci # This is a new-style v8 version branched from main. The commit 6031cb0ef41Sopenharmony_ci # "latest_hash" is the version-file change. Its parent is the release 6041cb0ef41Sopenharmony_ci # base on main. 6051cb0ef41Sopenharmony_ci return self.GitLog(n=1, format="%H", git_hash="%s^" % latest_hash) 6061cb0ef41Sopenharmony_ci 6071cb0ef41Sopenharmony_ci self.Die("Unknown latest release: %s" % latest_hash) 6081cb0ef41Sopenharmony_ci 6091cb0ef41Sopenharmony_ci def ArrayToVersion(self, prefix): 6101cb0ef41Sopenharmony_ci return ".".join([self[prefix + "major"], 6111cb0ef41Sopenharmony_ci self[prefix + "minor"], 6121cb0ef41Sopenharmony_ci self[prefix + "build"], 6131cb0ef41Sopenharmony_ci self[prefix + "patch"]]) 6141cb0ef41Sopenharmony_ci 6151cb0ef41Sopenharmony_ci def StoreVersion(self, version, prefix): 6161cb0ef41Sopenharmony_ci version_parts = version.split(".") 6171cb0ef41Sopenharmony_ci if len(version_parts) == 3: 6181cb0ef41Sopenharmony_ci version_parts.append("0") 6191cb0ef41Sopenharmony_ci major, minor, build, patch = version_parts 6201cb0ef41Sopenharmony_ci self[prefix + "major"] = major 6211cb0ef41Sopenharmony_ci self[prefix + "minor"] = minor 6221cb0ef41Sopenharmony_ci self[prefix + "build"] = build 6231cb0ef41Sopenharmony_ci self[prefix + "patch"] = patch 6241cb0ef41Sopenharmony_ci 6251cb0ef41Sopenharmony_ci def SetVersion(self, version_file, prefix): 6261cb0ef41Sopenharmony_ci output = "" 6271cb0ef41Sopenharmony_ci for line in FileToText(version_file).splitlines(): 6281cb0ef41Sopenharmony_ci if line.startswith("#define V8_MAJOR_VERSION"): 6291cb0ef41Sopenharmony_ci line = re.sub("\d+$", self[prefix + "major"], line) 6301cb0ef41Sopenharmony_ci elif line.startswith("#define V8_MINOR_VERSION"): 6311cb0ef41Sopenharmony_ci line = re.sub("\d+$", self[prefix + "minor"], line) 6321cb0ef41Sopenharmony_ci elif line.startswith("#define V8_BUILD_NUMBER"): 6331cb0ef41Sopenharmony_ci line = re.sub("\d+$", self[prefix + "build"], line) 6341cb0ef41Sopenharmony_ci elif line.startswith("#define V8_PATCH_LEVEL"): 6351cb0ef41Sopenharmony_ci line = re.sub("\d+$", self[prefix + "patch"], line) 6361cb0ef41Sopenharmony_ci elif (self[prefix + "candidate"] and 6371cb0ef41Sopenharmony_ci line.startswith("#define V8_IS_CANDIDATE_VERSION")): 6381cb0ef41Sopenharmony_ci line = re.sub("\d+$", self[prefix + "candidate"], line) 6391cb0ef41Sopenharmony_ci output += "%s\n" % line 6401cb0ef41Sopenharmony_ci TextToFile(output, version_file) 6411cb0ef41Sopenharmony_ci 6421cb0ef41Sopenharmony_ci 6431cb0ef41Sopenharmony_ciclass BootstrapStep(Step): 6441cb0ef41Sopenharmony_ci MESSAGE = "Bootstrapping checkout and state." 6451cb0ef41Sopenharmony_ci 6461cb0ef41Sopenharmony_ci def RunStep(self): 6471cb0ef41Sopenharmony_ci # Reserve state entry for json output. 6481cb0ef41Sopenharmony_ci self['json_output'] = {} 6491cb0ef41Sopenharmony_ci 6501cb0ef41Sopenharmony_ci if os.path.realpath(self.default_cwd) == os.path.realpath(V8_BASE): 6511cb0ef41Sopenharmony_ci self.Die("Can't use v8 checkout with calling script as work checkout.") 6521cb0ef41Sopenharmony_ci # Directory containing the working v8 checkout. 6531cb0ef41Sopenharmony_ci if not os.path.exists(self._options.work_dir): 6541cb0ef41Sopenharmony_ci os.makedirs(self._options.work_dir) 6551cb0ef41Sopenharmony_ci if not os.path.exists(self.default_cwd): 6561cb0ef41Sopenharmony_ci self.Command("fetch", "v8", cwd=self._options.work_dir) 6571cb0ef41Sopenharmony_ci 6581cb0ef41Sopenharmony_ci 6591cb0ef41Sopenharmony_ciclass UploadStep(Step): 6601cb0ef41Sopenharmony_ci MESSAGE = "Upload for code review." 6611cb0ef41Sopenharmony_ci 6621cb0ef41Sopenharmony_ci def RunStep(self): 6631cb0ef41Sopenharmony_ci reviewer = None 6641cb0ef41Sopenharmony_ci if self._options.reviewer: 6651cb0ef41Sopenharmony_ci print("Using account %s for review." % self._options.reviewer) 6661cb0ef41Sopenharmony_ci reviewer = self._options.reviewer 6671cb0ef41Sopenharmony_ci 6681cb0ef41Sopenharmony_ci tbr_reviewer = None 6691cb0ef41Sopenharmony_ci if self._options.tbr_reviewer: 6701cb0ef41Sopenharmony_ci print("Using account %s for TBR review." % self._options.tbr_reviewer) 6711cb0ef41Sopenharmony_ci tbr_reviewer = self._options.tbr_reviewer 6721cb0ef41Sopenharmony_ci 6731cb0ef41Sopenharmony_ci if not reviewer and not tbr_reviewer: 6741cb0ef41Sopenharmony_ci print( 6751cb0ef41Sopenharmony_ci "Please enter the email address of a V8 reviewer for your patch: ", 6761cb0ef41Sopenharmony_ci end=' ') 6771cb0ef41Sopenharmony_ci self.DieNoManualMode("A reviewer must be specified in forced mode.") 6781cb0ef41Sopenharmony_ci reviewer = self.ReadLine() 6791cb0ef41Sopenharmony_ci 6801cb0ef41Sopenharmony_ci self.GitUpload(reviewer, self._options.force_upload, 6811cb0ef41Sopenharmony_ci bypass_hooks=self._options.bypass_upload_hooks, 6821cb0ef41Sopenharmony_ci tbr_reviewer=tbr_reviewer) 6831cb0ef41Sopenharmony_ci 6841cb0ef41Sopenharmony_ci 6851cb0ef41Sopenharmony_cidef MakeStep(step_class=Step, number=0, state=None, config=None, 6861cb0ef41Sopenharmony_ci options=None, side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER): 6871cb0ef41Sopenharmony_ci # Allow to pass in empty dictionaries. 6881cb0ef41Sopenharmony_ci state = state if state is not None else {} 6891cb0ef41Sopenharmony_ci config = config if config is not None else {} 6901cb0ef41Sopenharmony_ci 6911cb0ef41Sopenharmony_ci try: 6921cb0ef41Sopenharmony_ci message = step_class.MESSAGE 6931cb0ef41Sopenharmony_ci except AttributeError: 6941cb0ef41Sopenharmony_ci message = step_class.__name__ 6951cb0ef41Sopenharmony_ci 6961cb0ef41Sopenharmony_ci return step_class(message, number=number, config=config, 6971cb0ef41Sopenharmony_ci state=state, options=options, 6981cb0ef41Sopenharmony_ci handler=side_effect_handler) 6991cb0ef41Sopenharmony_ci 7001cb0ef41Sopenharmony_ci 7011cb0ef41Sopenharmony_ciclass ScriptsBase(object): 7021cb0ef41Sopenharmony_ci def __init__(self, 7031cb0ef41Sopenharmony_ci config=None, 7041cb0ef41Sopenharmony_ci side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER, 7051cb0ef41Sopenharmony_ci state=None): 7061cb0ef41Sopenharmony_ci self._config = config or self._Config() 7071cb0ef41Sopenharmony_ci self._side_effect_handler = side_effect_handler 7081cb0ef41Sopenharmony_ci self._state = state if state is not None else {} 7091cb0ef41Sopenharmony_ci 7101cb0ef41Sopenharmony_ci def _Description(self): 7111cb0ef41Sopenharmony_ci return None 7121cb0ef41Sopenharmony_ci 7131cb0ef41Sopenharmony_ci def _PrepareOptions(self, parser): 7141cb0ef41Sopenharmony_ci pass 7151cb0ef41Sopenharmony_ci 7161cb0ef41Sopenharmony_ci def _ProcessOptions(self, options): 7171cb0ef41Sopenharmony_ci return True 7181cb0ef41Sopenharmony_ci 7191cb0ef41Sopenharmony_ci def _Steps(self): # pragma: no cover 7201cb0ef41Sopenharmony_ci raise Exception("Not implemented.") 7211cb0ef41Sopenharmony_ci 7221cb0ef41Sopenharmony_ci def _Config(self): 7231cb0ef41Sopenharmony_ci return {} 7241cb0ef41Sopenharmony_ci 7251cb0ef41Sopenharmony_ci def MakeOptions(self, args=None): 7261cb0ef41Sopenharmony_ci parser = argparse.ArgumentParser(description=self._Description()) 7271cb0ef41Sopenharmony_ci parser.add_argument("-a", "--author", default="", 7281cb0ef41Sopenharmony_ci help="The author email used for code review.") 7291cb0ef41Sopenharmony_ci parser.add_argument("--dry-run", default=False, action="store_true", 7301cb0ef41Sopenharmony_ci help="Perform only read-only actions.") 7311cb0ef41Sopenharmony_ci parser.add_argument("--json-output", 7321cb0ef41Sopenharmony_ci help="File to write results summary to.") 7331cb0ef41Sopenharmony_ci parser.add_argument("-r", "--reviewer", default="", 7341cb0ef41Sopenharmony_ci help="The account name to be used for reviews.") 7351cb0ef41Sopenharmony_ci parser.add_argument("--tbr-reviewer", "--tbr", default="", 7361cb0ef41Sopenharmony_ci help="The account name to be used for TBR reviews.") 7371cb0ef41Sopenharmony_ci parser.add_argument("-s", "--step", 7381cb0ef41Sopenharmony_ci help="Specify the step where to start work. Default: 0.", 7391cb0ef41Sopenharmony_ci default=0, type=int) 7401cb0ef41Sopenharmony_ci parser.add_argument("--work-dir", 7411cb0ef41Sopenharmony_ci help=("Location where to bootstrap a working v8 " 7421cb0ef41Sopenharmony_ci "checkout.")) 7431cb0ef41Sopenharmony_ci self._PrepareOptions(parser) 7441cb0ef41Sopenharmony_ci 7451cb0ef41Sopenharmony_ci if args is None: # pragma: no cover 7461cb0ef41Sopenharmony_ci options = parser.parse_args() 7471cb0ef41Sopenharmony_ci else: 7481cb0ef41Sopenharmony_ci options = parser.parse_args(args) 7491cb0ef41Sopenharmony_ci 7501cb0ef41Sopenharmony_ci # Process common options. 7511cb0ef41Sopenharmony_ci if options.step < 0: # pragma: no cover 7521cb0ef41Sopenharmony_ci print("Bad step number %d" % options.step) 7531cb0ef41Sopenharmony_ci parser.print_help() 7541cb0ef41Sopenharmony_ci return None 7551cb0ef41Sopenharmony_ci 7561cb0ef41Sopenharmony_ci # Defaults for options, common to all scripts. 7571cb0ef41Sopenharmony_ci options.manual = getattr(options, "manual", True) 7581cb0ef41Sopenharmony_ci options.force = getattr(options, "force", False) 7591cb0ef41Sopenharmony_ci options.bypass_upload_hooks = False 7601cb0ef41Sopenharmony_ci 7611cb0ef41Sopenharmony_ci # Derived options. 7621cb0ef41Sopenharmony_ci options.requires_editor = not options.force 7631cb0ef41Sopenharmony_ci options.wait_for_lgtm = not options.force 7641cb0ef41Sopenharmony_ci options.force_readline_defaults = not options.manual 7651cb0ef41Sopenharmony_ci options.force_upload = not options.manual 7661cb0ef41Sopenharmony_ci 7671cb0ef41Sopenharmony_ci # Process script specific options. 7681cb0ef41Sopenharmony_ci if not self._ProcessOptions(options): 7691cb0ef41Sopenharmony_ci parser.print_help() 7701cb0ef41Sopenharmony_ci return None 7711cb0ef41Sopenharmony_ci 7721cb0ef41Sopenharmony_ci if not options.work_dir: 7731cb0ef41Sopenharmony_ci options.work_dir = "/tmp/v8-release-scripts-work-dir" 7741cb0ef41Sopenharmony_ci return options 7751cb0ef41Sopenharmony_ci 7761cb0ef41Sopenharmony_ci def RunSteps(self, step_classes, args=None): 7771cb0ef41Sopenharmony_ci options = self.MakeOptions(args) 7781cb0ef41Sopenharmony_ci if not options: 7791cb0ef41Sopenharmony_ci return 1 7801cb0ef41Sopenharmony_ci 7811cb0ef41Sopenharmony_ci # Ensure temp dir exists for state files. 7821cb0ef41Sopenharmony_ci state_dir = os.path.dirname(self._config["PERSISTFILE_BASENAME"]) 7831cb0ef41Sopenharmony_ci if not os.path.exists(state_dir): 7841cb0ef41Sopenharmony_ci os.makedirs(state_dir) 7851cb0ef41Sopenharmony_ci 7861cb0ef41Sopenharmony_ci state_file = "%s-state.json" % self._config["PERSISTFILE_BASENAME"] 7871cb0ef41Sopenharmony_ci if options.step == 0 and os.path.exists(state_file): 7881cb0ef41Sopenharmony_ci os.remove(state_file) 7891cb0ef41Sopenharmony_ci 7901cb0ef41Sopenharmony_ci steps = [] 7911cb0ef41Sopenharmony_ci for (number, step_class) in enumerate([BootstrapStep] + step_classes): 7921cb0ef41Sopenharmony_ci steps.append(MakeStep(step_class, number, self._state, self._config, 7931cb0ef41Sopenharmony_ci options, self._side_effect_handler)) 7941cb0ef41Sopenharmony_ci 7951cb0ef41Sopenharmony_ci try: 7961cb0ef41Sopenharmony_ci for step in steps[options.step:]: 7971cb0ef41Sopenharmony_ci if step.Run(): 7981cb0ef41Sopenharmony_ci return 0 7991cb0ef41Sopenharmony_ci finally: 8001cb0ef41Sopenharmony_ci if options.json_output: 8011cb0ef41Sopenharmony_ci with open(options.json_output, "w") as f: 8021cb0ef41Sopenharmony_ci json.dump(self._state['json_output'], f) 8031cb0ef41Sopenharmony_ci 8041cb0ef41Sopenharmony_ci return 0 8051cb0ef41Sopenharmony_ci 8061cb0ef41Sopenharmony_ci def Run(self, args=None): 8071cb0ef41Sopenharmony_ci return self.RunSteps(self._Steps(), args) 808