116 lines
4.2 KiB
Python
116 lines
4.2 KiB
Python
|
|
# 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 pkg_resources import packaging
|
||
|
|
from typing import List
|
||
|
|
from urllib.request import urlopen
|
||
|
|
from . import helper
|
||
|
|
|
||
|
|
SRC_DIR = os.path.abspath(
|
||
|
|
os.path.join(os.path.dirname(__file__), *([os.pardir] * 4)))
|
||
|
|
|
||
|
|
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_chrome(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_chrome_mac(version: str) -> str:
|
||
|
|
files = ["mac-universal/chrome-mac.zip", "mac-arm64/chromedriver_mac64.zip"]
|
||
|
|
downloaded_dir = _download_chrome(version, files)
|
||
|
|
shutil.move(
|
||
|
|
os.path.join(downloaded_dir, "chromedriver_mac64", "chromedriver"),
|
||
|
|
os.path.join(downloaded_dir, "chrome-mac", "chromedriver"))
|
||
|
|
|
||
|
|
return os.path.join(downloaded_dir, "chrome-mac")
|
||
|
|
|
||
|
|
def download_chrome_win(version: str) -> str:
|
||
|
|
files = ["win64-clang/chrome-win64-clang.zip",
|
||
|
|
"win64-clang/chromedriver_win64.zip"]
|
||
|
|
downloaded_dir = _download_chrome(version, files)
|
||
|
|
shutil.move(
|
||
|
|
os.path.join(downloaded_dir, "chromedriver_win64", "chromedriver.exe"),
|
||
|
|
os.path.join(downloaded_dir, "chrome-win64-clang", "chromedriver.exe"))
|
||
|
|
|
||
|
|
return os.path.join(downloaded_dir, "chrome-win64-clang")
|
||
|
|
|
||
|
|
|
||
|
|
def download_chrome_linux(version: str) -> str:
|
||
|
|
files = ["linux64/chrome-linux64.zip", "linux64/chromedriver_linux64.zip"]
|
||
|
|
downloaded_dir = _download_chrome(version, files)
|
||
|
|
|
||
|
|
shutil.move(
|
||
|
|
os.path.join(downloaded_dir, "chromedriver_linux64", "chromedriver"),
|
||
|
|
os.path.join(downloaded_dir, "chrome-linux64", "chromedriver"))
|
||
|
|
|
||
|
|
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
|