blob: bd67b50c372c4020582746e92467157302c8c2ad [file] [log] [blame]
Ray Milkeyf80bbb22016-03-11 10:16:22 -08001#!/usr/bin/env python
2# Copyright (C) 2013 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16from __future__ import print_function
17
18from hashlib import sha1
19from optparse import OptionParser
20from os import link, makedirs, path, remove
21import shutil
22from subprocess import check_call, CalledProcessError
23from sys import stderr
24from util import hash_file, resolve_url
25from zipfile import ZipFile, BadZipfile, LargeZipFile
26
27GERRIT_HOME = path.expanduser('~/.gerritcodereview')
28CACHE_DIR = path.join(GERRIT_HOME, 'buck-cache', 'downloaded-artifacts')
29# LEGACY_CACHE_DIR is only used to allow existing workspaces to move already
30# downloaded files to the new cache directory.
31# Please remove after 3 months (2015-10-07).
32LEGACY_CACHE_DIR = path.join(GERRIT_HOME, 'buck-cache')
33LOCAL_PROPERTIES = 'local.properties'
34
35
36def safe_mkdirs(d):
37 if path.isdir(d):
38 return
39 try:
40 makedirs(d)
41 except OSError as err:
42 if not path.isdir(d):
43 raise err
44
45
46def download_properties(root_dir):
47 """ Get the download properties.
48
49 First tries to find the properties file in the given root directory,
50 and if not found there, tries in the Gerrit settings folder in the
51 user's home directory.
52
53 Returns a set of download properties, which may be empty.
54
55 """
56 p = {}
57 local_prop = path.join(root_dir, LOCAL_PROPERTIES)
58 if not path.isfile(local_prop):
59 local_prop = path.join(GERRIT_HOME, LOCAL_PROPERTIES)
60 if path.isfile(local_prop):
61 try:
62 with open(local_prop) as fd:
63 for line in fd:
64 if line.startswith('download.'):
65 d = [e.strip() for e in line.split('=', 1)]
66 name, url = d[0], d[1]
67 p[name[len('download.'):]] = url
68 except OSError:
69 pass
70 return p
71
72
73def cache_entry(args):
74 if args.v:
75 h = args.v
76 else:
77 h = sha1(args.u.encode('utf-8')).hexdigest()
78 name = '%s-%s' % (path.basename(args.o), h)
79 return path.join(CACHE_DIR, name)
80
81# Please remove after 3 months (2015-10-07). See LEGACY_CACHE_DIR above.
82def legacy_cache_entry(args):
83 if args.v:
84 h = args.v
85 else:
86 h = sha1(args.u.encode('utf-8')).hexdigest()
87 name = '%s-%s' % (path.basename(args.o), h)
88 return path.join(LEGACY_CACHE_DIR, name)
89
90
91opts = OptionParser()
92opts.add_option('-o', help='local output file')
93opts.add_option('-u', help='URL to download')
94opts.add_option('-v', help='expected content SHA-1')
95opts.add_option('-x', action='append', help='file to delete from ZIP')
96opts.add_option('--exclude_java_sources', action='store_true')
97opts.add_option('--unsign', action='store_true')
98args, _ = opts.parse_args()
99
100root_dir = args.o
101while root_dir:
102 root_dir, n = path.split(root_dir)
103 if n == 'buck-out':
104 break
105
106redirects = download_properties(root_dir)
107cache_ent = cache_entry(args)
108legacy_cache_ent = legacy_cache_entry(args)
109src_url = resolve_url(args.u, redirects)
110
111# Please remove after 3 months (2015-10-07). See LEGACY_CACHE_DIR above.
112if not path.exists(cache_ent) and path.exists(legacy_cache_ent):
113 try:
114 safe_mkdirs(path.dirname(cache_ent))
115 except OSError as err:
116 print('error creating directory %s: %s' %
117 (path.dirname(cache_ent), err), file=stderr)
118 exit(1)
119 shutil.move(legacy_cache_ent, cache_ent)
120
121if not path.exists(cache_ent):
122 try:
123 safe_mkdirs(path.dirname(cache_ent))
124 except OSError as err:
125 print('error creating directory %s: %s' %
126 (path.dirname(cache_ent), err), file=stderr)
127 exit(1)
128
129 print('Download %s' % src_url, file=stderr)
130 try:
131 check_call(['curl', '--proxy-anyauth', '-ksfo', cache_ent, src_url])
132 except OSError as err:
133 print('could not invoke curl: %s\nis curl installed?' % err, file=stderr)
134 exit(1)
135 except CalledProcessError as err:
136 print('error using curl: %s' % err, file=stderr)
137 exit(1)
138
139if args.v:
140 have = hash_file(sha1(), cache_ent).hexdigest()
141 if args.v != have:
142 print((
143 '%s:\n' +
144 'expected %s\n' +
145 'received %s\n') % (src_url, args.v, have), file=stderr)
146 try:
147 remove(cache_ent)
148 except OSError as err:
149 if path.exists(cache_ent):
150 print('error removing %s: %s' % (cache_ent, err), file=stderr)
151 exit(1)
152
153exclude = []
154if args.x:
155 exclude += args.x
156if args.exclude_java_sources:
157 try:
158 with ZipFile(cache_ent, 'r') as zf:
159 for n in zf.namelist():
160 if n.endswith('.java'):
161 exclude.append(n)
162 except (BadZipfile, LargeZipFile) as err:
163 print('error opening %s: %s' % (cache_ent, err), file=stderr)
164 exit(1)
165
166if args.unsign:
167 try:
168 with ZipFile(cache_ent, 'r') as zf:
169 for n in zf.namelist():
170 if (n.endswith('.RSA')
171 or n.endswith('.SF')
172 or n.endswith('.LIST')):
173 exclude.append(n)
174 except (BadZipfile, LargeZipFile) as err:
175 print('error opening %s: %s' % (cache_ent, err), file=stderr)
176 exit(1)
177
178safe_mkdirs(path.dirname(args.o))
179if exclude:
180 try:
181 shutil.copyfile(cache_ent, args.o)
182 except (shutil.Error, IOError) as err:
183 print('error copying to %s: %s' % (args.o, err), file=stderr)
184 exit(1)
185 try:
186 check_call(['zip', '-d', args.o] + exclude)
187 except CalledProcessError as err:
188 print('error removing files from zip: %s' % err, file=stderr)
189 exit(1)
190else:
191 try:
192 link(cache_ent, args.o)
193 except OSError as err:
194 try:
195 shutil.copyfile(cache_ent, args.o)
196 except (shutil.Error, IOError) as err:
197 print('error copying to %s: %s' % (args.o, err), file=stderr)
198 exit(1)