tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

fetch_util.py (6077B)


      1 # Copyright 2025 The Chromium Authors
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import hashlib
      6 import json
      7 import pathlib
      8 import os
      9 import subprocess
     10 import zipfile
     11 import re
     12 
     13 _SRC_PATH = pathlib.Path(__file__).resolve().parents[2]
     14 _FETCH_ALL_PATH = _SRC_PATH / 'third_party/android_deps/fetch_all.py'
     15 _HASH_LENGTH = 15
     16 _SKIP_FILES = ('OWNERS', 'cipd.yaml')
     17 
     18 _DEFAULT_GENERATED_DISCLAIMER = '''\
     19 // **IMPORTANT**: build.gradle is generated and any changes would be overridden
     20 //                by the autoroller. Please update build.gradle.template
     21 //                instead.
     22 '''
     23 
     24 
     25 def generate_version_map_str(bom_path, with_hash=False):
     26  """Generate groovy code to fill the versionCache map.
     27 
     28    Args:
     29      bom_path: Path to bill_of_materials.json to parse.
     30      with_hash: Whether to also return a hash of all the packages in the BoM.
     31    """
     32  bom = []
     33  version_map_lines = []
     34  bom_hash = hashlib.sha256()
     35  with open(bom_path) as f:
     36    bom = json.load(f)
     37  bom.sort(key=lambda x: (x['group'], x['name']))
     38  for dep in bom:
     39    group = dep['group']
     40    name = dep['name']
     41    version = dep['version']
     42    bom_hash.update(f'${group}:${name}:${version}'.encode())
     43    map_line = f"versionCache['{group}:{name}'] = '{version}'"
     44    version_map_lines.append(map_line)
     45  version_map_str = '\n'.join(sorted(version_map_lines))
     46  version_hash = bom_hash.hexdigest()[:_HASH_LENGTH]
     47  if with_hash:
     48    return version_map_str, version_hash
     49  return version_map_str
     50 
     51 
     52 def fill_template(template_path, output_path, **kwargs):
     53  """Fills in a template.
     54 
     55    Args:
     56      template_path: Path to <file>.template.
     57      output_path: Path to <file>.
     58      **kwargs: each kwarg should be a string to replace in the template.
     59    """
     60  content = pathlib.Path(template_path).read_text()
     61  for key, value in kwargs.items():
     62    replace_string = '{{' + key + '}}'
     63    if not replace_string in content:
     64      raise Exception(f'Replace text {replace_string} '
     65                      f'not found in {template_path}')
     66    try:
     67      content = content.replace(replace_string, value)
     68    except Exception as e:
     69      raise e from Exception(
     70          f'Failed to replace {repr(replace_string)} with {repr(value)}')
     71 
     72  content = content.replace(r'{{generated_disclaimer}}',
     73                            _DEFAULT_GENERATED_DISCLAIMER)
     74 
     75  unreplaced_variable_re = re.compile(r'\{\{(.+)\}\}')
     76  if matches := unreplaced_variable_re.findall(content):
     77    unreplaced_variables = ', '.join(repr(match) for match in matches)
     78    raise Exception('Found unreplaced variables '
     79                    f'[{unreplaced_variables}] in {template_path}')
     80 
     81  pathlib.Path(output_path).write_text(content)
     82 
     83 
     84 def write_cipd_yaml(package_root,
     85                    package_name,
     86                    version,
     87                    output_path,
     88                    experimental=False):
     89  """Writes cipd.yaml file at the passed-in path."""
     90 
     91  root_libs_dir = package_root / 'libs'
     92  lib_dirs = os.listdir(root_libs_dir)
     93  if not lib_dirs:
     94    raise Exception('No generated libraries in {}'.format(root_libs_dir))
     95 
     96  data_files = [
     97      'BUILD.gn',
     98      'VERSION.txt',
     99      'bill_of_materials.json',
    100      'additional_readme_paths.json',
    101      'build.gradle',
    102      'to_commit.zip',
    103  ]
    104  for lib_dir in lib_dirs:
    105    abs_lib_dir: pathlib.Path = root_libs_dir / lib_dir
    106    if not abs_lib_dir.is_dir():
    107      continue
    108 
    109    for lib_file in abs_lib_dir.iterdir():
    110      if lib_file.name in _SKIP_FILES:
    111        continue
    112      data_files.append((abs_lib_dir / lib_file).relative_to(package_root))
    113 
    114  if experimental:
    115    package_name = (f'experimental/google.com/{os.getlogin()}/{package_name}')
    116  contents = [
    117      '# Copyright 2025 The Chromium Authors',
    118      '# Use of this source code is governed by a BSD-style license that can be',
    119      '# found in the LICENSE file.',
    120      f'# version: {version}',
    121      f'package: {package_name}',
    122      f'description: CIPD package for {package_name}',
    123      'data:',
    124  ]
    125  contents.extend(f'- file: {str(f)}' for f in data_files)
    126 
    127  with open(output_path, 'w') as out:
    128    out.write('\n'.join(contents))
    129 
    130 
    131 def create_to_commit_zip(output_path, package_root, dirnames,
    132                         absolute_file_map):
    133  """Generates a to_commit.zip from useful text files inside |package_root|.
    134 
    135    Args:
    136      output_path: where to output the zipfile.
    137      package_root: path to gradle/cipd package.
    138      dirnames: list of subdirs under |package_root| to walk.
    139      absolute_file_map: List of files to be stored under the absolute prefix
    140        CHROMIUM_SRC/.
    141  """
    142  to_commit_paths = []
    143  for directory in dirnames:
    144    for root, _, files in os.walk(package_root / directory):
    145      for filename in files:
    146        # Avoid committing actual artifacts.
    147        if filename.endswith(('.aar', '.jar')):
    148          continue
    149        # TODO(mheikal): stop outputting these from gradle since they are not
    150        # useful.
    151        if filename in _SKIP_FILES:
    152          continue
    153        file_path = pathlib.Path(root) / filename
    154        file_path_in_zip = file_path.relative_to(package_root)
    155        to_commit_paths.append((file_path, file_path_in_zip))
    156 
    157  for filename, path_in_repo in absolute_file_map.items():
    158    file_path = package_root / filename
    159    path_in_zip = f'CHROMIUM_SRC/{path_in_repo}'
    160    to_commit_paths.append((file_path, path_in_zip))
    161 
    162  with zipfile.ZipFile(output_path, 'w') as zip_file:
    163    for filename, arcname in to_commit_paths:
    164      zip_file.write(filename, arcname=arcname)
    165 
    166 
    167 def run_fetch_all(android_deps_dir,
    168                  extra_args,
    169                  verbose_count=0,
    170                  output_subdir=None):
    171  fetch_all_cmd = [
    172      _FETCH_ALL_PATH, '--android-deps-dir', android_deps_dir,
    173      '--ignore-vulnerabilities'
    174  ] + ['-v'] * verbose_count
    175  if output_subdir:
    176    fetch_all_cmd += ['--output-subdir', output_subdir]
    177 
    178  # Filter out -- from the args to pass to fetch_all.py.
    179  fetch_all_cmd += [a for a in extra_args if a != '--']
    180 
    181  subprocess.run(fetch_all_cmd, check=True)