1# Copyright 2023 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 5import argparse 6import hashlib 7import json 8import os 9import pathlib 10import re 11import sys 12from typing import Dict, List 13import urllib.request 14 15 16def _fetch_json(url): 17 return json.load(urllib.request.urlopen(url)) 18 19 20def _find_valid_urls(release, artifact_regex): 21 urls = [x['browser_download_url'] for x in release['assets']] 22 if artifact_regex: 23 urls = [x for x in urls if re.search(artifact_regex, x)] 24 return urls 25 26 27def _latest(api_url, install_scripts=None, artifact_regex=None): 28 # Make the version change every time this file changes. 29 md5 = hashlib.md5() 30 md5.update(pathlib.Path(__file__).read_bytes()) 31 import __main__ 32 md5.update(pathlib.Path(__main__.__file__).read_bytes()) 33 34 if install_scripts: 35 for path in install_scripts: 36 md5.update(pathlib.Path(path).read_bytes()) 37 file_hash = md5.hexdigest()[:10] 38 39 releases: List[Dict] = _fetch_json(f'{api_url}/releases') 40 for release in releases: 41 tag_name = release['tag_name'] 42 urls = _find_valid_urls(release, artifact_regex) 43 if len(urls) == 1: 44 print('{}.{}'.format(tag_name, file_hash)) 45 return 46 print(f'Bad urls={urls} for tag_name={tag_name}, skipping.', 47 file=sys.stderr) 48 49 50def _get_url(api_url, 51 artifact_filename=None, 52 artifact_extension=None, 53 artifact_regex=None): 54 # Split off our md5 hash. 55 version = os.environ['_3PP_VERSION'].rsplit('.', 1)[0] 56 json_dict = _fetch_json(f'{api_url}/releases/tags/{version}') 57 urls = _find_valid_urls(json_dict, artifact_regex) 58 59 if len(urls) != 1: 60 raise Exception('len(urls) != 1, urls: \n' + '\n'.join(urls)) 61 62 partial_manifest = { 63 'url': urls, 64 'ext': artifact_extension or '', 65 } 66 if artifact_filename: 67 partial_manifest['name'] = [artifact_filename] 68 69 print(json.dumps(partial_manifest)) 70 71 72def main(*, 73 project, 74 artifact_filename=None, 75 artifact_extension=None, 76 artifact_regex=None, 77 install_scripts=None): 78 """The fetch.py script for a 3pp module. 79 80 Args: 81 project: GitHub username for the repo. e.g. "google/protobuf". 82 artifact_filename: The name for the downloaded file. Required when not 83 setting "unpack_archive: true" in 3pp.pb. 84 artifact_extension: File extension of file being downloaded. Required when 85 setting "unpack_archive: true" in 3pp.pb. 86 artifact_regex: A regex to use to identify the desired artifact from the 87 list of artifacts on the release. 88 install_scripts: List of script to add to the md5 of the version. The main 89 module and this module are always included. 90 """ 91 parser = argparse.ArgumentParser() 92 parser.add_argument('action', choices=('latest', 'get_url')) 93 args = parser.parse_args() 94 95 api_url = f'https://api.github.com/repos/{project}' 96 if args.action == 'latest': 97 _latest(api_url, 98 install_scripts=install_scripts, 99 artifact_regex=artifact_regex) 100 else: 101 _get_url(api_url, 102 artifact_filename=artifact_filename, 103 artifact_extension=artifact_extension, 104 artifact_regex=artifact_regex) 105