blob: 7174561963fe77c4d66a9a0a5331008f8dc92bcd [file] [edit]
#!/usr/bin/env python3
# Copyright 2020 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
This script is used to download a file from the Chromium repository.
It's equivalent to using curl to download the file, and is intended to be ran
as a gclient hook.
"""
import argparse
import base64
import os
import random
import sys
import time
import urllib.error
import urllib.request
GITILES_URL_TEMPLATE = 'https://chromium.googlesource.com/chromium/src/+/{}/{}?format=TEXT'
# Status codes that may indicate temporary server unavailability or rate
# limiting.
RETRY_STATUS_CODES = {429, 500, 502, 503, 504}
MAX_ATTEMPTS = 5
INITIAL_BACKOFF_SECONDS = 2.0
TIMEOUT_SECONDS = 30.0
def download_file(revision: str,
path: str,
output_path: str,
max_attempts: int = MAX_ATTEMPTS,
initial_backoff: float = INITIAL_BACKOFF_SECONDS) -> bool:
url = GITILES_URL_TEMPLATE.format(revision or 'main', path)
print(f' -> Downloading from "{url}" to "{output_path}"...')
req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
for attempt in range(max_attempts):
try:
with urllib.request.urlopen(req, timeout=TIMEOUT_SECONDS) as resp:
content = base64.b64decode(resp.read())
dir_path = os.path.dirname(os.path.abspath(output_path))
os.makedirs(dir_path, exist_ok=True)
with open(output_path, 'wb') as f:
f.write(content)
return True
except urllib.error.HTTPError as e:
if e.code in RETRY_STATUS_CODES and attempt < max_attempts - 1:
backoff = initial_backoff * (2**attempt) + random.uniform(0, 1)
print(
f'WARNING: Failed to download {url} (HTTP {e.code}). '
f'Retrying in {backoff:.1f}s '
f'(attempt {attempt + 1}/{max_attempts})...',
file=sys.stderr)
time.sleep(backoff)
continue
print(f'ERROR: Failed to download {url}: {e}', file=sys.stderr)
return False
except (urllib.error.URLError, TimeoutError) as e:
if attempt < max_attempts - 1:
backoff = initial_backoff * (2**attempt) + random.uniform(0, 1)
reason = getattr(e, 'reason', str(e))
print(
f'WARNING: Failed to download {url} ({reason}). '
f'Retrying in {backoff:.1f}s '
f'(attempt {attempt + 1}/{max_attempts})...',
file=sys.stderr)
time.sleep(backoff)
continue
print(f'ERROR: Failed to download {url}: {e}', file=sys.stderr)
return False
return False
def main():
parser = argparse.ArgumentParser(
description='Download a file from the Chromium repository')
parser.add_argument('--output',
required=True,
help='path to file to create/overwrite')
parser.add_argument('--revision',
required=True,
help='revision to download')
parser.add_argument('--path',
required=True,
help='path within the Chromium repository')
args = parser.parse_args()
return 0 if download_file(args.revision, args.path, args.output) else 1
if __name__ == '__main__':
sys.exit(main())