1#!/usr/bin/env vpython3 2# Copyright 2022 The Chromium Authors 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5"""Updates the Fuchsia product bundles to the given revision. Should be used 6in a 'hooks_os' entry so that it only runs when .gclient's target_os includes 7'fuchsia'.""" 8 9import argparse 10import json 11import logging 12import os 13import sys 14 15sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), 16 'test'))) 17 18import common 19import update_sdk 20 21 22# TODO(crbug/1361089): Remove when the old scripts have been deprecated. 23_IMAGE_TO_PRODUCT_BUNDLE = { 24 'core.x64-dfv2-release': 'core.x64-dfv2', 25 'qemu.arm64': 'terminal.qemu-arm64', 26 'qemu.x64': 'terminal.x64', 27 'workstation_eng.chromebook-x64-dfv2-release': 28 'workstation_eng.chromebook-x64-dfv2', 29 'workstation_eng.chromebook-x64-release': 'workstation_eng.chromebook-x64', 30 'workstation_eng.qemu-x64-release': 'workstation_eng.qemu-x64', 31} 32 33 34# TODO(crbug/1361089): Remove when the old scripts have been deprecated. 35def convert_to_products(images_list): 36 """Convert image names in the SDK to product bundle names.""" 37 38 product_bundle_list = [] 39 for image in images_list: 40 if image in _IMAGE_TO_PRODUCT_BUNDLE: 41 logging.warning(f'Image name {image} has been deprecated. Use ' 42 f'{_IMAGE_TO_PRODUCT_BUNDLE.get(image)} instead.') 43 product_bundle_list.append(_IMAGE_TO_PRODUCT_BUNDLE.get(image, image)) 44 return product_bundle_list 45 46 47def remove_repositories(repo_names_to_remove): 48 """Removes given repos from repo list. 49 Repo MUST be present in list to succeed. 50 51 Args: 52 repo_names_to_remove: List of repo names (as strings) to remove. 53 """ 54 for repo_name in repo_names_to_remove: 55 common.run_ffx_command(cmd=('repository', 'remove', repo_name), check=True) 56 57 58def get_repositories(): 59 """Lists repositories that are available on disk. 60 61 Also prunes repositories that are listed, but do not have an actual packages 62 directory. 63 64 Returns: 65 List of dictionaries containing info about the repositories. They have the 66 following structure: 67 { 68 'name': <repo name>, 69 'spec': { 70 'type': <type, usually pm>, 71 'path': <path to packages directory> 72 }, 73 } 74 """ 75 76 repos = json.loads( 77 common.run_ffx_command(cmd=('--machine', 'json', 'repository', 'list'), 78 check=True, 79 capture_output=True).stdout.strip()) 80 to_prune = set() 81 sdk_root_abspath = os.path.abspath(os.path.dirname(common.SDK_ROOT)) 82 for repo in repos: 83 # Confirm the path actually exists. If not, prune list. 84 # Also assert the product-bundle repository is for the current repo 85 # (IE within the same directory). 86 if not os.path.exists(repo['spec']['path']): 87 to_prune.add(repo['name']) 88 89 if not repo['spec']['path'].startswith(sdk_root_abspath): 90 to_prune.add(repo['name']) 91 92 repos = [repo for repo in repos if repo['name'] not in to_prune] 93 94 remove_repositories(to_prune) 95 return repos 96 97 98def get_current_signature(image_dir): 99 """Determines the current version of the image, if it exists. 100 101 Returns: 102 The current version, or None if the image is non-existent. 103 """ 104 105 version_file = os.path.join(image_dir, 'product_bundle.json') 106 if os.path.exists(version_file): 107 with open(version_file) as f: 108 return json.load(f)['product_version'] 109 return None 110 111 112def main(): 113 parser = argparse.ArgumentParser() 114 parser.add_argument('--verbose', 115 '-v', 116 action='store_true', 117 help='Enable debug-level logging.') 118 parser.add_argument( 119 'products', 120 type=str, 121 help='List of product bundles to download, represented as a comma ' 122 'separated list.') 123 parser.add_argument('--auth', 124 action='store_true', 125 help='Enable additional authorization for ffx product') 126 args = parser.parse_args() 127 128 logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO) 129 130 # Check whether there's Fuchsia support for this platform. 131 common.get_host_os() 132 133 new_products = convert_to_products(args.products.split(',')) 134 logging.info('Searching for the following products: %s', str(new_products)) 135 136 logging.debug('Getting new SDK hash') 137 new_sdk_hash = common.get_hash_from_sdk() 138 139 auth_args = [ 140 '--auth', 141 os.path.join(os.path.dirname(__file__), 'get_auth_token.py') 142 ] if args.auth else [] 143 144 for product in new_products: 145 prod, board = product.split('.', 1) 146 image_dir = os.path.join(common.IMAGES_ROOT, prod, board) 147 148 curr_signature = get_current_signature(image_dir) 149 150 if curr_signature != new_sdk_hash: 151 common.make_clean_directory(image_dir) 152 logging.debug('Checking for override file') 153 override_file = os.path.join(os.path.dirname(__file__), 154 'sdk_override.txt') 155 if os.path.isfile(override_file): 156 base_url = update_sdk.GetSDKOverrideGCSPath().replace('/sdk', '') 157 else: 158 base_url = f'gs://fuchsia/development/{new_sdk_hash}' 159 download_url = common.run_ffx_command(cmd=[ 160 'product', 'lookup', product, new_sdk_hash, '--base-url', base_url 161 ] + auth_args, 162 check=True, 163 capture_output=True).stdout.strip() 164 logging.info(f'Downloading {product} from {base_url}.') 165 common.run_ffx_command( 166 cmd=['product', 'download', download_url, image_dir] + auth_args, 167 check=True) 168 169 return 0 170 171 172if __name__ == '__main__': 173 sys.exit(main()) 174