File: downloader.py

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (173 lines) | stat: -rw-r--r-- 6,321 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# Copyright 2023 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Downloader module to download various versions of Chrome from GCS bucket.

download_chrome_{platform}(version) returns a folder that contains Chrome
binary and chromedriver so it is ready to run a webdriver test.
"""

import functools
import json
import os
import shutil
import subprocess
import sys

from chrome.test.variations.test_utils import helper
from chrome.test.variations.test_utils import SRC_DIR
import packaging
from typing import List, Optional
from urllib.request import urlopen

GSUTIL_PATH = os.path.join(
    SRC_DIR, 'third_party', 'catapult', 'third_party', 'gsutil', 'gsutil')

CHROME_DIR = os.path.join(SRC_DIR, "_chrome")


@functools.lru_cache
def _find_gsutil_cmd() -> str:
  if gsutil := (shutil.which('gsutil.py') or shutil.which('gsutil')):
    return gsutil
  if os.path.exists(GSUTIL_PATH):
    return GSUTIL_PATH
  raise RuntimeError("Please specify script path for gsutil or run "
                     "'sudo apt install google-cloud-sdk' and try again.")

def _download_files_from_gcs(version: str, files: List[str]) -> str:
  downloaded_dir = os.path.join(CHROME_DIR, version)
  os.makedirs(downloaded_dir, exist_ok=True)

  # TODO: we can compare the local md5 if existed to avoid repetitive downloads
  gs_cmd = [_find_gsutil_cmd(), "cp"]
  gs_cmd.extend([f'gs://chrome-unsigned/desktop-5c0tCh/{version}/{file}'
                 for file in files])
  gs_cmd.extend([downloaded_dir])

  if helper.get_hosted_platform() == "win":
    subprocess.run([sys.executable] + gs_cmd, check=False)
    for file in files:
      unzip_cmd = ["powershell", "-command",
                   "Expand-Archive -Force '%s'" %
                     os.path.join(downloaded_dir, os.path.basename(file)),
                   downloaded_dir]
      subprocess.run(unzip_cmd, check=False)

  else:
    subprocess.run(gs_cmd, check=False)

    for file in files:
      unzip_cmd = ["unzip",
                   "-o",
                   os.path.join(downloaded_dir, os.path.basename(file)),
                   "-d",
                   downloaded_dir]
      subprocess.run(unzip_cmd, capture_output=True, check=False)
  return downloaded_dir


def download_chromedriver(platform: str, version: Optional[str]) -> str:
  """Download the latest available chromedriver for the platform.

  Args:
    platform: The platform that chromedriver is running on
    version: The version of chromedriver. It will find the latest if None.

  Returns:
    the path to the chromedriver executable
  """

  # Linux doesn't have canary
  if platform == 'linux':
    channel = 'dev'
    release_os = 'linux'
    driver_pathname = 'linux64/chromedriver_linux64.zip'
  elif platform == 'win':
    channel = 'canary'
    release_os = 'win64'
    driver_pathname = 'win64-clang/chromedriver_win64.zip'
  elif platform == 'mac':
    channel = 'canary'
    release_os = 'mac_arm64'
    driver_pathname = 'mac-arm64/chromedriver_mac64.zip'
  else:
    assert False, f'Not supported platform {platform}'
  driver_zip_path = os.path.basename(driver_pathname)[:-4]

  if not version:
    version = find_version(release_os, channel)
  downloaded_dir = _download_files_from_gcs(str(version), [driver_pathname])

  hosted_platform = helper.get_hosted_platform()
  if hosted_platform == 'win':
    chromedriver_bin = 'chromedriver.exe'
  else:
    chromedriver_bin = 'chromedriver'
  chromedriver_path = os.path.join(downloaded_dir, chromedriver_bin)
  shutil.move(
    os.path.join(downloaded_dir, driver_zip_path, chromedriver_bin),
    chromedriver_path)
  return chromedriver_path


def download_chrome_mac(version: str) -> str:
  files = ['mac-universal/chrome-mac.zip']
  downloaded_dir = _download_files_from_gcs(version, files)
  return os.path.join(downloaded_dir, "chrome-mac")

def download_chrome_win(version: str) -> str:
  files = ['win64-clang/chrome-win64-clang.zip']
  downloaded_dir = _download_files_from_gcs(version, files)
  return os.path.join(downloaded_dir, "chrome-win64-clang")


def download_chrome_linux(version: str) -> str:
  files = ["linux64/chrome-linux64.zip"]
  downloaded_dir = _download_files_from_gcs(version, files)
  return os.path.join(downloaded_dir, "chrome-linux64")

def find_version(release_os: str, channel: str) -> packaging.version.Version:
  # See go/versionhistory-user-guide
  url = (
      f"https://versionhistory.googleapis.com/v1/chrome/"
      # Limit to just the platform and channel that we care about
      f"platforms/{release_os}/channels/{channel}/versions/all/releases"
      # There might be experiments where different versions are shipping to the
      # same channel, so order the results descending by the fraction so that
      # the first returned release is the standard release for the channel
      f"?order_by=fraction%20desc")

  try:
    response = json.loads(urlopen(url=url).read())
    return packaging.version.parse(response['releases'][0]['version'])
  except Exception as e:
    raise RuntimeError("Fail to retrieve version info.") from e

def parse_version(version: str) -> packaging.version.Version:
  try:
    return packaging.version.parse(version)
  except packaging.version.InvalidVersion:
    raise RuntimeError(f"Invalid version: {version}")

def find_closest_version(release_os: str,
                         channel: str,
                         version: str) -> packaging.version.Version:
  # See find_version
  # Search for the newest major version,
  # https://crsrc.org/c/chrome/test/chromedriver/chrome_launcher.cc;l=287;drc=ea1bdac
  major = int(version.split('.')[0])
  url = (
      f"https://versionhistory.googleapis.com/v1/chrome/"
      f"platforms/{release_os}/channels/{channel}/versions/all/releases"
      # Find the newest version that's earlier than the next major,
      # so effectively, it finds the latest of the current major.
      f"?filter=version<{major+1}"
      # Order by version descending to find the earliest/closest version.
      f"&order_by=version%20desc")

  try:
    response = json.loads(urlopen(url=url).read())
    return packaging.version.parse(response['releases'][0]['version'])
  except Exception as e:
    raise RuntimeError("Fail to retrieve version info.") from e