1*3c875a21SAndroid Build Coastguard Worker# 2*3c875a21SAndroid Build Coastguard Worker# Copyright (C) 2018 The Android Open Source Project 3*3c875a21SAndroid Build Coastguard Worker# 4*3c875a21SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 5*3c875a21SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 6*3c875a21SAndroid Build Coastguard Worker# You may obtain a copy of the License at 7*3c875a21SAndroid Build Coastguard Worker# 8*3c875a21SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 9*3c875a21SAndroid Build Coastguard Worker# 10*3c875a21SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 11*3c875a21SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 12*3c875a21SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*3c875a21SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 14*3c875a21SAndroid Build Coastguard Worker# limitations under the License. 15*3c875a21SAndroid Build Coastguard Worker"""A commandline tool to check and update packages in external/ 16*3c875a21SAndroid Build Coastguard Worker 17*3c875a21SAndroid Build Coastguard WorkerExample usage: 18*3c875a21SAndroid Build Coastguard Workerupdater.sh checkall 19*3c875a21SAndroid Build Coastguard Workerupdater.sh update kotlinc 20*3c875a21SAndroid Build Coastguard Workerupdater.sh update --refresh --keep_date rust/crates/libc 21*3c875a21SAndroid Build Coastguard Worker""" 22*3c875a21SAndroid Build Coastguard Worker 23*3c875a21SAndroid Build Coastguard Workerimport argparse 24*3c875a21SAndroid Build Coastguard Workerfrom collections.abc import Iterable 25*3c875a21SAndroid Build Coastguard Workerimport json 26*3c875a21SAndroid Build Coastguard Workerimport logging 27*3c875a21SAndroid Build Coastguard Workerimport os 28*3c875a21SAndroid Build Coastguard Workerimport subprocess 29*3c875a21SAndroid Build Coastguard Workerimport textwrap 30*3c875a21SAndroid Build Coastguard Workerimport time 31*3c875a21SAndroid Build Coastguard Workerfrom typing import Dict, Iterator, List, Union, Tuple, Type 32*3c875a21SAndroid Build Coastguard Workerfrom pathlib import Path 33*3c875a21SAndroid Build Coastguard Worker 34*3c875a21SAndroid Build Coastguard Workerfrom base_updater import Updater 35*3c875a21SAndroid Build Coastguard Workerfrom color import Color, color_string 36*3c875a21SAndroid Build Coastguard Workerfrom crates_updater import CratesUpdater 37*3c875a21SAndroid Build Coastguard Workerfrom git_updater import GitUpdater 38*3c875a21SAndroid Build Coastguard Workerfrom github_archive_updater import GithubArchiveUpdater 39*3c875a21SAndroid Build Coastguard Workerimport fileutils 40*3c875a21SAndroid Build Coastguard Workerimport git_utils 41*3c875a21SAndroid Build Coastguard Worker# pylint: disable=import-error 42*3c875a21SAndroid Build Coastguard Workerimport metadata_pb2 # type: ignore 43*3c875a21SAndroid Build Coastguard Workerimport updater_utils 44*3c875a21SAndroid Build Coastguard Worker 45*3c875a21SAndroid Build Coastguard WorkerUPDATERS: List[Type[Updater]] = [ 46*3c875a21SAndroid Build Coastguard Worker CratesUpdater, 47*3c875a21SAndroid Build Coastguard Worker GithubArchiveUpdater, 48*3c875a21SAndroid Build Coastguard Worker GitUpdater, 49*3c875a21SAndroid Build Coastguard Worker] 50*3c875a21SAndroid Build Coastguard Worker 51*3c875a21SAndroid Build Coastguard WorkerTMP_BRANCH_NAME = 'tmp_auto_upgrade' 52*3c875a21SAndroid Build Coastguard Worker 53*3c875a21SAndroid Build Coastguard Worker 54*3c875a21SAndroid Build Coastguard Workerdef build_updater(proj_path: Path) -> Tuple[Updater, metadata_pb2.MetaData]: 55*3c875a21SAndroid Build Coastguard Worker """Build updater for a project specified by proj_path. 56*3c875a21SAndroid Build Coastguard Worker 57*3c875a21SAndroid Build Coastguard Worker Reads and parses METADATA file. And builds updater based on the information. 58*3c875a21SAndroid Build Coastguard Worker 59*3c875a21SAndroid Build Coastguard Worker Args: 60*3c875a21SAndroid Build Coastguard Worker proj_path: Absolute or relative path to the project. 61*3c875a21SAndroid Build Coastguard Worker 62*3c875a21SAndroid Build Coastguard Worker Returns: 63*3c875a21SAndroid Build Coastguard Worker The updater object built. None if there's any error. 64*3c875a21SAndroid Build Coastguard Worker """ 65*3c875a21SAndroid Build Coastguard Worker 66*3c875a21SAndroid Build Coastguard Worker proj_path = fileutils.get_absolute_project_path(proj_path) 67*3c875a21SAndroid Build Coastguard Worker metadata = fileutils.read_metadata(proj_path) 68*3c875a21SAndroid Build Coastguard Worker metadata = fileutils.convert_url_to_identifier(metadata) 69*3c875a21SAndroid Build Coastguard Worker updater = updater_utils.create_updater(metadata, proj_path, UPDATERS) 70*3c875a21SAndroid Build Coastguard Worker return updater, metadata 71*3c875a21SAndroid Build Coastguard Worker 72*3c875a21SAndroid Build Coastguard Worker 73*3c875a21SAndroid Build Coastguard Workerdef commit_message_generator(project_name: str, version: str, path: str, bug: int | None = None) -> str: 74*3c875a21SAndroid Build Coastguard Worker header = f"Upgrade {project_name} to {version}\n" 75*3c875a21SAndroid Build Coastguard Worker body = textwrap.dedent(f""" 76*3c875a21SAndroid Build Coastguard Worker This project was upgraded with external_updater. 77*3c875a21SAndroid Build Coastguard Worker Usage: tools/external_updater/updater.sh update external/{path} 78*3c875a21SAndroid Build Coastguard Worker For more info, check https://cs.android.com/android/platform/superproject/main/+/main:tools/external_updater/README.md\n\n""") 79*3c875a21SAndroid Build Coastguard Worker if bug is None: 80*3c875a21SAndroid Build Coastguard Worker footer = "Test: TreeHugger" 81*3c875a21SAndroid Build Coastguard Worker else: 82*3c875a21SAndroid Build Coastguard Worker footer = f"Bug: {bug}\nTest: TreeHugger" 83*3c875a21SAndroid Build Coastguard Worker return header + body + footer 84*3c875a21SAndroid Build Coastguard Worker 85*3c875a21SAndroid Build Coastguard Worker 86*3c875a21SAndroid Build Coastguard Workerdef _do_update(args: argparse.Namespace, updater: Updater, 87*3c875a21SAndroid Build Coastguard Worker metadata: metadata_pb2.MetaData) -> None: 88*3c875a21SAndroid Build Coastguard Worker full_path = updater.project_path 89*3c875a21SAndroid Build Coastguard Worker 90*3c875a21SAndroid Build Coastguard Worker if not args.keep_local_changes: 91*3c875a21SAndroid Build Coastguard Worker git_utils.detach_to_android_head(full_path) 92*3c875a21SAndroid Build Coastguard Worker if TMP_BRANCH_NAME in git_utils.list_local_branches(full_path): 93*3c875a21SAndroid Build Coastguard Worker git_utils.delete_branch(full_path, TMP_BRANCH_NAME) 94*3c875a21SAndroid Build Coastguard Worker git_utils.reset_hard(full_path) 95*3c875a21SAndroid Build Coastguard Worker git_utils.clean(full_path) 96*3c875a21SAndroid Build Coastguard Worker git_utils.start_branch(full_path, TMP_BRANCH_NAME) 97*3c875a21SAndroid Build Coastguard Worker 98*3c875a21SAndroid Build Coastguard Worker try: 99*3c875a21SAndroid Build Coastguard Worker updater.update() 100*3c875a21SAndroid Build Coastguard Worker 101*3c875a21SAndroid Build Coastguard Worker updated_metadata = updater.update_metadata(metadata) 102*3c875a21SAndroid Build Coastguard Worker fileutils.write_metadata(full_path, updated_metadata, args.keep_date) 103*3c875a21SAndroid Build Coastguard Worker git_utils.add_file(full_path, 'METADATA') 104*3c875a21SAndroid Build Coastguard Worker 105*3c875a21SAndroid Build Coastguard Worker try: 106*3c875a21SAndroid Build Coastguard Worker rel_proj_path = str(fileutils.get_relative_project_path(full_path)) 107*3c875a21SAndroid Build Coastguard Worker except ValueError: 108*3c875a21SAndroid Build Coastguard Worker # Absolute paths to other trees will not be relative to our tree. There are 109*3c875a21SAndroid Build Coastguard Worker # not portable instructions for upgrading that project, since the path will 110*3c875a21SAndroid Build Coastguard Worker # differ between machines (or checkouts). 111*3c875a21SAndroid Build Coastguard Worker rel_proj_path = "<absolute path to project>" 112*3c875a21SAndroid Build Coastguard Worker commit_message = commit_message_generator(metadata.name, updater.latest_version, rel_proj_path, args.bug) 113*3c875a21SAndroid Build Coastguard Worker git_utils.remove_gitmodules(full_path) 114*3c875a21SAndroid Build Coastguard Worker git_utils.add_file(full_path, '*') 115*3c875a21SAndroid Build Coastguard Worker git_utils.commit(full_path, commit_message, args.no_verify) 116*3c875a21SAndroid Build Coastguard Worker 117*3c875a21SAndroid Build Coastguard Worker if not args.skip_post_update: 118*3c875a21SAndroid Build Coastguard Worker updater_utils.run_post_update(full_path, full_path) 119*3c875a21SAndroid Build Coastguard Worker git_utils.add_file(full_path, '*') 120*3c875a21SAndroid Build Coastguard Worker git_utils.commit_amend(full_path) 121*3c875a21SAndroid Build Coastguard Worker 122*3c875a21SAndroid Build Coastguard Worker if args.build: 123*3c875a21SAndroid Build Coastguard Worker try: 124*3c875a21SAndroid Build Coastguard Worker updater_utils.build(full_path) 125*3c875a21SAndroid Build Coastguard Worker except subprocess.CalledProcessError: 126*3c875a21SAndroid Build Coastguard Worker logging.exception("Build failed, aborting upload") 127*3c875a21SAndroid Build Coastguard Worker return 128*3c875a21SAndroid Build Coastguard Worker except Exception as err: 129*3c875a21SAndroid Build Coastguard Worker if updater.rollback(): 130*3c875a21SAndroid Build Coastguard Worker print('Rolled back.') 131*3c875a21SAndroid Build Coastguard Worker raise err 132*3c875a21SAndroid Build Coastguard Worker 133*3c875a21SAndroid Build Coastguard Worker if not args.no_upload: 134*3c875a21SAndroid Build Coastguard Worker git_utils.push(full_path, args.remote_name, updater.has_errors) 135*3c875a21SAndroid Build Coastguard Worker 136*3c875a21SAndroid Build Coastguard Worker 137*3c875a21SAndroid Build Coastguard Workerdef has_new_version(updater: Updater) -> bool: 138*3c875a21SAndroid Build Coastguard Worker """Checks if a newer version of the project is available.""" 139*3c875a21SAndroid Build Coastguard Worker if updater.latest_version is not None and updater.current_version != updater.latest_version: 140*3c875a21SAndroid Build Coastguard Worker return True 141*3c875a21SAndroid Build Coastguard Worker return False 142*3c875a21SAndroid Build Coastguard Worker 143*3c875a21SAndroid Build Coastguard Worker 144*3c875a21SAndroid Build Coastguard Workerdef print_project_status(updater: Updater) -> None: 145*3c875a21SAndroid Build Coastguard Worker """Prints the current status of the project on console.""" 146*3c875a21SAndroid Build Coastguard Worker 147*3c875a21SAndroid Build Coastguard Worker current_version = updater.current_version 148*3c875a21SAndroid Build Coastguard Worker latest_version = updater.latest_version 149*3c875a21SAndroid Build Coastguard Worker alternative_latest_version = updater.alternative_latest_version 150*3c875a21SAndroid Build Coastguard Worker 151*3c875a21SAndroid Build Coastguard Worker print(f'Current version: {current_version}') 152*3c875a21SAndroid Build Coastguard Worker print('Latest version: ', end='') 153*3c875a21SAndroid Build Coastguard Worker if not latest_version: 154*3c875a21SAndroid Build Coastguard Worker print(color_string('Not available', Color.STALE)) 155*3c875a21SAndroid Build Coastguard Worker else: 156*3c875a21SAndroid Build Coastguard Worker print(latest_version) 157*3c875a21SAndroid Build Coastguard Worker if alternative_latest_version is not None: 158*3c875a21SAndroid Build Coastguard Worker print(f'Alternative latest version: {alternative_latest_version}') 159*3c875a21SAndroid Build Coastguard Worker if has_new_version(updater): 160*3c875a21SAndroid Build Coastguard Worker print(color_string('Out of date!', Color.STALE)) 161*3c875a21SAndroid Build Coastguard Worker else: 162*3c875a21SAndroid Build Coastguard Worker print(color_string('Up to date.', Color.FRESH)) 163*3c875a21SAndroid Build Coastguard Worker 164*3c875a21SAndroid Build Coastguard Worker 165*3c875a21SAndroid Build Coastguard Workerdef find_ver_types(current_version: str) -> Tuple[str, str]: 166*3c875a21SAndroid Build Coastguard Worker if git_utils.is_commit(current_version): 167*3c875a21SAndroid Build Coastguard Worker alternative_ver_type = 'tag' 168*3c875a21SAndroid Build Coastguard Worker latest_ver_type = 'sha' 169*3c875a21SAndroid Build Coastguard Worker else: 170*3c875a21SAndroid Build Coastguard Worker alternative_ver_type = 'sha' 171*3c875a21SAndroid Build Coastguard Worker latest_ver_type = 'tag' 172*3c875a21SAndroid Build Coastguard Worker return latest_ver_type, alternative_ver_type 173*3c875a21SAndroid Build Coastguard Worker 174*3c875a21SAndroid Build Coastguard Worker 175*3c875a21SAndroid Build Coastguard Workerdef use_alternative_version(updater: Updater) -> bool: 176*3c875a21SAndroid Build Coastguard Worker """This function only runs when there is an alternative version available.""" 177*3c875a21SAndroid Build Coastguard Worker 178*3c875a21SAndroid Build Coastguard Worker latest_ver_type, alternative_ver_type = find_ver_types(updater.current_version) 179*3c875a21SAndroid Build Coastguard Worker latest_version = updater.latest_version 180*3c875a21SAndroid Build Coastguard Worker alternative_version = updater.alternative_latest_version 181*3c875a21SAndroid Build Coastguard Worker new_version_available = has_new_version(updater) 182*3c875a21SAndroid Build Coastguard Worker 183*3c875a21SAndroid Build Coastguard Worker out_of_date_question = f'Would you like to upgrade to {alternative_ver_type} {alternative_version} instead of {latest_ver_type} {latest_version}? (yes/no)\n' 184*3c875a21SAndroid Build Coastguard Worker up_to_date_question = f'Would you like to upgrade to {alternative_ver_type} {alternative_version}? (yes/no)\n' 185*3c875a21SAndroid Build Coastguard Worker recom_message = color_string(f'We recommend upgrading to {alternative_ver_type} {alternative_version} instead. ', Color.FRESH) 186*3c875a21SAndroid Build Coastguard Worker not_recom_message = color_string(f'We DO NOT recommend upgrading to {alternative_ver_type} {alternative_version}. ', Color.STALE) 187*3c875a21SAndroid Build Coastguard Worker 188*3c875a21SAndroid Build Coastguard Worker # If alternative_version is not None, there are four possible 189*3c875a21SAndroid Build Coastguard Worker # scenarios: 190*3c875a21SAndroid Build Coastguard Worker # Scenario 1, out of date, we recommend switching to tag: 191*3c875a21SAndroid Build Coastguard Worker # Current version: sha1 192*3c875a21SAndroid Build Coastguard Worker # Latest version: sha2 193*3c875a21SAndroid Build Coastguard Worker # Alternative latest version: tag 194*3c875a21SAndroid Build Coastguard Worker 195*3c875a21SAndroid Build Coastguard Worker # Scenario 2, up to date, we DO NOT recommend switching to sha. 196*3c875a21SAndroid Build Coastguard Worker # Current version: tag1 197*3c875a21SAndroid Build Coastguard Worker # Latest version: tag1 198*3c875a21SAndroid Build Coastguard Worker # Alternative latest version: sha 199*3c875a21SAndroid Build Coastguard Worker 200*3c875a21SAndroid Build Coastguard Worker # Scenario 3, out of date, we DO NOT recommend switching to sha. 201*3c875a21SAndroid Build Coastguard Worker # Current version: tag1 202*3c875a21SAndroid Build Coastguard Worker # Latest version: tag2 203*3c875a21SAndroid Build Coastguard Worker # Alternative latest version: sha 204*3c875a21SAndroid Build Coastguard Worker 205*3c875a21SAndroid Build Coastguard Worker # Scenario 4, out of date, no recommendations at all 206*3c875a21SAndroid Build Coastguard Worker # Current version: sha1 207*3c875a21SAndroid Build Coastguard Worker # Latest version: No tag found or a tag that doesn't belong to any branch 208*3c875a21SAndroid Build Coastguard Worker # Alternative latest version: sha 209*3c875a21SAndroid Build Coastguard Worker 210*3c875a21SAndroid Build Coastguard Worker if alternative_ver_type == 'tag': 211*3c875a21SAndroid Build Coastguard Worker warning = out_of_date_question + recom_message 212*3c875a21SAndroid Build Coastguard Worker else: 213*3c875a21SAndroid Build Coastguard Worker if not new_version_available: 214*3c875a21SAndroid Build Coastguard Worker warning = up_to_date_question + not_recom_message 215*3c875a21SAndroid Build Coastguard Worker else: 216*3c875a21SAndroid Build Coastguard Worker if not latest_version: 217*3c875a21SAndroid Build Coastguard Worker warning = up_to_date_question 218*3c875a21SAndroid Build Coastguard Worker else: 219*3c875a21SAndroid Build Coastguard Worker warning = out_of_date_question + not_recom_message 220*3c875a21SAndroid Build Coastguard Worker 221*3c875a21SAndroid Build Coastguard Worker answer = input(warning) 222*3c875a21SAndroid Build Coastguard Worker if "yes".startswith(answer.lower()): 223*3c875a21SAndroid Build Coastguard Worker return True 224*3c875a21SAndroid Build Coastguard Worker elif answer.lower().startswith("no"): 225*3c875a21SAndroid Build Coastguard Worker return False 226*3c875a21SAndroid Build Coastguard Worker # If user types something that is not "yes" or "no" or something similar, abort. 227*3c875a21SAndroid Build Coastguard Worker else: 228*3c875a21SAndroid Build Coastguard Worker raise ValueError(f"Invalid input: {answer}") 229*3c875a21SAndroid Build Coastguard Worker 230*3c875a21SAndroid Build Coastguard Worker 231*3c875a21SAndroid Build Coastguard Workerdef check_and_update(args: argparse.Namespace, 232*3c875a21SAndroid Build Coastguard Worker proj_path: Path, 233*3c875a21SAndroid Build Coastguard Worker update_lib=False) -> Union[Updater, str]: 234*3c875a21SAndroid Build Coastguard Worker """Checks updates for a project. 235*3c875a21SAndroid Build Coastguard Worker 236*3c875a21SAndroid Build Coastguard Worker Args: 237*3c875a21SAndroid Build Coastguard Worker args: commandline arguments 238*3c875a21SAndroid Build Coastguard Worker proj_path: Absolute or relative path to the project. 239*3c875a21SAndroid Build Coastguard Worker update_lib: If false, will only check for new version, but not update. 240*3c875a21SAndroid Build Coastguard Worker """ 241*3c875a21SAndroid Build Coastguard Worker 242*3c875a21SAndroid Build Coastguard Worker try: 243*3c875a21SAndroid Build Coastguard Worker canonical_path = fileutils.canonicalize_project_path(proj_path) 244*3c875a21SAndroid Build Coastguard Worker print(f'Checking {canonical_path}...') 245*3c875a21SAndroid Build Coastguard Worker updater, metadata = build_updater(proj_path) 246*3c875a21SAndroid Build Coastguard Worker updater.check() 247*3c875a21SAndroid Build Coastguard Worker 248*3c875a21SAndroid Build Coastguard Worker new_version_available = has_new_version(updater) 249*3c875a21SAndroid Build Coastguard Worker print_project_status(updater) 250*3c875a21SAndroid Build Coastguard Worker 251*3c875a21SAndroid Build Coastguard Worker if update_lib: 252*3c875a21SAndroid Build Coastguard Worker if args.custom_version is not None: 253*3c875a21SAndroid Build Coastguard Worker updater.set_custom_version(args.custom_version) 254*3c875a21SAndroid Build Coastguard Worker print(f"Upgrading to custom version {args.custom_version}") 255*3c875a21SAndroid Build Coastguard Worker elif args.refresh: 256*3c875a21SAndroid Build Coastguard Worker updater.refresh_without_upgrading() 257*3c875a21SAndroid Build Coastguard Worker elif new_version_available: 258*3c875a21SAndroid Build Coastguard Worker if updater.alternative_latest_version is not None: 259*3c875a21SAndroid Build Coastguard Worker if use_alternative_version(updater): 260*3c875a21SAndroid Build Coastguard Worker updater.set_new_version(updater.alternative_latest_version) 261*3c875a21SAndroid Build Coastguard Worker else: 262*3c875a21SAndroid Build Coastguard Worker return updater 263*3c875a21SAndroid Build Coastguard Worker _do_update(args, updater, metadata) 264*3c875a21SAndroid Build Coastguard Worker return updater 265*3c875a21SAndroid Build Coastguard Worker 266*3c875a21SAndroid Build Coastguard Worker # pylint: disable=broad-except 267*3c875a21SAndroid Build Coastguard Worker except Exception as err: 268*3c875a21SAndroid Build Coastguard Worker logging.exception("Failed to check or update %s", proj_path) 269*3c875a21SAndroid Build Coastguard Worker return str(err) 270*3c875a21SAndroid Build Coastguard Worker 271*3c875a21SAndroid Build Coastguard Worker 272*3c875a21SAndroid Build Coastguard Workerdef check_and_update_path(args: argparse.Namespace, paths: Iterable[Path], 273*3c875a21SAndroid Build Coastguard Worker update_lib: bool, 274*3c875a21SAndroid Build Coastguard Worker delay: int) -> Dict[str, Dict[str, str]]: 275*3c875a21SAndroid Build Coastguard Worker results = {} 276*3c875a21SAndroid Build Coastguard Worker for path in paths: 277*3c875a21SAndroid Build Coastguard Worker res = {} 278*3c875a21SAndroid Build Coastguard Worker updater = check_and_update(args, path, update_lib) 279*3c875a21SAndroid Build Coastguard Worker if isinstance(updater, str): 280*3c875a21SAndroid Build Coastguard Worker res['error'] = updater 281*3c875a21SAndroid Build Coastguard Worker else: 282*3c875a21SAndroid Build Coastguard Worker res['current'] = updater.current_version 283*3c875a21SAndroid Build Coastguard Worker res['latest'] = updater.latest_version 284*3c875a21SAndroid Build Coastguard Worker results[str(fileutils.canonicalize_project_path(path))] = res 285*3c875a21SAndroid Build Coastguard Worker time.sleep(delay) 286*3c875a21SAndroid Build Coastguard Worker return results 287*3c875a21SAndroid Build Coastguard Worker 288*3c875a21SAndroid Build Coastguard Worker 289*3c875a21SAndroid Build Coastguard Workerdef _list_all_metadata() -> Iterator[str]: 290*3c875a21SAndroid Build Coastguard Worker for path, dirs, files in os.walk(fileutils.external_path()): 291*3c875a21SAndroid Build Coastguard Worker if fileutils.METADATA_FILENAME in files: 292*3c875a21SAndroid Build Coastguard Worker # Skip sub directories. 293*3c875a21SAndroid Build Coastguard Worker dirs[:] = [] 294*3c875a21SAndroid Build Coastguard Worker yield path 295*3c875a21SAndroid Build Coastguard Worker dirs.sort(key=lambda d: d.lower()) 296*3c875a21SAndroid Build Coastguard Worker 297*3c875a21SAndroid Build Coastguard Worker 298*3c875a21SAndroid Build Coastguard Workerdef write_json(json_file: str, results: Dict[str, Dict[str, str]]) -> None: 299*3c875a21SAndroid Build Coastguard Worker """Output a JSON report.""" 300*3c875a21SAndroid Build Coastguard Worker with Path(json_file).open('w', encoding='utf-8') as res_file: 301*3c875a21SAndroid Build Coastguard Worker json.dump(results, res_file, sort_keys=True, indent=4) 302*3c875a21SAndroid Build Coastguard Worker 303*3c875a21SAndroid Build Coastguard Worker 304*3c875a21SAndroid Build Coastguard Workerdef validate(args: argparse.Namespace) -> None: 305*3c875a21SAndroid Build Coastguard Worker """Handler for validate command.""" 306*3c875a21SAndroid Build Coastguard Worker paths = fileutils.resolve_command_line_paths(args.paths) 307*3c875a21SAndroid Build Coastguard Worker try: 308*3c875a21SAndroid Build Coastguard Worker canonical_path = fileutils.canonicalize_project_path(paths[0]) 309*3c875a21SAndroid Build Coastguard Worker print(f'Validating {canonical_path}') 310*3c875a21SAndroid Build Coastguard Worker updater, _ = build_updater(paths[0]) 311*3c875a21SAndroid Build Coastguard Worker print(updater.validate()) 312*3c875a21SAndroid Build Coastguard Worker except Exception: # pylint: disable=broad-exception-caught 313*3c875a21SAndroid Build Coastguard Worker logging.exception("Failed to check or update %s", paths) 314*3c875a21SAndroid Build Coastguard Worker 315*3c875a21SAndroid Build Coastguard Worker 316*3c875a21SAndroid Build Coastguard Workerdef check(args: argparse.Namespace) -> None: 317*3c875a21SAndroid Build Coastguard Worker """Handler for check command.""" 318*3c875a21SAndroid Build Coastguard Worker if args.all: 319*3c875a21SAndroid Build Coastguard Worker paths = [Path(p) for p in _list_all_metadata()] 320*3c875a21SAndroid Build Coastguard Worker else: 321*3c875a21SAndroid Build Coastguard Worker paths = fileutils.resolve_command_line_paths(args.paths) 322*3c875a21SAndroid Build Coastguard Worker results = check_and_update_path(args, paths, False, args.delay) 323*3c875a21SAndroid Build Coastguard Worker 324*3c875a21SAndroid Build Coastguard Worker if args.json_output is not None: 325*3c875a21SAndroid Build Coastguard Worker write_json(args.json_output, results) 326*3c875a21SAndroid Build Coastguard Worker 327*3c875a21SAndroid Build Coastguard Worker 328*3c875a21SAndroid Build Coastguard Workerdef update(args: argparse.Namespace) -> None: 329*3c875a21SAndroid Build Coastguard Worker """Handler for update command.""" 330*3c875a21SAndroid Build Coastguard Worker all_paths = fileutils.resolve_command_line_paths(args.paths) 331*3c875a21SAndroid Build Coastguard Worker # Remove excluded paths. 332*3c875a21SAndroid Build Coastguard Worker excludes = set() if args.exclude is None else set(args.exclude) 333*3c875a21SAndroid Build Coastguard Worker filtered_paths = [path for path in all_paths 334*3c875a21SAndroid Build Coastguard Worker if not path.name in excludes] 335*3c875a21SAndroid Build Coastguard Worker # Now we can update each path. 336*3c875a21SAndroid Build Coastguard Worker results = check_and_update_path(args, filtered_paths, True, 0) 337*3c875a21SAndroid Build Coastguard Worker 338*3c875a21SAndroid Build Coastguard Worker if args.json_output is not None: 339*3c875a21SAndroid Build Coastguard Worker write_json(args.json_output, results) 340*3c875a21SAndroid Build Coastguard Worker 341*3c875a21SAndroid Build Coastguard Worker 342*3c875a21SAndroid Build Coastguard Workerdef parse_args() -> argparse.Namespace: 343*3c875a21SAndroid Build Coastguard Worker """Parses commandline arguments.""" 344*3c875a21SAndroid Build Coastguard Worker 345*3c875a21SAndroid Build Coastguard Worker parser = argparse.ArgumentParser( 346*3c875a21SAndroid Build Coastguard Worker description='Check updates for third party projects in external/.') 347*3c875a21SAndroid Build Coastguard Worker subparsers = parser.add_subparsers(dest='cmd') 348*3c875a21SAndroid Build Coastguard Worker subparsers.required = True 349*3c875a21SAndroid Build Coastguard Worker 350*3c875a21SAndroid Build Coastguard Worker diff_parser = subparsers.add_parser('validate', 351*3c875a21SAndroid Build Coastguard Worker help='Check if aosp version is what it claims to be.') 352*3c875a21SAndroid Build Coastguard Worker diff_parser.add_argument( 353*3c875a21SAndroid Build Coastguard Worker 'paths', 354*3c875a21SAndroid Build Coastguard Worker nargs='*', 355*3c875a21SAndroid Build Coastguard Worker help='Paths of the project. ' 356*3c875a21SAndroid Build Coastguard Worker 'Relative paths will be resolved from external/.') 357*3c875a21SAndroid Build Coastguard Worker diff_parser.set_defaults(func=validate) 358*3c875a21SAndroid Build Coastguard Worker 359*3c875a21SAndroid Build Coastguard Worker # Creates parser for check command. 360*3c875a21SAndroid Build Coastguard Worker check_parser = subparsers.add_parser('check', 361*3c875a21SAndroid Build Coastguard Worker help='Check update for one project.') 362*3c875a21SAndroid Build Coastguard Worker check_parser.add_argument( 363*3c875a21SAndroid Build Coastguard Worker 'paths', 364*3c875a21SAndroid Build Coastguard Worker nargs='*', 365*3c875a21SAndroid Build Coastguard Worker help='Paths of the project. ' 366*3c875a21SAndroid Build Coastguard Worker 'Relative paths will be resolved from external/.') 367*3c875a21SAndroid Build Coastguard Worker check_parser.add_argument('--json-output', 368*3c875a21SAndroid Build Coastguard Worker help='Path of a json file to write result to.') 369*3c875a21SAndroid Build Coastguard Worker check_parser.add_argument( 370*3c875a21SAndroid Build Coastguard Worker '--all', 371*3c875a21SAndroid Build Coastguard Worker action='store_true', 372*3c875a21SAndroid Build Coastguard Worker help='If set, check updates for all supported projects.') 373*3c875a21SAndroid Build Coastguard Worker check_parser.add_argument( 374*3c875a21SAndroid Build Coastguard Worker '--delay', 375*3c875a21SAndroid Build Coastguard Worker default=0, 376*3c875a21SAndroid Build Coastguard Worker type=int, 377*3c875a21SAndroid Build Coastguard Worker help='Time in seconds to wait between checking two projects.') 378*3c875a21SAndroid Build Coastguard Worker check_parser.set_defaults(func=check) 379*3c875a21SAndroid Build Coastguard Worker 380*3c875a21SAndroid Build Coastguard Worker # Creates parser for update command. 381*3c875a21SAndroid Build Coastguard Worker update_parser = subparsers.add_parser('update', help='Update one project.') 382*3c875a21SAndroid Build Coastguard Worker update_parser.add_argument( 383*3c875a21SAndroid Build Coastguard Worker 'paths', 384*3c875a21SAndroid Build Coastguard Worker nargs='*', 385*3c875a21SAndroid Build Coastguard Worker help='Paths of the project as globs. ' 386*3c875a21SAndroid Build Coastguard Worker 'Relative paths will be resolved from external/.') 387*3c875a21SAndroid Build Coastguard Worker update_parser.add_argument('--json-output', 388*3c875a21SAndroid Build Coastguard Worker help='Path of a json file to write result to.') 389*3c875a21SAndroid Build Coastguard Worker update_parser.add_argument( 390*3c875a21SAndroid Build Coastguard Worker '--refresh', 391*3c875a21SAndroid Build Coastguard Worker help='Run update and refresh to the current version.', 392*3c875a21SAndroid Build Coastguard Worker action='store_true') 393*3c875a21SAndroid Build Coastguard Worker update_parser.add_argument( 394*3c875a21SAndroid Build Coastguard Worker '--keep-date', 395*3c875a21SAndroid Build Coastguard Worker help='Run update and do not change date in METADATA.', 396*3c875a21SAndroid Build Coastguard Worker action='store_true') 397*3c875a21SAndroid Build Coastguard Worker update_parser.add_argument('--no-upload', 398*3c875a21SAndroid Build Coastguard Worker action='store_true', 399*3c875a21SAndroid Build Coastguard Worker help='Does not upload to Gerrit after upgrade') 400*3c875a21SAndroid Build Coastguard Worker update_parser.add_argument('--keep-local-changes', 401*3c875a21SAndroid Build Coastguard Worker action='store_true', 402*3c875a21SAndroid Build Coastguard Worker help='Updates the current branch') 403*3c875a21SAndroid Build Coastguard Worker update_parser.add_argument('--skip-post-update', 404*3c875a21SAndroid Build Coastguard Worker action='store_true', 405*3c875a21SAndroid Build Coastguard Worker help='Skip post_update script') 406*3c875a21SAndroid Build Coastguard Worker update_parser.add_argument('--no-build', 407*3c875a21SAndroid Build Coastguard Worker action='store_false', 408*3c875a21SAndroid Build Coastguard Worker dest='build', 409*3c875a21SAndroid Build Coastguard Worker help='Skip building') 410*3c875a21SAndroid Build Coastguard Worker update_parser.add_argument('--no-verify', 411*3c875a21SAndroid Build Coastguard Worker action='store_true', 412*3c875a21SAndroid Build Coastguard Worker help='Pass --no-verify to git commit') 413*3c875a21SAndroid Build Coastguard Worker update_parser.add_argument('--bug', 414*3c875a21SAndroid Build Coastguard Worker type=int, 415*3c875a21SAndroid Build Coastguard Worker help='Bug number for this update') 416*3c875a21SAndroid Build Coastguard Worker update_parser.add_argument('--custom-version', 417*3c875a21SAndroid Build Coastguard Worker type=str, 418*3c875a21SAndroid Build Coastguard Worker help='Custom version we want to upgrade to.') 419*3c875a21SAndroid Build Coastguard Worker update_parser.add_argument('--remote-name', 420*3c875a21SAndroid Build Coastguard Worker default='aosp', 421*3c875a21SAndroid Build Coastguard Worker required=False, 422*3c875a21SAndroid Build Coastguard Worker help='Upstream remote name.') 423*3c875a21SAndroid Build Coastguard Worker update_parser.add_argument('--exclude', 424*3c875a21SAndroid Build Coastguard Worker action='append', 425*3c875a21SAndroid Build Coastguard Worker help='Names of projects to exclude. ' 426*3c875a21SAndroid Build Coastguard Worker 'These are just the final part of the path ' 427*3c875a21SAndroid Build Coastguard Worker 'with no directories.') 428*3c875a21SAndroid Build Coastguard Worker update_parser.set_defaults(func=update) 429*3c875a21SAndroid Build Coastguard Worker 430*3c875a21SAndroid Build Coastguard Worker return parser.parse_args() 431*3c875a21SAndroid Build Coastguard Worker 432*3c875a21SAndroid Build Coastguard Worker 433*3c875a21SAndroid Build Coastguard Workerdef main() -> None: 434*3c875a21SAndroid Build Coastguard Worker """The main entry.""" 435*3c875a21SAndroid Build Coastguard Worker 436*3c875a21SAndroid Build Coastguard Worker args = parse_args() 437*3c875a21SAndroid Build Coastguard Worker args.func(args) 438*3c875a21SAndroid Build Coastguard Worker 439*3c875a21SAndroid Build Coastguard Worker 440*3c875a21SAndroid Build Coastguard Workerif __name__ == '__main__': 441*3c875a21SAndroid Build Coastguard Worker main() 442