OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2016 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. |
| 5 |
| 6 import os |
| 7 import shutil |
| 8 import sys |
| 9 import subprocess |
| 10 import tarfile |
| 11 import tempfile |
| 12 import urllib2 |
| 13 |
| 14 THIS_DIR = os.path.dirname(os.path.abspath(__file__)) |
| 15 |
| 16 DEFAULT_VERSION = '0.12.2' |
| 17 BUCKET = 'chromium-infra-bins' |
| 18 |
| 19 |
| 20 def install_latest_node_js(version, tmp_dir): |
| 21 target_dir = os.path.join(THIS_DIR, 'runtimes', version) |
| 22 version_file = os.path.join(target_dir, 'VERSION') |
| 23 |
| 24 if sys.platform == 'win32': |
| 25 bin_location = os.path.join(target_dir, 'node.exe') |
| 26 else: |
| 27 bin_location = os.path.join(target_dir, 'bin', 'node') |
| 28 |
| 29 # We assume that, if the VERSION file exists, then the installation is good. |
| 30 if os.path.exists(version_file): |
| 31 with open(version_file, 'r') as f: |
| 32 if f.read() == version: |
| 33 return bin_location |
| 34 |
| 35 # TODO(hinoka): This probably doesn't work that well on Windows... |
| 36 shutil.rmtree(target_dir, ignore_errors=True) |
| 37 |
| 38 # Get the target name correct. |
| 39 if sys.platform == 'win32': |
| 40 target = 'node.exe' |
| 41 elif sys.platform == 'darwin': |
| 42 target = 'node-v%s-darwin-x86.tar.gz' % version |
| 43 elif sys.platform == 'linux2': |
| 44 target = 'node-v%s-linux-x86.tar.gz' % version |
| 45 else: |
| 46 raise Exception('Unrecognized platform %s' % sys.platform) |
| 47 |
| 48 dest = os.path.join(tmp_dir, 'node_download') |
| 49 url = 'https://storage.googleapis.com/%s/node/%s/%s' % ( |
| 50 BUCKET, version, target) |
| 51 print('Fetching %s' % url) |
| 52 u = urllib2.urlopen(url) |
| 53 with open(dest, 'wb') as f: |
| 54 while True: |
| 55 chunk = u.read(2 ** 20) |
| 56 if not chunk: |
| 57 break |
| 58 f.write(chunk) |
| 59 |
| 60 # When multiple node.py instances run at the same time for the first time, |
| 61 # the check to see whether or not the installation occured already. But the
n |
| 62 # they all race to see who's the first to run shutil.move(), which obviously |
| 63 # fails for everyone other than the first instance. This CL makes |
| 64 # os.rename() not fail, since its assumed that if it fails that means |
| 65 # someone else already created an installation. |
| 66 # |
| 67 # Another approach is to use an flock, but then it starts to get messy when |
| 68 # you have to keep polling filesystem state to see if another instance |
| 69 # finished, or add timeouts to remove an flock if it was left on the system
by |
| 70 # a failed attempt, etc, etc. This just seemed like a less flaky solution, |
| 71 # despite the fact that it means multiple network requests are spawned. |
| 72 write_version = True |
| 73 if sys.platform != 'win32': |
| 74 # The Windows version comes as a self contained executable, the other |
| 75 # versions come as a tar.gz that needs to be extracted. |
| 76 with tarfile.open(dest, 'r:gz') as f: |
| 77 f.extractall(path=tmp_dir) |
| 78 try: |
| 79 os.mkdir(os.path.join(THIS_DIR, 'runtimes')) |
| 80 os.rename(os.path.join(tmp_dir, target[:-len('.tar.gz')]), |
| 81 target_dir) |
| 82 except OSError: |
| 83 write_version = False |
| 84 os.remove(dest) |
| 85 else: |
| 86 try: |
| 87 # Still potentiall racy, from python docs: |
| 88 # "On Windows...there may be no way to implement an atomic rename wh
en dst |
| 89 # names an existing file." |
| 90 os.mkdir(target_dir) |
| 91 os.rename(dest, bin_location) |
| 92 except OSError: |
| 93 write_version = False |
| 94 |
| 95 if write_version: |
| 96 with open(version_file, 'w') as f: |
| 97 f.write(version) |
| 98 |
| 99 return bin_location |
| 100 |
| 101 |
| 102 def main(mode=None): |
| 103 version = os.environ.get('NODE_VERSION', DEFAULT_VERSION) |
| 104 try: |
| 105 tmp_dir = tempfile.mkdtemp(dir=THIS_DIR) |
| 106 bin_location = install_latest_node_js(version, tmp_dir) |
| 107 finally: |
| 108 if os.path.exists(tmp_dir): |
| 109 shutil.rmtree(tmp_dir) |
| 110 |
| 111 if mode == 'npm': |
| 112 # TODO(hinoka): How about Windows...? |
| 113 bin_location = os.path.join(os.path.dirname(bin_location), 'npm') |
| 114 |
| 115 return subprocess.call([bin_location, ] + sys.argv[1:]) |
| 116 |
| 117 |
| 118 if __name__ == '__main__': |
| 119 sys.exit(main()) |
OLD | NEW |