blob: 929aa7c4fdb3856a7d65da79a8be52121cb5d0e2 [file]
#!/usr/bin/env python3
# Copyright 2019 The ChromiumOS Authors
# 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 = (
"a8883766ac35c101613cfb8a3b8a4e2813e409b7faa0703b3114c90e2ba4bac1"
)
# 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.
# Run `./node_sync_with_chromium` to update these settings.
NODE_VER = "20.11.0"
NODE_LINUX_HASH = "46795170ff5df9831955f163f6966abde581c8af"
NODE_MAC_HASH = "85ccc2202fd4f1615a443248c01a866ae227ba78"
NODE_WIN_HASH = "2cb36010af52bc5e2a2d1e3675c10361c80d8f8d"
# 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(f"Unknown OS {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."""
libdot.download_tarball(
f"node_modules-{NODE_MODULES_HASH}.tar.xz",
NODE_MODULES_BASE_URI,
NODE_MODULES_DIR,
NODE_MODULES_HASH,
)
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:]))