| #!/usr/bin/env python |
| # Copyright (C) 2015 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| from __future__ import print_function |
| |
| import argparse |
| import hashlib |
| import json |
| import os |
| import shutil |
| import subprocess |
| import sys |
| |
| import bowerutil |
| |
| CACHE_DIR = os.environ.get( |
| 'GERRIT_CACHE_HOME', |
| os.path.expanduser(os.path.join( |
| '~', '.gerritcodereview', 'bazel-cache', 'downloaded-artifacts' |
| )) |
| ) |
| |
| |
| def bower_cmd(bower, *args): |
| cmd = bower.split(' ') |
| cmd.extend(args) |
| return cmd |
| |
| |
| def bower_info(bower, name, package, version): |
| cmd = bower_cmd(bower, '-l=error', '-j', |
| 'info', '%s#%s' % (package, version)) |
| try: |
| p = subprocess.Popen(cmd, stdout=subprocess.PIPE, |
| stderr=subprocess.PIPE) |
| except: |
| sys.stderr.write("error executing: %s\n" % ' '.join(cmd)) |
| raise |
| out, err = p.communicate() |
| if p.returncode: |
| # For python3 support we wrap str around err. |
| sys.stderr.write(str(err)) |
| raise OSError('Command failed: %s' % ' '.join(cmd)) |
| |
| try: |
| info = json.loads(out) |
| except ValueError: |
| raise ValueError('invalid JSON from %s:\n%s' % (" ".join(cmd), out)) |
| info_name = info.get('name') |
| if info_name != name: |
| raise ValueError( |
| 'expected package name %s, got: %s' % (name, info_name)) |
| return info |
| |
| |
| def ignore_deps(info): |
| # Tell bower to ignore dependencies so we just download this component. |
| # This is just an optimization, since we only pick out the component we |
| # need, but it's important when downloading sizable dependency trees. |
| # |
| # As of 1.6.5 I don't think ignoredDependencies can be specified on the |
| # command line with --config, so we have to create .bowerrc. |
| deps = info.get('dependencies') |
| if deps: |
| with open(os.path.join('.bowerrc'), 'w') as f: |
| json.dump({'ignoredDependencies': list(deps.keys())}, f) |
| |
| |
| def cache_entry(name, package, version, sha1): |
| if not sha1: |
| sha1 = hashlib.sha1('%s#%s' % (package, version)).hexdigest() |
| return os.path.join(CACHE_DIR, '%s-%s.zip-%s' % (name, version, sha1)) |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser() |
| parser.add_argument('-n', help='short name of component') |
| parser.add_argument('-b', help='bower command') |
| parser.add_argument('-p', help='full package name of component') |
| parser.add_argument('-v', help='version number') |
| parser.add_argument('-s', help='expected content sha1') |
| parser.add_argument('-o', help='output file location') |
| args = parser.parse_args() |
| |
| assert args.p |
| assert args.v |
| assert args.n |
| |
| cwd = os.getcwd() |
| outzip = os.path.join(cwd, args.o) |
| cached = cache_entry(args.n, args.p, args.v, args.s) |
| |
| if not os.path.exists(cached): |
| info = bower_info(args.b, args.n, args.p, args.v) |
| ignore_deps(info) |
| subprocess.check_call( |
| bower_cmd( |
| args.b, '--quiet', 'install', '%s#%s' % (args.p, args.v))) |
| bc = os.path.join(cwd, 'bower_components') |
| subprocess.check_call( |
| ['zip', '-q', '--exclude', '.bower.json', '-r', cached, args.n], |
| cwd=bc) |
| |
| if args.s: |
| path = os.path.join(bc, args.n) |
| sha1 = bowerutil.hash_bower_component( |
| hashlib.sha1(), path).hexdigest() |
| if args.s != sha1: |
| print(( |
| '%s#%s:\n' |
| 'expected %s\n' |
| 'received %s\n') % (args.p, args.v, args.s, sha1), |
| file=sys.stderr) |
| try: |
| os.remove(cached) |
| except OSError as err: |
| if path.exists(cached): |
| print('error removing %s: %s' % (cached, err), |
| file=sys.stderr) |
| return 1 |
| |
| shutil.copyfile(cached, outzip) |
| return 0 |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |