forked from WebKit/WebKit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdownload-github-release.py
162 lines (123 loc) · 5.97 KB
/
download-github-release.py
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
#! /usr/bin/env python
#
# Copyright (C) 2017 Sony Interactive Entertainment Inc.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import argparse
import json
import os
import sys
try:
from urllib.request import urlopen, Request
from urllib.error import URLError
except ImportError:
from urllib2 import urlopen, Request, URLError
PUBLIC_GITHUB_API_ENDPOINT = 'https://api.github.com/'
DESCRIPTION = '''Downloads a release binary from a GitHub repository.
(Requests the latest release unless a specific tag is provided.)
Intended for download of vswhere.exe and WinCairoRequirements.zip,
but may be used for arbitrary binaries / repositories.
Checks whether the desired version already exists in the output directory
(by looking for a .version file saved alongside the release binary) --
if so, download is skipped; otherwise any existing version is overwritten.
'''
class Status:
DOWNLOADED = 0
UP_TO_DATE = 1
COULD_NOT_FIND = 2
USING_EXISTING = 3
def parse_args(argv):
parser = argparse.ArgumentParser(description=DESCRIPTION, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('repo', help='GitHub repository slug (e.g., "org/repo")')
parser.add_argument('filename', help='filename of release binary to download (e.g., "foo.exe", "bar.zip")')
parser.add_argument('-o', '--output-dir', default='.', help='output directory (defaults to working directory)')
parser.add_argument('-e', '--endpoint', default=PUBLIC_GITHUB_API_ENDPOINT, help='GitHub API endpoint (defaults to api.github.com)')
parser.add_argument('-t', '--token', default=None, help='GitHub API OAuth token (for private repos/endpoints)')
parser.add_argument('-r', '--release-tag', default=None, help='release tag to download (defaults to latest)')
return parser.parse_args(argv)
def find_release(endpoint, repo, filename, token, tag):
release_name = 'tags/{}'.format(tag) if tag else 'latest'
url = '{}/repos/{}/releases/{}'.format(endpoint.rstrip('/'), repo, release_name)
request = Request(url)
request.add_header('Accept', 'application/vnd.github.v3+json')
if token:
request.add_header('Authorization', 'token {}'.format(token))
try:
response = urlopen(request)
except URLError as error:
print(error)
return None, None
data = json.loads(response.read())
for asset in data['assets']:
if asset['name'] == filename:
version_info = {'tag_name': data['tag_name'], 'updated_at': asset['updated_at']}
return asset['url'], version_info
return None, None
def download_release(source_url, target_path, token):
request = Request(source_url)
request.add_header('Accept', 'application/octet-stream')
if token:
request.add_header('Authorization', 'token {}'.format(token))
with open(target_path, 'wb') as file:
file.write(urlopen(request).read())
def load_version_info(version_info_path):
if not os.path.exists(version_info_path):
return None
with open(version_info_path) as file:
return json.load(file)
def save_version_info(version_info_path, version_info):
with open(version_info_path, 'w') as file:
json.dump(version_info, file)
def main(argv):
args = parse_args(argv)
binary_path = os.path.join(args.output_dir, args.filename)
version_info_path = binary_path + '.version'
print('Updating {}...'.format(args.filename))
existing_version_info = load_version_info(version_info_path)
if existing_version_info:
print('Found existing release: {}'.format(existing_version_info['tag_name']))
else:
print('No existing release found.')
release_title = 'release "{}"'.format(args.release_tag) if args.release_tag else 'latest release'
print('Seeking {} from {}...'.format(release_title, args.repo))
release_url, target_version_info = find_release(args.endpoint, args.repo, args.filename, args.token, args.release_tag)
if not target_version_info:
if existing_version_info:
print('Falling back to existing release!')
return Status.USING_EXISTING
print('No release found!')
return Status.COULD_NOT_FIND
print('Found release to download: {}'.format(target_version_info['tag_name']))
if target_version_info == existing_version_info:
print('Already up-to-date!')
return Status.UP_TO_DATE
if not os.path.exists(args.output_dir):
os.makedirs(args.output_dir)
print('Downloading to {}...'.format(os.path.abspath(args.output_dir)))
download_release(release_url, binary_path, args.token)
save_version_info(version_info_path, target_version_info)
print('Done!')
return Status.DOWNLOADED
if __name__ == '__main__':
result = main(sys.argv[1:])
if result == Status.COULD_NOT_FIND:
sys.exit(1)