blob: 2dad3ff41a397f43a1316fff3601d0798500a1f9 [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 2019 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Run node with the right settings."""
import os
import platform
import shutil
import sys
import libdot
# The hash of the node_modules that we maintain.
# Allow a long line for easy automated updating.
# pylint: disable=line-too-long
NODE_MODULES_HASH = '2cd2dd365999ae139b6b0fb62a5a09e2a7fb5ab1c0926cf1968a1dec9b74fea5'
# pylint: enable=line-too-long
# In sync with Chromium's DEPS file because it's easier to use something that
# already exists than maintain our own. Look for 'node_linux64' here:
# https://chromium.googlesource.com/chromium/src/+/HEAD/DEPS
NODE_VER = '12.14.1'
# Run `./node_sync_with_chromium` to update these hashes.
NODE_LINUX_HASH = '4572d3801500bcbebafadf203056d6263c840cda'
NODE_MAC_HASH = '6b9eefd982e255d639234acfa1d772c34a2529cb'
NODE_WIN_HASH = 'a25b445d6e02de0fdbf426d5cfd193702351989e'
# Bucket maintained by Chromium.
# gsutil ls gs://chromium-nodejs/
NODE_BASE_URI = 'https://storage.googleapis.com/chromium-nodejs'
# Bucket maintained by us.
NODE_MODULES_GS_FRAGMENT = 'chromeos-localmirror/secureshell/distfiles'
NODE_MODULES_GS_URI = f'gs://{NODE_MODULES_GS_FRAGMENT}'
NODE_MODULES_BASE_URI = (
f'https://storage.googleapis.com/{NODE_MODULES_GS_FRAGMENT}')
# The node_modules & node/npm paths.
NODE_MODULES_DIR = libdot.LIBAPPS_DIR / 'node_modules'
NODE_BIN_DIR = NODE_MODULES_DIR / '.bin'
NODE = NODE_BIN_DIR / 'node'
NPM = NODE_BIN_DIR / 'npm'
# Use a dotdir as npm expects to manage everything under node_modules/.
NODE_DIR = NODE_MODULES_DIR / '.node'
def run(cmd, **kwargs):
"""Run the node |cmd|.
This assumes |cmd| is a node program. Different OS's might need to invoke
node differently, so wrap the calls here.
"""
# We need the node bin dir to be at the start of the $PATH as some packages
# will try to run other npm packages directly.
extra_env = kwargs.setdefault('extra_env', {})
assert 'PATH' not in extra_env
extra_env['PATH'] = os.pathsep.join((str(NODE_BIN_DIR), os.getenv('PATH')))
return libdot.run(
cmd[1:],
cmd_prefix=[NODE, NODE_BIN_DIR / cmd[0]],
log_prefix=[cmd[0]],
**kwargs)
def update():
"""Download & update our copy of node."""
osname = platform.system()
if osname == 'Linux':
node_hash = NODE_LINUX_HASH
elif osname == 'Darwin':
node_hash = NODE_MAC_HASH
elif osname == 'Windows':
node_hash = NODE_WIN_HASH
else:
raise RuntimeError('Unknown OS %s' % (osname,))
# In case of an upgrade, nuke existing dir.
hash_file = NODE_DIR / node_hash
if not hash_file.exists():
shutil.rmtree(NODE_DIR, ignore_errors=True)
node = NODE
if osname == 'Windows':
node = node.with_suffix('.exe')
if not node.exists():
os.makedirs(NODE_BIN_DIR, exist_ok=True)
os.makedirs(NODE_DIR, exist_ok=True)
# Download & unpack the archive.
uri = '/'.join((NODE_BASE_URI, NODE_VER, node_hash))
output = NODE_DIR / node_hash
libdot.fetch(uri, output)
if osname == 'Windows':
libdot.unlink(node)
output.rename(node)
else:
libdot.unpack(output, cwd=NODE_DIR)
libdot.unlink(output)
# Create canonical symlinks for node & npm.
path = next(NODE_DIR.glob('*/bin/node'))
libdot.symlink(path, NODE)
path = next(NODE_DIR.glob('*/*/node_modules/npm/bin/npm-cli.js'))
libdot.symlink(path, NPM)
# Mark the hash of this checkout.
libdot.touch(hash_file)
def modules_update():
"""Download & update our copy of node_modules."""
hash_file = NODE_MODULES_DIR / '.hash'
old_hash = None
try:
old_hash = hash_file.read_text(encoding='utf-8').strip()
except FileNotFoundError:
pass
# In case of an upgrade, nuke existing dir.
if old_hash != NODE_MODULES_HASH:
shutil.rmtree(NODE_MODULES_DIR, ignore_errors=True)
if not hash_file.exists():
# Download & unpack the archive.
tar = f'node_modules-{NODE_MODULES_HASH}.tar.xz'
uri = '/'.join((NODE_MODULES_BASE_URI, tar))
output = libdot.LIBAPPS_DIR / tar
libdot.fetch(uri, output)
libdot.unpack(output, cwd=libdot.LIBAPPS_DIR)
libdot.unlink(output)
# Mark the hash of this checkout.
hash_file.write_text(NODE_MODULES_HASH, encoding='utf-8')
def main(argv):
"""The main func!"""
libdot.setup_logging()
libdot.node_and_npm_setup()
os.execv(NODE, ['node'] + argv)
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))