xref: /aosp_15_r20/external/cronet/build/3pp_common/fetch_github_release.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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