• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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