blob: 29398e60829783b369a544a44302e85efa9be231 [file] [log] [blame]
Björn Pedersenc357ceb2015-05-27 10:17:36 +02001#!/usr/bin/env python
Shawn Pearcefd6bb9f2013-05-08 14:14:24 -07002# 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
Chirayu Desai4c5ee482013-05-13 13:48:43 +053016from __future__ import print_function
17
Shawn Pearcefd6bb9f2013-05-08 14:14:24 -070018from hashlib import sha1
19from optparse import OptionParser
Shawn Pearcef98b3792013-08-06 12:20:30 -070020from os import link, makedirs, path, remove
Shawn Pearcefd6bb9f2013-05-08 14:14:24 -070021import shutil
David Pursehouse28798772013-05-09 14:58:20 +010022from subprocess import check_call, CalledProcessError
Shawn Pearcefd6bb9f2013-05-08 14:14:24 -070023from sys import stderr
Dave Borowitz868f7642015-11-18 15:25:24 -050024from util import hash_file, resolve_url
Shawn Pearcefd6bb9f2013-05-08 14:14:24 -070025from zipfile import ZipFile, BadZipfile, LargeZipFile
26
David Pursehouse45286f12013-05-17 13:46:40 +090027GERRIT_HOME = path.expanduser('~/.gerritcodereview')
Dave Borowitz9c359792017-11-14 08:40:55 -050028CACHE_DIR = path.join(GERRIT_HOME, 'bazel-cache', 'downloaded-artifacts')
David Pursehouse6fb2c4d2013-05-16 16:51:46 +090029LOCAL_PROPERTIES = 'local.properties'
30
David Pursehouse15a9f532014-06-23 10:55:20 +090031
Shawn Pearcefd6bb9f2013-05-08 14:14:24 -070032def safe_mkdirs(d):
Chad Horohoedd224702018-05-16 22:33:06 -040033 if path.isdir(d):
34 return
35 try:
36 makedirs(d)
37 except OSError as err:
38 if not path.isdir(d):
39 raise err
Shawn Pearcefd6bb9f2013-05-08 14:14:24 -070040
David Pursehouse15a9f532014-06-23 10:55:20 +090041
Shawn Pearce1b89f852013-05-13 20:26:34 -070042def download_properties(root_dir):
Chad Horohoedd224702018-05-16 22:33:06 -040043 """ Get the download properties.
David Pursehouse6fb2c4d2013-05-16 16:51:46 +090044
Chad Horohoedd224702018-05-16 22:33:06 -040045 First tries to find the properties file in the given root directory,
46 and if not found there, tries in the Gerrit settings folder in the
47 user's home directory.
David Pursehouse6fb2c4d2013-05-16 16:51:46 +090048
Chad Horohoedd224702018-05-16 22:33:06 -040049 Returns a set of download properties, which may be empty.
David Pursehouse6fb2c4d2013-05-16 16:51:46 +090050
Chad Horohoedd224702018-05-16 22:33:06 -040051 """
52 p = {}
53 local_prop = path.join(root_dir, LOCAL_PROPERTIES)
54 if not path.isfile(local_prop):
55 local_prop = path.join(GERRIT_HOME, LOCAL_PROPERTIES)
56 if path.isfile(local_prop):
57 try:
58 with open(local_prop) as fd:
59 for line in fd:
60 if line.startswith('download.'):
61 d = [e.strip() for e in line.split('=', 1)]
62 name, url = d[0], d[1]
63 p[name[len('download.'):]] = url
64 except OSError:
65 pass
66 return p
Shawn Pearce1b89f852013-05-13 20:26:34 -070067
David Pursehouse15a9f532014-06-23 10:55:20 +090068
David Pursehouse45286f12013-05-17 13:46:40 +090069def cache_entry(args):
Chad Horohoedd224702018-05-16 22:33:06 -040070 if args.v:
71 h = args.v
72 else:
73 h = sha1(args.u.encode('utf-8')).hexdigest()
74 name = '%s-%s' % (path.basename(args.o), h)
75 return path.join(CACHE_DIR, name)
76
Shawn Pearce1b89f852013-05-13 20:26:34 -070077
Shawn Pearcefd6bb9f2013-05-08 14:14:24 -070078opts = OptionParser()
79opts.add_option('-o', help='local output file')
80opts.add_option('-u', help='URL to download')
81opts.add_option('-v', help='expected content SHA-1')
82opts.add_option('-x', action='append', help='file to delete from ZIP')
83opts.add_option('--exclude_java_sources', action='store_true')
David Ostrovsky6e0a3e52013-10-26 09:55:15 +020084opts.add_option('--unsign', action='store_true')
Shawn Pearcefd6bb9f2013-05-08 14:14:24 -070085args, _ = opts.parse_args()
86
87root_dir = args.o
David Ostrovskye89717d2017-08-30 09:03:20 +020088while root_dir and path.dirname(root_dir) != root_dir:
Chad Horohoedd224702018-05-16 22:33:06 -040089 root_dir, n = path.split(root_dir)
90 if n == 'WORKSPACE':
91 break
Shawn Pearcefd6bb9f2013-05-08 14:14:24 -070092
Shawn Pearce1b89f852013-05-13 20:26:34 -070093redirects = download_properties(root_dir)
David Pursehouse45286f12013-05-17 13:46:40 +090094cache_ent = cache_entry(args)
Shawn Pearce1b89f852013-05-13 20:26:34 -070095src_url = resolve_url(args.u, redirects)
Shawn Pearcefd6bb9f2013-05-08 14:14:24 -070096
97if not path.exists(cache_ent):
Chad Horohoedd224702018-05-16 22:33:06 -040098 try:
99 safe_mkdirs(path.dirname(cache_ent))
100 except OSError as err:
101 print('error creating directory %s: %s' %
102 (path.dirname(cache_ent), err), file=stderr)
103 exit(1)
Sasa Zivkov4709fff2013-06-13 17:01:25 +0200104
Chad Horohoedd224702018-05-16 22:33:06 -0400105 print('Download %s' % src_url, file=stderr)
106 try:
107 check_call(['curl', '--proxy-anyauth', '-ksSfLo', cache_ent, src_url])
108 except OSError as err:
Chad Horohoe69142322018-05-17 10:19:22 -0700109 print('could not invoke curl: %s\nis curl installed?' % err,
110 file=stderr)
Chad Horohoedd224702018-05-16 22:33:06 -0400111 exit(1)
112 except CalledProcessError as err:
113 print('error using curl: %s' % err, file=stderr)
114 exit(1)
Shawn Pearcefd6bb9f2013-05-08 14:14:24 -0700115
116if args.v:
Chad Horohoedd224702018-05-16 22:33:06 -0400117 have = hash_file(sha1(), cache_ent).hexdigest()
118 if args.v != have:
119 print((
120 '%s:\n' +
121 'expected %s\n' +
122 'received %s\n') % (src_url, args.v, have), file=stderr)
123 try:
124 remove(cache_ent)
125 except OSError as err:
126 if path.exists(cache_ent):
127 print('error removing %s: %s' % (cache_ent, err), file=stderr)
128 exit(1)
Shawn Pearcefd6bb9f2013-05-08 14:14:24 -0700129
130exclude = []
131if args.x:
Chad Horohoedd224702018-05-16 22:33:06 -0400132 exclude += args.x
Shawn Pearcefd6bb9f2013-05-08 14:14:24 -0700133if args.exclude_java_sources:
Chad Horohoedd224702018-05-16 22:33:06 -0400134 try:
135 with ZipFile(cache_ent, 'r') as zf:
136 for n in zf.namelist():
137 if n.endswith('.java'):
138 exclude.append(n)
139 except (BadZipfile, LargeZipFile) as err:
140 print('error opening %s: %s' % (cache_ent, err), file=stderr)
141 exit(1)
David Ostrovsky6e0a3e52013-10-26 09:55:15 +0200142
143if args.unsign:
Chad Horohoedd224702018-05-16 22:33:06 -0400144 try:
145 with ZipFile(cache_ent, 'r') as zf:
146 for n in zf.namelist():
147 if (n.endswith('.RSA')
148 or n.endswith('.SF')
149 or n.endswith('.LIST')):
150 exclude.append(n)
151 except (BadZipfile, LargeZipFile) as err:
152 print('error opening %s: %s' % (cache_ent, err), file=stderr)
153 exit(1)
Shawn Pearcefd6bb9f2013-05-08 14:14:24 -0700154
155safe_mkdirs(path.dirname(args.o))
156if exclude:
David Pursehousee277fc32013-08-08 10:44:23 +0900157 try:
Chad Horohoedd224702018-05-16 22:33:06 -0400158 shutil.copyfile(cache_ent, args.o)
David Pursehousee277fc32013-08-08 10:44:23 +0900159 except (shutil.Error, IOError) as err:
Chad Horohoedd224702018-05-16 22:33:06 -0400160 print('error copying to %s: %s' % (args.o, err), file=stderr)
161 exit(1)
162 try:
163 check_call(['zip', '-d', args.o] + exclude)
164 except CalledProcessError as err:
165 print('error removing files from zip: %s' % err, file=stderr)
166 exit(1)
167else:
168 try:
169 link(cache_ent, args.o)
170 except OSError as err:
171 try:
172 shutil.copyfile(cache_ent, args.o)
173 except (shutil.Error, IOError) as err:
174 print('error copying to %s: %s' % (args.o, err), file=stderr)
175 exit(1)